Hello guys,
I wanted to start making some light effects with a fragment shader, but I get some strange behaviour.
Thats the issue:
(http://puu.sh/3MmxG.jpg)
I made a simple class which draws the light on a renderTexture to have multiple lights, however the mixing seems not like it should be for me and I get the strange flickering. I have a vector of the lights and if I make green after blue and red, the flickering is gone, which is quite strange to me.
I can't find the mistake though.
Thats how I draw the lights on the texture:
game.cpp:
void Game::render()
{
// Clears the texture
texture.clear();
// Draws all lights on the texture
for(auto&& light : lightList)
light.render(texture);
// Draws the texture
window.draw(sf::Sprite(texture.getTexture()));
}
And pointLight.cpp:
void PointLight::render(sf::RenderTexture& texture)
{
// Sets the parameters
shader.setParameter("radius", light.getRadius());
shader.setParameter("color", light.getFillColor());
shader.setParameter("texture", sf::Shader::CurrentTexture);
shader.setParameter("center", sf::Vector2f(light.getPosition().x + light.getRadius(), light.getPosition().y + light.getRadius()));
// Draws the light on the texture
texture.draw(sf::Sprite(texture.getTexture()), &shader);
}
And of course the shader:
uniform vec4 color;
uniform vec2 center;
uniform float radius;
uniform sampler2D texture;
void main()
{
// Takes the pixel and calculates the difference to the center of the light
vec4 pixel = texture2D(texture, gl_TexCoord[0].xy);
float difference = distance(vec2(gl_FragCoord), center) / radius;
// Calculates the alpha value and sets the light color
float alpha = 1.0 - difference;
vec4 light = vec4(color.x, color.y, color.z, alpha);
// The actual color is the addition of the pixel color and the light color
vec4 col = pixel + light;
gl_FragColor = vec4(col.xyz, alpha);
}
Hopefully somebody knows what I am making wrong.
Thx and Greetings!
Geheim
texture.draw(sf::Sprite(texture.getTexture()), &shader);
You can't draw to a texture using itself and that's not how light should be implemented, lights get drawn to black buffer using additive blending and then they get drawn onto scene that looks fully lit using multiplicative blending. You should probably also not use gl_FragCoord because gl_FragCoord assumes a lower-left origin for window coordinates
http://www.opengl.org/sdk/docs/manglsl/xhtml/gl_FragCoord.xml and that is a bit bothersome. So your shader and code should be:
#include <SFML/Graphics.hpp>
#include <vector>
int main()
{
sf::RenderWindow window(sf::VideoMode(800, 600, 32), "Bug_Example", sf::Style::Close);
window.setFramerateLimit(60);
sf::Event windowEvent;
sf::Shader shader;
if (!shader.loadFromFile("shader.frag", sf::Shader::Fragment))
window.close();
sf::RenderTexture texture;
if (!texture.create(800, 600))
window.close();
std::vector<sf::CircleShape> lightList;
for (int i = 0; i != 3; ++i)
lightList.push_back(sf::CircleShape(400));
lightList[0].setFillColor(sf::Color::Red);
lightList[0].setPosition(200.0f, 200.0f);
lightList[1].setFillColor(sf::Color::Green);
lightList[1].setPosition(500.0f, 200.0f);
lightList[2].setFillColor(sf::Color::Blue);
lightList[2].setPosition(350.0f, 400.0f);
while (window.isOpen())
{
while (window.pollEvent(windowEvent))
{
if (windowEvent.type == sf::Event::Closed)
window.close();
}
texture.clear();
sf::RenderStates states;
states.blendMode = sf::BlendAdd;
states.shader = &shader;
for (auto&& light : lightList)
{
shader.setParameter("radius", light.getRadius());
shader.setParameter("center", sf::Vector2f(light.getPosition().x, light.getPosition().y));
sf::VertexArray q(sf::Quads, 4);
q[0].color = q[1].color = q[2].color = q[3].color = light.getFillColor();
q[1].texCoords = q[1].position = sf::Vector2f(0.f, 600.f);
q[2].texCoords = q[2].position = sf::Vector2f(800.f, 600.f);
q[3].texCoords = q[3].position = sf::Vector2f(800.f, 0.f);
texture.draw(q, states);
}
texture.display();
window.clear(sf::Color::White);
window.draw(sf::Sprite(texture.getTexture()), sf::BlendMultiply);
window.display();
}
return EXIT_SUCCESS;
}
uniform vec2 center;
uniform float radius;
void main()
{
float difference = distance(vec2(gl_TexCoord[0].xy), center) / radius;
difference=max(0.0,difference);
gl_FragColor = (1.0-difference) * gl_Color;
}
Of course this is not perfect(passing color through gl_Color and using one big quad) but it's just the general idea.