SFML community forums

Help => Graphics => Topic started by: Foaly on September 13, 2012, 06:29:31 pm

Title: Fading without artifacts
Post by: Foaly on September 13, 2012, 06:29:31 pm
Hello there,
for a little program I'm making, I am drawing a couple drawable objects on top of a Rendertexture, without clearing it. Now I want that the stuff that is already on the texture to slowly fade. To achieve this I made a RectangleShape as big as the Texture and has a semi-transparent black color, which is drawn on top of the Texture at the beginning of each render cycle.
The problem is that it only works partly. The texture fades a little bit, but after some time it stops and the old objects stay as dark artifacts.
Does anybody know whats wrong or another way how I could fade the texture?
Thanks in advance,
Foaly
Title: Re: Fading without artifacts
Post by: eXpl0it3r on September 13, 2012, 07:05:13 pm
Well you should provide a minimal example, it's hard to really follow, what the end effect will look like or what you want to achieve...
Title: Re: Fading without artifacts
Post by: Foaly on September 13, 2012, 07:30:04 pm
Yes sure. Here is a minimal code example:
#include <SFML/Graphics.hpp>
#include <iostream>


int main()
{
    sf::RenderWindow window(sf::VideoMode(1024, 768), "SFML");

    sf::RenderTexture m_RenderTexture;
    m_RenderTexture.create(window.getSize().x, window.getSize().y);
    sf::Sprite m_RenderSprite;

    sf::RectangleShape FadeRect(sf::Vector2f(window.getSize()));
    FadeRect.setFillColor(sf::Color(0, 0, 0, 1));

    sf::CircleShape circle;
    circle.setRadius(10);
    circle.setFillColor(sf::Color::Red);


    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            // Close Event
            if (event.type == sf::Event::Closed)
                window.close();

            // Escape key pressed
            if ((event.type == sf::Event::KeyPressed) && (event.key.code == sf::Keyboard::Escape))
                window.close();

        }

        circle.setPosition(sf::Vector2f(sf::Mouse::getPosition(window)));



        // Draw everything
        m_RenderTexture.draw(FadeRect);
        m_RenderTexture.draw(circle);
        m_RenderTexture.display();

        window.clear();
        m_RenderSprite.setTexture(m_RenderTexture.getTexture());
        window.draw(m_RenderSprite);
        window.display();
    }

    return 0;
}
 
The red circle is following the mouse. It's drawn on the texture. The fade rectangle is also drawn on the texture. So the red circle should fade out. It fades a little bit, but then stops. I'm sure the example will show what I mean.
Title: Re: Fading without artifacts
Post by: masskiller on September 14, 2012, 01:06:16 am
I don't know well if what I will say applies since I am new to SFML myself and I don't know if you want to do more than just making it fade, but if you want the circle to fade wouldn't decreasing the circle's alpha in the loop be better? You would have no need for a rectangle shape texture the size of the window. You would just need to use a speed for the fading and the loop handles the rest.
Title: Re: Fading without artifacts
Post by: eXpl0it3r on September 14, 2012, 02:22:53 am
I guess that's the expected behavior, the color adding with sf::Color(0,0,0,1) will always have to let shine through something and at a certain point it just sticks to it.

You can demonstrate this also with the following code:
#include <SFML/Graphics.hpp>

int main()
{
    sf::RenderWindow window(sf::VideoMode(1024, 768), "SFML");
    window.setFramerateLimit(60);

    sf::RenderTexture m_RenderTexture;
    m_RenderTexture.create(window.getSize().x, window.getSize().y);
    sf::Sprite m_RenderSprite;

    sf::RectangleShape FadeRect(sf::Vector2f(window.getSize()));
    FadeRect.setFillColor(sf::Color(0, 0, 0, 1));

    sf::CircleShape circle;
    circle.setRadius(10);
    circle.setFillColor(sf::Color::Red);

    m_RenderSprite.setTexture(m_RenderTexture.getTexture());

    m_RenderTexture.draw(circle);
    m_RenderTexture.display();

    for(unsigned int i = 0; i < 1000; ++i)
    {
        m_RenderTexture.draw(FadeRect);
        m_RenderTexture.display();
    }

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
        }

        circle.setPosition(sf::Vector2f(sf::Mouse::getPosition(window)));

        window.clear();
        window.draw(m_RenderSprite);
        window.display();
    }
}
 
