Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Use multiple shaders on same sprite?  (Read 11764 times)

0 Members and 1 Guest are viewing this topic.

addel

  • Newbie
  • *
  • Posts: 17
    • View Profile
Use multiple shaders on same sprite?
« on: June 28, 2013, 11:00:52 pm »
I am having a problem when i try to apply 2 different fragment shaders to the same sprite. What currently happens is it just uses the second filter only rather than both of them.

        // Apply Fragment Shader
        if (sf::Shader::isAvailable()) {
            // Create the shader
            sf::Shader shader;
            sf::Shader shader2;

            // Load Fragment shader
            if (shader.loadFromFile("assets/pixelate.frag", sf::Shader::Fragment)){
                float x = static_cast<float>(sf::Mouse::getPosition(window).x) / window.getSize().x;
                shader.setParameter("pixel_threshold", x/10);
                window.draw(Sprite001, &shader);
            }

            // Load Fragment shader
            if (shader2.loadFromFile("assets/blur.frag", sf::Shader::Fragment)){
                float x = static_cast<float>(sf::Mouse::getPosition(window).x) / window.getSize().x;
                shader2.setParameter("blur_radius", x* 0.008f);
                window.draw(Sprite001, &shader2);
            }
        }

Is it possible to stack/use multiple GLSL shaders?

cpolymeris

  • Jr. Member
  • **
  • Posts: 65
    • View Profile
    • Email
Re: Use multiple shaders on same sprite?
« Reply #1 on: June 29, 2013, 02:15:26 am »
I haven't really delved into the topic of shaders, but it seems you are just drawing over your previous sprite render.
I would try rendering to a rendertexture with one shader then rendering that texture to the window with the other shader.

Of course, it's probably more efficient to have only one shader that does both things you want.

Hiura

  • SFML Team
  • Hero Member
  • *****
  • Posts: 4321
    • View Profile
    • Email
Re: Use multiple shaders on same sprite?
« Reply #2 on: June 29, 2013, 05:27:56 pm »
Maybe this old post will help you : http://en.sfml-dev.org/forums/index.php?topic=4799.0
SFML / OS X developer

Groogy

  • Hero Member
  • *****
  • Posts: 1469
    • MSN Messenger - groogy@groogy.se
    • View Profile
    • http://www.groogy.se
    • Email
Re: Use multiple shaders on same sprite?
« Reply #3 on: June 29, 2013, 05:51:09 pm »
What I find it that you are missing is probably a proper blend mode. Though it depends on the effect you want. Do you want to blur only the result from the first draw call with the first shader? Or is the blur shader somehow independent of the result of the first shader?

Edit: Since you have only the default blend mode, the previous data will be overwritten, what you would need to do is either use a blend mode that will preserve the old data and apply the new data. Or you render the sprite to a render texture first, and then use that texture as input for the second texture.
« Last Edit: June 29, 2013, 05:54:40 pm by Groogy »
Developer and Maker of rbSFML and Programmer at Paradox Development Studio

addel

  • Newbie
  • *
  • Posts: 17
    • View Profile
Re: Use multiple shaders on same sprite?
« Reply #4 on: June 29, 2013, 06:07:24 pm »
I still haven't found a solution yet. According to the shader tutorial you apply the shader with the "window.draw(Sprite001, &shader);" part however it seems to overwrite the last shader when you use that multiple times. Hopefully there is a alternative to using draw or a way to use many shaders in a single draw?

Maybe this old post will help you : http://en.sfml-dev.org/forums/index.php?topic=4799.0

Thanks but the code Laurent posted is not recognizing "RenderImage" which i guess must be something from SFML1 however i am currently using SFML2 so that code does not seem to work for me. I am not familiar with V1 or the differences between the versions so i have no idea how to update the code but it looks as if it probably works for V1 projects.

What I find it that you are missing is probably a proper blend mode. Though it depends on the effect you want. Do you want to blur only the result from the first draw call with the first shader? Or is the blur shader somehow independent of the result of the first shader?

Edit: Since you have only the default blend mode, the previous data will be overwritten, what you would need to do is either use a blend mode that will preserve the old data and apply the new data. Or you render the sprite to a render texture first, and then use that texture as input for the second texture.

