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

Author Topic: SFML 2.0 Shader Masking  (Read 7926 times)

0 Members and 1 Guest are viewing this topic.

BlazeTide

  • Newbie
  • *
  • Posts: 22
    • View Profile
SFML 2.0 Shader Masking
« on: November 25, 2012, 10:17:08 am »
Recently, I was able to create a crepuscular ray shader with the help of a few individuals. However, when it comes to creating a mask for this effect, I'm experiencing difficulties getting the rays to bleed past the edges of the solid sprite overlay in front of the mask.

I would assume that it would be correct to draw the overlay sprite within the RenderTexture (renderTexture variable), but after scouring through the SFML 2.0 Documentation, I was unable to find a way to pass the shader specifically to a texture within the RenderTexture. If anyone here knows of a solution to this problem, please feel free to share it!

main.cpp:

#include <SFML\Graphics.hpp>
#include <SFML\System.hpp>
#include <SFML\Window.hpp>
#include <iostream>

void main()
{
        sf::RenderWindow _window(sf::VideoMode(800, 480, 32), "Lighting Test");
        sf::RenderTexture renderTexture;
        sf::RenderStates renderState;
        _window.setFramerateLimit(60);
        renderTexture.create(_window.getSize().x, _window.getSize().y);

        sf::Shader lightingShader;
        sf::Texture texture;
        texture.loadFromFile("image.png");
        sf::Sprite sprite = sf::Sprite(texture);
        sf::Sprite sprite2 = sf::Sprite(texture);
        //sprite.setTexture(texture);
        sf::Texture backgroundTexture;
        backgroundTexture.loadFromFile("light.png");
        sf::Sprite background = sf::Sprite(backgroundTexture);
        //background.setTexture(backgroundTexture);

        while (_window.isOpen())
        {
                int x = sf::Mouse::getPosition(_window).x;
                int y = sf::Mouse::getPosition(_window).y;

                sf::Event sfEvent;

                while(_window.pollEvent(sfEvent))
                {
                        switch(sfEvent.type)
                        {
                                case sf::Event::Closed: _window.close();
                                        break;
                                case sf::Event::KeyPressed:
                                        if(sfEvent.key.code==sf::Keyboard::Q) _window.close();
                                        break;
                                default: break;
                        }
                }

                lightingShader.loadFromFile("lightingShader.frag", sf::Shader::Type::Fragment);
                lightingShader.setParameter("exposure", 0.008f);
                lightingShader.setParameter("decay", 0.97f);
                lightingShader.setParameter("density", 0.97f);
                lightingShader.setParameter("weight", 5.5f);
                lightingShader.setParameter("lightPositionOnScreen", sf::Vector2f(0.5f, 0.5f));
                //lightingShader.setParameter("texture", sf::Shader::CurrentTexture);
                renderState.shader = &lightingShader;

                sprite.setPosition(x - 504, y - 360);
                //sprite2.setPosition((x - 504) * 0.5, (y - 360));
                renderTexture.clear();
                renderTexture.draw(background);
                //renderTexture.draw(sprite2);
                renderTexture.draw(sprite);
                renderTexture.display();
                _window.clear();
                _window.draw(sf::Sprite(renderTexture.getTexture()), renderState); //<-- Shader Mask.
                _window.draw(sprite); //<-- Solid Sprite Overlay
                _window.display();
        }
}

lightingShader.frag:

uniform float exposure;
uniform float decay;
uniform float density;
uniform float weight;
uniform vec2 lightPositionOnScreen;
uniform sampler2D texture;
const int NUM_SAMPLES = 100;

void main()
{      
        vec2 deltaTextCoord = vec2( gl_TexCoord[0].xy - lightPositionOnScreen.xy);
        vec2 textCoord = gl_TexCoord[0].xy;
        deltaTextCoord *= 1.0 / float(NUM_SAMPLES) * density;
        float illuminationDecay = 1.0;
       
        for(int i=0; i < NUM_SAMPLES ; i++)
        {
                        textCoord -= deltaTextCoord;
                        vec4 sample = texture2D(texture, textCoord);
                       
                        sample *= illuminationDecay * weight;
                       
                        gl_FragColor += sample;
                       
                        illuminationDecay *= decay;
        }
       
        gl_FragColor *= exposure;
}