You can now set the number in the for-loop to any value you want, but the circle will never disappear.

I'm not that good at color modification stuff, so it maybe possible with the way you were doing it, but I don't know and I'd try to find another method.
Title: Re: Fading without artifacts
Post by: Laurent on September 14, 2012, 08:21:33 am
Using the previous contents of a render-texture without clearing it is not clean.

You should rather use a variant of your solution: increase the alpha of the FadeRect until it is totally opaque, instead of accumulating the results of previous frames.
Title: Re: Fading without artifacts
Post by: Foaly on September 14, 2012, 12:05:48 pm
Hmm well that's the problem... My program needs to draw everything to a render-texture, without clearing it. I can't show you right now, because I'm not on my computer. But I'll post the program tomorrow, so you can see what I mean.
Title: Re: Fading without artifacts
Post by: Laurent on September 14, 2012, 12:20:39 pm
I'm not sure to understand what you mean, but instead of fading the render-texture, you could fade the sprite that draws the contents of the render-texture. This way you leave the render-texture untouched.
Title: Re: Fading without artifacts
Post by: Foaly on September 14, 2012, 12:30:21 pm
Yeah I tried that, but it doesn't work. The effect I want to achieve is that the longer an objects is on the render-texture the more it fades until it disappears. Fading the sprite means that everything is faded.
Title: Re: Fading without artifacts
Post by: Laurent on September 14, 2012, 01:03:52 pm
Then I don't understand what you want to achieve. I thought you wanted to fade the render-texture.

You should explain your goal more clearly.
Title: Re: Fading without artifacts
Post by: Foaly on September 22, 2012, 12:10:49 pm
I need the fade for this program: http://en.sfml-dev.org/forums/index.php?topic=9222.0
Open it and try the fade mode, by pressing "f" it will slowly fade, but there are always weird artifacts in the background.
Title: Re: Fading without artifacts
Post by: Foaly on October 01, 2012, 08:41:25 am
I don't know if you gave the program a try, but if you did, you see the problem I'm having again. This issue is starting to really get to me. I tryed a lot of things but nothing seems to work. The only idea I could come up with that might have solved the issue was to draw a rectangle with a subtractive blend mode over the entire scene, but the request for a subtractive blend mode got rejected. Can anybody help me and point me in the right direction of how to fade an entire render texture?
Thanks in advance, foaly
Title: Re: Fading without artifacts
Post by: Laurent on October 01, 2012, 08:46:16 am
Can you explain again what you want to do? Sometimes you talk about a rectangle that covers the whole window, sometimes you talk about fading individual entities. This is not clear. You should also tell us what kind of fading you want to achieve (fade to black? fade to transparent? ...).
Title: Re: Fading without artifacts
Post by: Foaly on October 01, 2012, 10:48:09 am
Ok I'm sorry I have to admit that my explanation got pretty confusing. To not bother you with the details of my program I thought of a simple example. Imagine that you want to visualize the trail of a mouse. So you create a circle and set its position to the mouse coordinates. That is updated every frame. Then you create a render texture the circle is drawn to the rendertexture every frame. The rendertexture is than drawn to the window. The rendertexture is never cleared so that the trail of the mouse stays. Here comes my problem: I want to fade the rendertexture, so that the old circles slowly fade out. (I want to fade to black for now, although fade to transparent is actually a very interesting idea, I might come back to that later) my first idea was to simply draw a semi opaque rectangle on the rendertexture at the beginning of each frame, but that leads to weird artifacts I discribed above. I experimented with the blend modes, but none of them seems to work. I thought a subtractive blend mode might help, so I tryed implementing it, but I wasn't able to. Then I tryed to use a shader to achieve the effect. But that didn't work either so I'm stuck now. How can I fade the render texture?!?
Title: Re: Fading without artifacts
Post by: eXpl0it3r on October 01, 2012, 11:38:48 am
IIRC it's not very good to not clear the RenderTexture every frame iteration, so you might want to keep track of all the objects you have and fade one after the other slowly.