I am not sure how to do this do you have a simple example if possible? i have not tested blend modes just yet. I was only using blur and pixelate as a test really but it could be any shaders and more than just 2 like i had in my code.

Usually when i code i start with simple examples like here and once they are working build them into a bigger project and expand on them. Basically my goal here however is just to be able to stack any amount shaders on the same image.
« Last Edit: June 29, 2013, 06:11:39 pm by addel »

Groogy

  • Hero Member
  • *****
  • Posts: 1469
    • MSN Messenger - groogy@groogy.se
    • View Profile
    • http://www.groogy.se
    • Email
Re: Use multiple shaders on same sprite?
« Reply #5 on: June 29, 2013, 06:15:10 pm »
The problem you have at the moment is that you are writing over your previous result, thus "only one shader is being shown". All of the shaders are applied, but only the last one is visible because you have written over it. A blend mode will let you choose how you merge these pixels. The default will give you the effect you have at the moment.

So blend mode is the most simple way to solve this. But they are quite limited. That is why I am suggesting what Laurent suggested (but that is from way back in time, it is called RenderTexture now and not RenderImage) that you have an intermediate buffer texture that you first render to, and then read from. That way you will first get the pxelate effect and then a blur effect applied on the pixelated result. While with blend mode you would get one pixelated result and one blurred result that is merged together.
Developer and Maker of rbSFML and Programmer at Paradox Development Studio

addel

  • Newbie
  • *
  • Posts: 17
    • View Profile
Re: Use multiple shaders on same sprite?
« Reply #6 on: June 29, 2013, 08:07:13 pm »
That sounds great but how do you use them in this way? I cannot find much information about blend modes.

It seems like i need sf::Blend::Add yet i have no idea how to use that with Sprite. I always thought a blend mode with add means the graphics get lighter though so i am kind of confused by what you mean.

Does anyone have a simple code example if possible?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Use multiple shaders on same sprite?
« Reply #7 on: June 29, 2013, 08:36:11 pm »
The two solutions are very different, so what do you want exactly? Apply effects separately and blend the results, or apply one effect after the other?
Laurent Gomila - SFML developer

Groogy

  • Hero Member
  • *****
  • Posts: 1469
    • MSN Messenger - groogy@groogy.se
    • View Profile
    • http://www.groogy.se
    • Email
Re: Use multiple shaders on same sprite?
« Reply #8 on: June 29, 2013, 09:00:00 pm »
That sounds great but how do you use them in this way? I cannot find much information about blend modes.

It seems like i need sf::Blend::Add yet i have no idea how to use that with Sprite. I always thought a blend mode with add means the graphics get lighter though so i am kind of confused by what you mean.

Does anyone have a simple code example if possible?

You want to look at sf::RenderStates, it is what you pass to the draw function after the sprite. The render state can take various settings for the draw call.

Like I said, blend modes will merge two results together. So Add would add the result of both pixels together thus giving a brighter result, yes. If you do the solution with shader, that will add the last shader on the first one. So if you pixelate first and then blur, you blur the result of the pixelation and not the source image you used.

Like Laurent said, it's hard for us to give you the right solution if we don't know exactly what effect you want to achieve. But if you are just experimenting to see how things work, you should do both :)
Developer and Maker of rbSFML and Programmer at Paradox Development Studio

addel

  • Newbie
  • *
  • Posts: 17
    • View Profile
Re: Use multiple shaders on same sprite?
« Reply #9 on: June 29, 2013, 10:21:43 pm »
The two solutions are very different, so what do you want exactly? Apply effects separately and blend the results, or apply one effect after the other?