My OS is Windows 7, and I'm currently using the SFML 2.0 Release Candidate.

masskiller

  • Sr. Member
  • ****
  • Posts: 284
  • Pointers to Functions rock!
    • MSN Messenger - kyogre_jb@hotmail.com
    • View Profile
    • Email
Re: SFML 2.0 Shader Masking
« Reply #1 on: November 25, 2012, 09:25:07 pm »
You should load the shader outside of the loop.

Quote
I'm experiencing difficulties getting the rays to bleed past the edges of the solid sprite overlay in front of the mask.

I'm a beginner in GLSL myself so I might be mistaken (I can't tell if there's anything wrong with the frag shader), but a vertex shader or making the sprite's image/texture bigger (with alpha filled pixels) should make it work the way you want it. The fragment shader can't do anything outside of the area you are applying the shader to. A custom vertex shader for the given frag shader might fix up the problem.

Have you tried using the shader on just the sprite without the background or the rendertexture? You could test it to see how it behaves, so that you make sure that it's not the frag shader's fault and see if you actually need a bigger texture or a vert shader.
« Last Edit: November 25, 2012, 09:27:41 pm by masskiller »
Programmer, Artist, Composer and Storyline/Script Writer of "Origin of Magic". If all goes well this could turn into a commercial project!

Finally back into the programming world!

BlazeTide

  • Newbie
  • *
  • Posts: 22
    • View Profile
Re: SFML 2.0 Shader Masking
« Reply #2 on: November 26, 2012, 03:48:05 am »
Implementing a vertex shader removes the crepuscular ray effect completely, and removing the vertex shader altogether was how I actually managed to solve my previous rendering problems before posting this question on the forums.

While making the texture larger partially solves the problem after taking out the overlay sprite, the fragment shader still bleeds ALL of the textures rather than a single specified texture, and setting the sampler2D variable within the fragment shader to a specific texture removes the specified texture altogether.

I've provided an image below under the Attachments that displays what I currently have, if it is of any help.

[attachment deleted by admin]

masskiller

  • Sr. Member
  • ****
  • Posts: 284
  • Pointers to Functions rock!
    • MSN Messenger - kyogre_jb@hotmail.com
    • View Profile
    • Email
Re: SFML 2.0 Shader Masking
« Reply #3 on: November 26, 2012, 07:43:58 pm »
You want to shade only one of the textures right? Then try drawing the sprite alone directly to the window and apply the shader to it. You draw many things to the renderTexture and then you apply the shader to everything that you drew in it, if you only want the effect for one sprite then use it for that one sprite and not more than just one thing.

I'll test the code a bit later, but the original image would be nice to have for testing and visually checking out things.

Edit: Also, if you do no changes to the shader parameters they should be out of the loop and not inside it.
« Last Edit: November 26, 2012, 09:33:51 pm by masskiller »
Programmer, Artist, Composer and Storyline/Script Writer of "Origin of Magic". If all goes well this could turn into a commercial project!

Finally back into the programming world!

BlazeTide

  • Newbie
  • *
  • Posts: 22
    • View Profile
Re: SFML 2.0 Shader Masking
« Reply #4 on: November 27, 2012, 03:18:26 am »
The shader effect only works if I pass it through as a parameter with a RenderTexture being drawn with it. Passing the shader through as a parameter with a sprite being drawn applies the shader effect incorrectly, as the crepuscular rays do not change dynamically if I move the sprite around with my mouse.

Also, I moved the shader parameters out of the loop as you recommended. The good news is that the program runs a lot faster than before, so kudos to you for the tip. The bad news is that the fragment shader is still bleeding all of the textures rather than a single specified texture, so no real progress there so far.

masskiller

  • Sr. Member
  • ****
  • Posts: 284
  • Pointers to Functions rock!
    • MSN Messenger - kyogre_jb@hotmail.com
    • View Profile
    • Email
Re: SFML 2.0 Shader Masking
« Reply #5 on: November 27, 2012, 05:39:54 am »
That is indeed something odd. To tell you the truth I haven't the slightest clue of why it happens that way, maybe Laurent could be of more help than I with something like that. Then again I still need to test your code. Can you attach the image you use to set the texture?