For example like this:
#include <SFML/Graphics.hpp>
#include <vector>
#include <iostream>

int main()
{
    sf::RenderWindow window(sf::VideoMode(300, 300), "Test");
    window.setFramerateLimit(60);

    sf::Vector2f old_pos(0, 0);
    static const int STEP = 5;

    std::vector<sf::CircleShape> points;

    while(window.isOpen())
    {
        sf::Event event;
        while(window.pollEvent(event))
        {
            if(event.type == sf::Event::Closed)
                window.close();
        }

        sf::Vector2f pos = static_cast<sf::Vector2f>(sf::Mouse::getPosition(window));
        if(pos != old_pos)
        {
            sf::CircleShape cs;
            cs.setRadius(10);
            cs.setPosition(pos);
            cs.setFillColor(sf::Color::Red);
            points.push_back(cs);

            old_pos = pos;
        }

        window.clear();

        for(auto it = points.begin(); it != points.end(); ++it)
        {
            window.draw(*it);
            if(it->getFillColor().a-STEP < 0)
                it = points.erase(it);
            else
                it->setFillColor(sf::Color(255, 0, 0, it->getFillColor().a-STEP));
        }

        window.display();
    }
}
 

Keep in mind that calling erase on a std::vector isn't very performant and could be avoided with a better structure. With another structure you could also make use of that the fading process is linear and thus the oldest element can get removed first. Also to minimize the memory usage/object creation you could also just store the positions and draw a single shape/sprite/etc. while changing it's position and it's transparency.

I guess there could be other ways but that was just the most straight forward one that popped into my head.
Title: Re: Fading without artifacts
Post by: Foaly on October 01, 2012, 12:59:53 pm
Can you explain more clearly why not clearing a rendertexture is bad? To my understanding clear() only paints the whole render target in a single color.
I thought about storing all the element individually, but is not an option for my program, because it's only a small program, but it has quiet a lot of elements, so it would use unreasonably huge amounts of memory. And that's not very efficient.
Title: Re: Fading without artifacts
Post by: masskiller on October 01, 2012, 05:08:14 pm
I might be wrong, but the RenderTexture, like the window needs to be cleared so that nothing unexpected appears in it. Display function displays anything that has been drawn beforehand, but doesn't clear it from previous pixels. You need to clear so that the window/rendertexture draws from scratch and it helps avoid unexpected behaviour.

For example try a simple code of moving a sprite without clearing the window, instead of the clean sprite moving what you will have is the sprite image displaying itself as it moves, so it doesn't show movement, rather it displays the sprite at many different positions that it has taken overtime.
Title: Re: Fading without artifacts
Post by: eXpl0it3r on October 01, 2012, 05:37:21 pm
For example try a simple code of moving a sprite without clearing the window, instead of the clean sprite moving what you will have is the sprite image displaying itself as it moves, so it doesn't show movement, rather it displays the sprite at many different positions that it has taken overtime.
That's the effect he's trying to get. ;)

Can you explain more clearly why not clearing a rendertexture is bad? To my understanding clear() only paints the whole render target in a single color.
I'm not sure about this. It could very well be that it's okay for render texture but not for the window...

I thought about storing all the element individually, but is not an option for my program, because it's only a small program, but it has quiet a lot of elements, so it would use unreasonably huge amounts of memory. And that's not very efficient.
What numbers are we talking about here?
I'd say you would need to care for the memory usage it's not as much as a problem as the storing/erasing/drawing itself.
I've seen your application but I don't know how you did it, so I can't help you further on how to optimize.

