#include <iostream>
#include <vector>
#include <SFML/Graphics.hpp>
std::string const my_shader = \
"uniform vec2 center;" \
"uniform float radius;" \
"uniform float intensity;" \
"void main() {" \
" float dist = distance(gl_TexCoord[0].xy, center);" \
" float color = exp(-1.5*dist/(radius/3.f));" \
" gl_FragColor = vec4(gl_Color.xyz * color, intensity / 255.0);" \
"}";
float const MAX_RADIUS = 500.f;
bool generate_lightmap(sf::Texture& target) {
float size = sf::Texture::getMaximumSize();
// used to draw enlightened area
sf::VertexArray array{sf::Quads, 4u};
array[0].position = {0.f, 0.f};
array[1].position = {size, 0.f};
array[2].position = {size, size};
array[3].position = {0.f, size};
for (std::size_t i = 0u; i < 4u; ++i) {
array[i].texCoords = array[i].position;
array[i].color = sf::Color::White;
}
// shader used for lighting
if (!sf::Shader::isAvailable()) {
return false;
}
sf::Shader shader;
shader.loadFromMemory(my_shader, sf::Shader::Fragment);
// prerender lightmap
sf::RenderTexture buffer;
if (!buffer.create(size, size)) {
return false;
}
buffer.clear(sf::Color::Black);
shader.setParameter("intensity", 255u);
shader.setParameter("radius", MAX_RADIUS);
shader.setParameter("center", {size / 2.f, size / 2.f});
buffer.draw(array, &shader);
buffer.display();
target = buffer.getTexture();
return true;
}
int main() {
sf::RenderWindow window{{640, 480}, "Test"};
window.setVerticalSyncEnabled(true);
auto size = window.getSize();
sf::RenderTexture buffer;
buffer.create(size.x, size.y);
sf::CircleShape shape{5};
shape.setOrigin({5.f, 5.f});
shape.setFillColor(sf::Color::Green);
// create general lightmap
sf::Texture lightmap;
std::cout << "Creating lightmap ..." << std::flush;
if (!generate_lightmap(lightmap)) {
std::cout << "Failed" << std::endl;
return 1;
}
std::cout << " Done" << std::endl;
sf::Sprite lighting{lightmap};
lighting.setOrigin(sf::Vector2f{lightmap.getSize()} / 2.f);
// create some light sources
std::vector<sf::Vector2f> sources;
for (auto y = 0u; y < 4u; ++y) {
for (auto x = 0u; x < 6u; ++x) {
sources.emplace_back(75.f + x * 100.f, 75.f + y * 100.f);
}
}
sf::Clock clock;
float radius{150.f};
bool up{false};
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
}
if (up) {
radius += 2.5f;
if (radius > 150.f) {
up = false;
}
} else {
radius -= 2.5f;
if (radius < 50.f) {
up = true;
}
}
clock.restart();
// create lightmap
buffer.clear(sf::Color::Black);
for (auto const & pos: sources) {
lighting.setPosition(pos);
lighting.setScale({radius / MAX_RADIUS, radius / MAX_RADIUS});
buffer.draw(lighting, sf::BlendAdd);
}
buffer.display();
window.clear(sf::Color::White);
// draw scene
for (auto const & pos: sources) {
shape.setPosition(pos);
window.draw(shape);
}
// apply lightmap
sf::Sprite tmp{buffer.getTexture()};
window.draw(tmp, sf::BlendMultiply);
window.display();
std::cout << clock.restart().asMilliseconds() << "ms" << std::endl;
}
}