Quote
Also, I moved the shader parameters out of the loop as you recommended. The good news is that the program runs a lot faster than before, so kudos to you for the tip. The bad news is that the fragment shader is still bleeding all of the textures rather than a single specified texture, so no real progress there so far.

I suggested this for speed and efficiency rather than fixing the shader applying issue. That change does no difference in how the shader works in general, since none of it's parameters change.
Programmer, Artist, Composer and Storyline/Script Writer of "Origin of Magic". If all goes well this could turn into a commercial project!

Finally back into the programming world!

cire

  • Full Member
  • ***
  • Posts: 138
    • View Profile
Re: SFML 2.0 Shader Masking
« Reply #6 on: November 27, 2012, 06:12:46 am »
The shader effect only works if I pass it through as a parameter with a RenderTexture being drawn with it. Passing the shader through as a parameter with a sprite being drawn applies the shader effect incorrectly, as the crepuscular rays do not change dynamically if I move the sprite around with my mouse.


When you draw a sprite, you don't expect it to affect an area other than where the sprite is drawn.  Only affecting the sprite, which is what you're seeing, is the correct behavior.  I think the bottom line is you can't expect to take a shader from somewhere and expect to plug it in and work exactly the way you want in your program.  To get the effect you want, you're going to have to do more work. I would guess that's going to require another texture object and some blending code.

You might want to check around 13.5.1 at the following link:
http://http.developer.nvidia.com/GPUGems3/gpugems3_ch13.html

masskiller

  • Sr. Member
  • ****
  • Posts: 284
  • Pointers to Functions rock!
    • MSN Messenger - kyogre_jb@hotmail.com
    • View Profile
    • Email
Re: SFML 2.0 Shader Masking
« Reply #7 on: November 27, 2012, 07:12:12 am »
Quote
When you draw a sprite, you don't expect it to affect an area other than where the sprite is drawn.  Only affecting the sprite, which is what you're seeing, is the correct behavior.  I think the bottom line is you can't expect to take a shader from somewhere and expect to plug it in and work exactly the way you want in your program.

Given this case, you could use a renderTexture where you draw only the sprite you want the effect on. and draw everything else normally. The shader will be applied to the whole renderTexture so you should have no problems with texture size if you set the renderTexture to a size that allows the effect to be fully shown.
Programmer, Artist, Composer and Storyline/Script Writer of "Origin of Magic". If all goes well this could turn into a commercial project!

Finally back into the programming world!

BlazeTide

  • Newbie
  • *
  • Posts: 22
    • View Profile
Re: SFML 2.0 Shader Masking
« Reply #8 on: November 28, 2012, 02:56:17 am »
Given this case, you could use a renderTexture where you draw only the sprite you want the effect on. and draw everything else normally. The shader will be applied to the whole renderTexture so you should have no problems with texture size if you set the renderTexture to a size that allows the effect to be fully shown.

The crepuscular rays won't bleed past any of the sprites if I were to do this, as I'm not passing through any particular shader while drawing the separate sprites. I will provide the textures I used for my program as soon as I fix my currently broken Dropbox.

When you draw a sprite, you don't expect it to affect an area other than where the sprite is drawn.  Only affecting the sprite, which is what you're seeing, is the correct behavior.  I think the bottom line is you can't expect to take a shader from somewhere and expect to plug it in and work exactly the way you want in your program.  To get the effect you want, you're going to have to do more work. I would guess that's going to require another texture object and some blending code.

You might want to check around 13.5.1 at the following link:
http://http.developer.nvidia.com/GPUGems3/gpugems3_ch13.html

I believe Cire here knows what my problem is, and I did indeed read through that tutorial before creating my program and shader. The only solution I can think of right now is to additively blend my current RenderTexture with the sprite overlay, but I have no idea how to accomplish this using the SFML 2.0 BlendModes.

BlazeTide

  • Newbie
  • *
  • Posts: 22
    • View Profile
Re: SFML 2.0 Shader Masking
« Reply #9 on: November 30, 2012, 02:56:46 am »
Alright, I've finally managed to fix my weird Dropbox error. Here are the textures I used for my crepuscular ray shader program:

image.png

https://www.dropbox.com/s/ffixj9habstdpny/image.png

light.png

https://www.dropbox.com/s/9plx3buk7f1999h/light.png