I'm not sure how SFML handles the blending but from my understanding this shouldn't be the expected behavior. It should keep going dimmer and dimmer until the decimal points get rounded to 0.
But instead the darkness depends on the alpha channel you're using.
For instance, if you set the color to (0, 0, 0, 1) the result will look like:
(http://i.imgur.com/NgFHI.png)

But if you use (0, 0, 0, 10) you'll get:
(http://i.imgur.com/LCNSq.png)

Testing code:
#include <SFML/Graphics.hpp>

int main()
{
    sf::RenderWindow window(sf::VideoMode(1024, 768), "SFML");
    window.setFramerateLimit(60);

    sf::RenderTexture m_RenderTexture;
    m_RenderTexture.create(window.getSize().x, window.getSize().y);
    sf::Sprite m_RenderSprite;

    sf::RectangleShape FadeRect(sf::Vector2f(window.getSize()));
    FadeRect.setFillColor(sf::Color(0, 0, 0, 1));

    sf::CircleShape circle;
    circle.setRadius(10);
    circle.setFillColor(sf::Color::Red);

    m_RenderSprite.setTexture(m_RenderTexture.getTexture());

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed)
                window.close();
        }

        circle.setPosition(sf::Vector2f(sf::Mouse::getPosition(window)));
        m_RenderTexture.draw(circle);
        m_RenderTexture.draw(FadeRect);
        m_RenderTexture.display();

        window.clear();
        window.draw(m_RenderSprite);
        window.display();
    }
}
Title: Re: Fading without artifacts
Post by: masskiller on October 01, 2012, 05:52:43 pm
Quote
That's the effect he's trying to get. ;)

Sorry then. It's been long since I read the topic and as I am in my University I didn't even try to make a sample code since I would have to set a project just to do it. My bad.
Title: Re: Fading without artifacts
Post by: eXpl0it3r on October 01, 2012, 05:59:33 pm
So after further testing this really seems like a bug, probably some float vs integer stuff.

If you use a black rect with alpha = 1 the final color of the originally red circle will be (128, 0, 0) thus exactly half of the original color. If you now set the original color to (128, 0, 0) and the fading alpha channel to 1, the color doesn't even change, it just stays (128, 0, 0).

Then again if you use alpha = 128 the resulting color will be 100% black, but alpha = 127 will get you a (1, 0, 0) color.

Like I already said what should actually happen is that the color should get dimmer and dimmer until it gets rounded completely to black and not some more or less random red-ish. ;)
Title: Re: Fading without artifacts
Post by: Laurent on October 01, 2012, 06:09:45 pm
Quote
probably some float vs integer stuff
Most likely.
Title: Re: Fading without artifacts
Post by: eXpl0it3r on October 01, 2012, 07:50:07 pm
Quote
probably some float vs integer stuff
Most likely.
And that now means what exactly? ???
Will you investigate it/fix it? ???
Title: Re: Fading without artifacts
Post by: Laurent on October 01, 2012, 08:59:05 pm
If it's not a bug, then there's nothing to investigate and fix. If it's the expected behaviour according to what happens on the graphics card, then you should rather find another technique.
Title: Re: Fading without artifacts
Post by: eXpl0it3r on October 01, 2012, 09:25:57 pm
If it's not a bug, then there's nothing to investigate and fix. If it's the expected behaviour according to what happens on the graphics card, then you should rather find another technique.
Sorry I'm still confused... Why are you using if-sentences? :-\

For me it seems like a bug and not the expected behavior, but I didn't create SFML and I don't have any knowledge on OpenGL, so I'm asking you: Is it a bug or is it the expected behavior?

From a logical point of view it's obvious that at some point it should turn completely black and not get stuck a certain point. One can also try this with an image editor, if you add more and more semi transparent objects on top of each other the color will at some point get solid.
Title: Re: Fading without artifacts
Post by: Foaly on October 01, 2012, 09:36:10 pm
Yeah I feel like it's not the expected behaviour. I think it might be a float / integer conversion too. Maybe it's because the sf::Color components are defined as Uint8. Maybe using floats will solve the issue.

Part of my original question was how to achieve the desired effect with another technique. Would it be possible to maybe use a shader?
Title: Re: Fading without artifacts
Post by: Laurent on October 01, 2012, 11:08:37 pm
Quote
Sorry I'm still confused... Why are you using if-sentences?
I can't be 100% sure, I'm not inside the graphics card to see what happens there ;)

Quote
I'm asking you: Is it a bug or is it the expected behavior?
I don't think it's a bug, but it's not what one would expect either. However, if you think about the operations involved (working with 8-bit color components), it seems to be "normal".

