Until LTBL2 people can play around with this.Basically it is a little deferred renderer.
At the moment it is only able to display light without ambient occlusion(easy to add through another pass for example) and specularity
Main.cpp#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Window/Event.hpp>
#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Graphics/Shader.hpp>
#include <SFML/Graphics/RenderTexture.hpp>
#include <memory>
const int width = 800;
const int height = 600;
struct Light
{
Light(sf::Vector3f col,sf::Vector3f pos,sf::Vector3f att) : color(col),
position(pos),
attenuation(att)
{
}
sf::Vector3f color;
sf::Vector3f position;
sf::Vector3f attenuation;
};
int main()
{
sf::RenderWindow window({width, height}, "Dynamic Lighting Test");
// Front and backbuffer as Pointer for std::swap
std::unique_ptr<sf::RenderTexture> front,back;
sf::RenderTexture pass_normals,pass_diffuse;
sf::Texture normal_map, diffuse_map;
front = std::unique_ptr<sf::RenderTexture>(new sf::RenderTexture());
back = std::unique_ptr<sf::RenderTexture>(new sf::RenderTexture());
front->create(width, height);
back->create(width, height);
pass_normals.create(width, height);
pass_diffuse.create(width, height);
normal_map.loadFromFile("rock_normal.png");
diffuse_map.loadFromFile("rock_diffuse.png");
sf::Sprite sprite(diffuse_map);
sf::Shader lights_shader;
sf::Shader normals_shader;
// Add a "warm" light, color needs to be in 0 - 1 range
Light light(sf::Vector3f(255/255.0,214/255.0,170/255.0),
sf::Vector3f(0,0,0.02),
sf::Vector3f(0.5,0.5,0.5));
lights_shader.loadFromFile("light.frag", sf::Shader::Fragment);
normals_shader.loadFromFile("normals.frag",sf::Shader::Fragment);
// Center Sprite
sprite.setOrigin(150.0f,112.5f);
sprite.setPosition(400,300);
// Environmental variables
float ambient_intensity = 0.7;
sf::Vector3f falloff(0.5,0.5,0.5);
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed) window.close();
// controls for environment
// mousewheel to control height of the controlled light
// + and - to control ambient light intensity
// x and y to control controlled light falloff
if (event.type == sf::Event::MouseWheelMoved)
{
light.position.z += event.mouseWheel.delta * 0.01;
}
if(event.type == sf::Event::KeyPressed)
{
if(event.key.code == sf::Keyboard::Add)
{
ambient_intensity += 0.05f;
}
if(event.key.code == sf::Keyboard::Subtract)
{
ambient_intensity -= 0.05f;
}
if(event.key.code == sf::Keyboard::Y)
{
falloff /= 0.5f;
}
if(event.key.code == sf::Keyboard::X)
{
falloff *= 0.5f;
}
}
}
// Clear renderbuffers
back->clear();
front->clear();
pass_diffuse.clear();
// Set normals buffer to neutral color
pass_normals.clear(sf::Color(128,128,255));
window.clear();
// set light position, and adjust for different coordinate systems
light.position.x = sf::Mouse::getPosition(window).x;
light.position.y = 600 - sf::Mouse::getPosition(window).y;
// Diffuse Pass, feed every sprite to draw here before display
pass_diffuse.draw(sprite);
pass_diffuse.display();
// Normals Pass, feed every normal map which should be rendered here
// For more then one repeat the next 2 steps before displaying
normals_shader.setParameter("sampler_normal",normal_map);
pass_normals.draw(sprite,&normals_shader);
pass_normals.display();
// Light Pass, renders every light into a rendertexture
lights_shader.setParameter("resolution",sf::Vector2f(width, height));
lights_shader.setParameter("sampler_normal",pass_normals.getTexture());
lights_shader.setParameter("ambient_intensity",ambient_intensity);
lights_shader.setParameter("falloff",falloff);
// For more lights put the next 6 lines into a loop
lights_shader.setParameter("sampler_light",front->getTexture());
lights_shader.setParameter("light_pos",light.position);
lights_shader.setParameter("light_color",light.color);
back->draw(sf::Sprite(pass_diffuse.getTexture()),&lights_shader);
back->display();
std::swap(back,front);
// Draw diffuse color
window.draw(sf::Sprite(pass_diffuse.getTexture()));
// Blend lighting over
window.draw(sf::Sprite(front->getTexture()),sf::BlendMultiply);
// Finally display it
window.display();
}
return 0;
}
light.frag#version 120
uniform vec2 resolution;
uniform sampler2D sampler_normal;
uniform sampler2D sampler_light;
uniform vec3 light_pos;
uniform vec3 light_color;
uniform vec3 ambient_color = vec3(0.5,0.5,0.5);
uniform float ambient_intensity = 0.5;
uniform vec3 falloff;
void main()
{
vec2 coord = gl_TexCoord[0].xy;
vec3 normal_map = texture2D(sampler_normal, coord).rgb;
vec3 light_map = texture2D(sampler_light,coord).rgb;
vec3 lightDir = vec3((light_pos.xy - gl_FragCoord.xy) / resolution.xy, light_pos.z);
lightDir.x *= resolution.x / resolution.y;
float D = length(lightDir);
vec3 N = normalize(normal_map * 2.0 - 1.0);
vec3 L = normalize(lightDir.xyz);
vec3 diffuse = light_color.rgb * max(dot(N,L),0.0);
vec3 ambient = ambient_color * ambient_intensity;
float attenuation = 1.0 / (falloff.x + (falloff.y * D) + falloff.z*D*D);
vec3 intensity = ambient + diffuse * attenuation;
gl_FragColor = vec4(light_map + intensity, 1.0 );
}
normals.fraguniform sampler2D sampler_normal;
void main(void)
{
vec3 normal_map = texture2D(sampler_normal, gl_TexCoord[0].xy).rgb;
gl_FragColor = vec4(normal_map,1.0);
}
This is what it looks like:
Depending on how the normals are generated you have to change:
vec3 N = normalize(normal_map * 2.0 - 1.0);
to:
vec3 N = -1.0 * normalize(normal_map * 2.0 - 1.0);
in the lights.frag
Ambient intensity can be controlled with + and -
Height of the controlled light with your mousewheel
The falloff of the light with x(higher) and y(lower)
Sorry for the long post and the large gif.
Edit: Link to gif hopefully fixed.