BlazeTide

  • Newbie
  • *
  • Posts: 22
    • View Profile
Re: SFML 2.0 Shader Masking
« Reply #10 on: December 03, 2012, 11:36:40 pm »
Bump, would anyone here happen to know how one would be able to draw and apply a RenderTexture as an additive blend?

cire

  • Full Member
  • ***
  • Posts: 138
    • View Profile
Re: SFML 2.0 Shader Masking
« Reply #11 on: December 04, 2012, 03:43:41 am »
I don't know if this is what you're going for, but...

#include <SFML\Graphics.hpp>
#include <SFML\System.hpp>
#include <SFML\Window.hpp>
#include <iostream>

void main()
{
    sf::RenderWindow _window(sf::VideoMode(800, 480, 32), "Lighting Test");
    _window.setFramerateLimit(60) ;

    sf::RenderTexture plainScene ;
    plainScene.create(_window.getSize().x, _window.getSize().y);

    sf::RenderTexture shadedScene;
    shadedScene.create(_window.getSize().x, _window.getSize().y) ;

    sf::Texture texture;
    texture.loadFromFile("image.png");
    sf::Sprite sprite = sf::Sprite(texture);
    sf::Sprite sprite2 = sf::Sprite(texture);
    //sprite.setTexture(texture);
    sf::Texture backgroundTexture;
    backgroundTexture.loadFromFile("light2.png");
    sf::Sprite background = sf::Sprite(backgroundTexture);
    //background.setTexture(backgroundTexture);

    sf::Shader lightingShader ;
    lightingShader.loadFromFile("lightingShader.frag", sf::Shader::Type::Fragment);
    lightingShader.setParameter("exposure", 0.008f);
    lightingShader.setParameter("decay", 0.97f);
    lightingShader.setParameter("density", 0.97f);
    lightingShader.setParameter("weight", 5.5f);
    lightingShader.setParameter("lightPositionOnScreen", sf::Vector2f(0.5f, 0.5f));

    sf::RenderStates renderState(&lightingShader) ;
 

    while (_window.isOpen())
    {
        int x = sf::Mouse::getPosition(_window).x;
        int y = sf::Mouse::getPosition(_window).y;

        sf::Event sfEvent;

        while(_window.pollEvent(sfEvent))
        {
            switch(sfEvent.type)
            {
                case sf::Event::Closed: _window.close();
                    break;
                case sf::Event::KeyPressed:
                    if(sfEvent.key.code==sf::Keyboard::Q) _window.close();
                    break;
                default: break;
            }
        }

        sf::RenderStates blendState(sf::BlendAlpha) ;

        sprite.setPosition(x - 504, y - 360);

        plainScene.clear() ;
        shadedScene.clear() ;
        _window.clear() ;

        plainScene.draw(background) ;
        plainScene.draw(sprite) ;
        plainScene.display() ;

        shadedScene.draw(sf::Sprite(plainScene.getTexture()), renderState) ;
        shadedScene.display();
         
        _window.draw(sf::Sprite(plainScene.getTexture())) ;
        _window.draw(sf::Sprite(shadedScene.getTexture()), sf::RenderStates(sf::BlendAdd));    
        _window.display();
    }
}



BlazeTide

  • Newbie
  • *
  • Posts: 22
    • View Profile
Re: SFML 2.0 Shader Masking
« Reply #12 on: December 04, 2012, 07:11:56 am »
Yes! This is EXACTLY what I was going for!

Thank you all very much for your wonderful help and assistance! I really appreciate all of you for spending your free time to help individuals like me fix and optimize their code!

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: SFML 2.0 Shader Masking
« Reply #13 on: December 04, 2012, 08:02:08 am »
Quote
sf::RenderStates blendState(sf::BlendAlpha) ;
Note that you could pass the blending mode directly to the draw function, the constructors of sf::RenderState are implicit.
Laurent Gomila - SFML developer

cire

  • Full Member
  • ***
  • Posts: 138
    • View Profile
Re: SFML 2.0 Shader Masking
« Reply #14 on: December 04, 2012, 09:27:54 am »
Yes! This is EXACTLY what I was going for!

Well, if it's EXACTLY what you're going for, I applied a little gaussian blur to your light.png to soften the rays up in the image

 

anything