Quote
Maybe using floats will solve the issue.
Using floats everywhere would be very complicated (using them in sf::Color wouldn't be enough).

Quote
Part of my original question was how to achieve the desired effect with another technique. Would it be possible to maybe use a shader?
Storing all the points and decrementing their alpha slowly is the best solution. How many points do you want to draw? If they fade out, you should never hav a huge list of points to draw.
Title: Re: Fading without artifacts
Post by: FRex on October 01, 2012, 11:16:53 pm
Quote
I can't be 100% sure, I'm not inside the graphics card to see what happens there ;)
No?  :(

Quote
I don't think it's a bug, but it's not what one would expect either. However, if you think about the operations involved (working with 8-bit color components), it seems to be "normal".
Isn't something unexpected or unintended happening the definition of a bug?  ;)
Going by that logic, doing something really stupid that crashes the program or introduces ub is not a bug because 'if you think about the operations involved (reinterpret casts, deleting stack variables, overflowing the buffers ect.), it seems to be "normal" ' ;)

Quote
Storing all the points and decrementing their alpha slowly is the best solution. How many points do you want to draw? If they fade out, you should never hav a huge list of points to draw.
Chances are they all fade out at same rate so some fifo list would speed it up, deque maybe.

btw: Does the above code snippet work without adding clear() call after creation? On my laptop rendertexture starts with complete garbage, last program's rendertexture contents ect. if I don't do call clear.
But if I do it's alright.(yes, laptop with intel's grapics ::)).
Title: Re: Fading without artifacts
Post by: eXpl0it3r on October 01, 2012, 11:25:24 pm
Isn't something unexpected or unintended happening the definition of a bug?  ;)
Going by that logic, doing something really stupid that crashes the program or introduces ub is not a bug because 'if you think about the operations involved (reinterpret casts, deleting stack variables, overflowing the buffers ect.), it seems to be "normal" ' ;)
It doesn't crash and doesn't create any problems, it just doesn't fully do what one would dream of. Also it doesn't really seem to be related to SFML itself.
To some extend it makes sense in a way that if the layer is < 128 there'll always be some bits getting shown and with >= 128 everything gets wiped out. Now you could try to get things to fade slowly be attaching the drawing of the fader to a sf::Clock and time it differently than just drawing it every frame.

Chances are they all fade out at same rate so some fifo list would speed it up, deque maybe.
Something like that, only problem is you'll have to be able to iterate over it. ;)

btw: Does the above code snippet work without adding clear() call after creation? On my laptop rendertexture starts with complete garbage, last program's rendertexture contents ect. if I don't do call clear.
But if I do it's alright.(yes, laptop with intel's grapics ::)).
Well some compilers/computers/whatever clear such buffers and then it seems to work, but that's not something you can rely on. I simply forgot to add it, although I initially had it in mind. ;)
Title: Re: Fading without artifacts
Post by: Laurent on October 01, 2012, 11:46:50 pm
Quote
Isn't something unexpected or unintended happening the definition of a bug?
There's a big difference. A bug is a programming mistake that makes the program not behave as specified. This is clearly not the case, (it seems that) the graphics card behaves exactly as it is supposed to, it's just not very intuitive for the end user in this specific context.
Title: Re: Fading without artifacts
Post by: Foaly on October 03, 2012, 01:39:53 am
So you're saying that is not a bug? Does that mean I shouldn't expect this issue to be resolved? I would really like see this beeing improved. I think I would be more intuitive and more like user would expect it to behave. Or could the subtractive blend mode be added as an alternative and kind of a workaround for problems like this?

Keeping count of all my objects is not an option, because it would be way to much memory usage for something that can be solved with the way more efficient rendertexture. So would another idea work: I simply draw a transparent rectangle (sf::blendmode::none) over the whole scene on the rendertexture and apply a shader to it. The shader does a texture lookup from the rendertexture. Then you take that color and subtract some of the alpha. Would that work? Or is there another way to apply a global shader to a rendertexture?
Title: Re: Fading without artifacts
Post by: Laurent on October 03, 2012, 07:34:32 am
Quote
Would that work?
I'm sorry, I don't have the time to investigate and find a good solution to your problem, so you'd better try your ideas directly and see if they work :-\
Title: Re: Fading without artifacts
Post by: Foaly on October 03, 2012, 01:07:41 pm
Well it was more a general question to everybody. I not an expert in GLSL so I was wondering if someone could tell me if my idea is even possible, before I waist my time with trying something that won't work. Main question is how can I pass the Rendertexture to the shader. (so that I can retrieve the color using texture2D())
Or is there another way to apply a global shader to a rendertexture?