I want to apply them one effect after the other.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Use multiple shaders on same sprite?
« Reply #10 on: June 29, 2013, 10:23:27 pm »
So you must use two sf::RenderTexture as described in the linked post (don't copy it directly, it's C#).
Laurent Gomila - SFML developer

addel

  • Newbie
  • *
  • Posts: 17
    • View Profile
Re: Use multiple shaders on same sprite?
« Reply #11 on: June 29, 2013, 10:40:22 pm »
I don't understand how to port the C# to C++ code or link the sf::RenderTexture with my original code. In that code it looks like back would be the sprite but i don't know what globalShaders is? Maybe like a shader array?

Could someone post a very simple code if possible please.

I try this in codeblocks but get a error about NonCopyable whatever that means.

sf::RenderTexture image1;
sf::RenderTexture image2;

sf::RenderTexture front = image1;
sf::RenderTexture back = image2;

if (sf::Shader::isAvailable()) {
foreach (sf::Shader shader in globalShaders) {
    // draw "back" into "front"
    front.Clear();
    front.Draw(new Sprite(back.Image), shader);
    front.Display();

    // swap front and back buffers
    sf::RenderTexture temp = front;
    front = back;
    back = temp;
}
}
« Last Edit: June 29, 2013, 10:50:09 pm by addel »

cpolymeris

  • Jr. Member
  • **
  • Posts: 65
    • View Profile
    • Email
Re: Use multiple shaders on same sprite?
« Reply #12 on: June 30, 2013, 12:17:50 am »
I don't understand how to port the C# to C++ code or link the sf::RenderTexture with my original code. In that code it looks like back would be the sprite but i don't know what globalShaders is? Maybe like a shader array?

Could someone post a very simple code if possible please.

I try this in codeblocks but get a error about NonCopyable whatever that means.

sf::RenderTexture image1;
sf::RenderTexture image2;

sf::RenderTexture front = image1;
sf::RenderTexture back = image2;

if (sf::Shader::isAvailable()) {
foreach (sf::Shader shader in globalShaders) {
    // draw "back" into "front"
    front.Clear();
    front.Draw(new Sprite(back.Image), shader);
    front.Display();

    // swap front and back buffers
    sf::RenderTexture temp = front;
    front = back;
    back = temp;
}
}

You can't copy textures (I think it's because they reside in video memory). Maybe you can swap pointers instead. Also, I move display out of the loop, (Nevermind, that's a bad idea)

sf::RenderTexture image1;
sf::RenderTexture image2;

sf::RenderTexture * front = new sf::RenderTexture(image1);
sf::RenderTexture * back = new sf::RenderTexture(image2);

if (sf::Shader::isAvailable()) {
foreach (sf::Shader shader in globalShaders) {
    // draw "back" into "front"
    front->Clear();
    front->Draw(Sprite(back->Image), shader);
    front->Display();

    // swap front and back buffers
    sf::RenderTexture * temp = front;
    front = back;
    back = temp;
}

sf:Sprite backSprite = back;
window.Draw(back);
window.Display()

// Remember to delete front and back somewhere...

}

Or something in that vein... I haven't tested it.
« Last Edit: June 30, 2013, 12:23:53 am by cpolymeris »

addel

  • Newbie
  • *
  • Posts: 17
    • View Profile
Re: Use multiple shaders on same sprite?
« Reply #13 on: June 30, 2013, 12:58:42 am »
It gives the same NonCopyable error unfortunately. I am also confused as to how you apply the shaders with this method, in that code globalShaders is no doubt a array of shader which i am not sure how to make but i don't know.

I am just a C++ noob having started only last week but i am surprised nobody has done shader stacking already as it seems like a very useful thing. I know i could make a single fragment shader with both the pixelate and blur packed together but that is not very efficient, a stack of shaders would be much better but right now i am totally stuck with this. :(

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Use multiple shaders on same sprite?
« Reply #14 on: June 30, 2013, 09:02:12 am »
std::vector<sf::Shader> shaders; // we assume it is already filled

sf::RenderTexture image1;
sf::RenderTexture image2;

sf::RenderTexture* front = &image1;
sf::RenderTexture* back = &image2;

// draw the initial scene into "back"
...

for (std::vector<sf::Shader>::iterator it = shaders.begin(); it != shaders.end(); ++it)
{
    // draw "back" into "front"
    front->clear();
    front->draw(sf::Sprite(back->getTexture()), &*it);
    front->display();

    // swap front and back buffers
    std::swap(back, front)
}

// the final result is in "back"
 

This is the general way, but you don't have to copy it directly, the most important is that you understand how it works so that you can adapt it to your own specific code.
Laurent Gomila - SFML developer