SFML community forums

Help => Graphics => Topic started by: Geheim on July 26, 2013, 09:13:49 pm

Title: Shader flickering
Post by: Geheim on July 26, 2013, 09:13:49 pm
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
Title: Re: Shader flickering
Post by: eXpl0it3r on July 26, 2013, 09:32:43 pm
Please create a minimal and complete example, so we can directly test it and don't have to write our own framework. ;)

Also I see some kind of structure shining through the blue there. Is that the issue you're talking about, or is that a texture you're using. If it's a texture, could you provide that as well?
Title: Re: Shader flickering
Post by: Geheim on July 26, 2013, 09:57:31 pm
Alright I squeezed the minimal code ugly into the main.cpp ;)
(Put the shader.frag where the main is)

Pastebin:
main.cpp --> http://pastebin.com/uEADum75 (http://pastebin.com/uEADum75)
shader.frag --> http://pastebin.com/ZY2Vr9qF (http://pastebin.com/ZY2Vr9qF)

Zip-file:
main.cpp + shader.frag + exe --> https://dl.dropboxusercontent.com/u/8088302/Bug_Example.zip (https://dl.dropboxusercontent.com/u/8088302/Bug_Example.zip)
Title: Re: Shader flickering
Post by: eXpl0it3r on July 27, 2013, 01:03:41 am
Okay, I can reproduce the problem on my AMD card, but since I've no idea about shaders I can't help you further...
Title: Re: Shader flickering
Post by: FRex on July 27, 2013, 02:09:38 am
 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
Quote
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.
Title: Re: Shader flickering
Post by: Geheim on July 27, 2013, 01:18:45 pm
Alright thank you very much FRex, everything works fine now and I hope that I understand everything you said:

My black buffer is the renderTexture, I should use TexCoord instead of FragCoord, because of the lower-left origin coordinates and I should not draw on the texture using itself!

The blending makes sence too now, but I have one question though:
You said, that it's not perfect using the vertexArray and gl_Color, what would be better then?

I thought about it and came to this two ideas: To pass the color and to use a second renderTexture OR to draw the light directly on the black buffer. Would this be "possible"/better than the vertexArray?
Title: Re: Shader flickering
Post by: FRex on July 27, 2013, 01:51:51 pm
I don't know what is perfect but this is too simple to be perfect. ;D It probably depends on what you need.
Quote
To pass the color and to use a second renderTexture
I don't know what you mean by that, pass the color where and why would you need a second buffer? Light don't care about each other and don't use the buffer texture for anything. You can draw everything to one buffer using additive blending and then multiply it with the scene. You can draw cricle shapes, vertex arrays, whatever geometry you want, you can use shaders or not, use textures(if you want textured light for something). I used shader here because I guessed you wanted a fading with distance effect, you could also do that effect by using a triangle fan and texturing outer vertices to black and inner to your light's color.
http://fd.fabiensanglard.net/doom3/additive_blending/allLights.jpg
This is doom 3 screen from fabien sanglard's website, you can see how three lights of basic colors mix like in the famous RGB venn's graph(three circles that overlap in the middle to make white), it all was rendered to one black buffer and then multiplicated with the scene textures(diffuse maps), there's also specular and bump mapping involved with light but you're not doing either. You should of course take this with a grain of salt, I might be wrong in many aspects. ;)
Title: Re: Shader flickering
Post by: Geheim on July 27, 2013, 02:49:50 pm
Alright I see, I have read/experimented too less with shaders and your solution works fine, so I will take it for now. Thanks for your help. Simple does not mean that it is bad ;)