What about the other questions in my previous post?
Title: Re: Fading without artifacts
Post by: Laurent on October 03, 2012, 01:32:21 pm
Quote
Main question is how can I pass the Rendertexture to the shader
sf::Shader has a setParameter(name, sf::Texture) function.

Quote
Or is there another way to apply a global shader to a rendertexture?
Other than what?
Title: Re: Fading without artifacts
Post by: Foaly on October 03, 2012, 03:39:19 pm
Other than what?
Other than drawing a rectangle over the texture after everything else is drawn with blendmode set to None and apply a shader to the rectangle. The rendertextures texture is passed to the shader so you can modify it there.

I get a error saying "parameter "texture" not found in shader" when I call shader.setParameter("texture", rendertexture.getTexture()); although I declared uniform sampler2D texture; in the shader.
Title: Re: Fading without artifacts
Post by: Laurent on October 03, 2012, 03:52:09 pm
Quote
Other than drawing a rectangle over the texture after everything else is drawn with blendmode set to None and apply a shader to the rectangle. The rendertextures texture is passed to the shader so you can modify it there.
So you want to draw the render-texture on the render-texture? It doesn't work, you can't have the render-texture as both the source and the destination.

Quote
I get a error saying "parameter "texture" not found in shader" when I call shader.setParameter("texture", rendertexture.getTexture()); although I declared uniform sampler2D texture; in the shader.
The GLSL compiler optimizes out unused variables (unused = not part of the code that produces the final pixel), so make sure that you use it in your shader.
Title: Re: Fading without artifacts
Post by: Foaly on October 03, 2012, 04:13:17 pm
Ah thanks for the explanation. I was wondering what the problem was.

So if I can't use a render-texture as the source and the destination, is there any other way to apply a "global" shader to a render-texture?
Title: Re: Fading without artifacts
Post by: Laurent on October 03, 2012, 04:26:20 pm
If you want to draw a texture on itself, you have to use two render-textures: one is the source, one is the destination, and you swap them every frame so that the previous destination becomes the new source etc.
Title: Re: Fading without artifacts
Post by: Foaly on October 03, 2012, 06:11:43 pm
So that is the only way how i can fade out a render-texture? Using two textures and a shader. There is no way the problem with the artifacts will be resolved?

By the way: what exactly do you me by a render-texture can't be source and destination. If I apply the shader to the rectangle and draw it onto the render-texture, the rectangle is the source and the render-texture the destination. the shader simply uses information from the render-texture to compute it's color.
Title: Re: Fading without artifacts
Post by: Laurent on October 03, 2012, 06:20:54 pm
Quote
So that is the only way how i can fade out a render-texture? Using two textures and a shader
Nobody said that, this is just your idea. But since nobody seems to have the time to investigate this issue, you're on your own ;)

Quote
By the way: what exactly do you me by a render-texture can't be source and destination. If I apply the shader to the rectangle and draw it onto the render-texture, the rectangle is the source and the render-texture the destination. the shader simply uses information from the render-texture to compute it's color.
If you use the texture in your shader, it is a source. You can't read and write to the same texture at the same time.
Title: Re: Fading without artifacts
Post by: Foaly on October 07, 2012, 09:57:18 pm
Alright I found this article: http://www.pixelnerve.com/v/2010/07/20/pingpong-technique/ I think the technique described is what I need. Or does anybody else know a different technique on how I could achieve this effect?

@Laurent: Are still convinced that adding a subtract blendmode is still not an option? I think it would be really helpful. Especially with this issue or issues like this. You could simply use the subtract blendmode instead of solving it with two textures and a shader that subtracts the colors :)