SFML community forums

Help => Graphics => Topic started by: ChonDee on March 30, 2012, 06:00:02 am

Title: Sprite Masking in SFML2
Post by: ChonDee on March 30, 2012, 06:00:02 am
Hi,

I wanted to be able mask sprites such as having a base sprite and a masking sprite. The sprite used for masking is not drawn at all, only the alpha value is used such that the overlapping areas of the base sprite and the masking sprite is not drawn (or rather the masking sprite's alpha value is subtracted from the base sprite's alpha on the overlapping pixels).

I have made a sample demonstrating of what I would like to achieve:
(http://s7.postimage.org/5khfwyqmj/masking_Demo.jpg)

I noticed that there is an entry on SFML Wiki that would allow exactly just that:
http://www.sfml-dev.org/wiki/en/sources/masking_using_alpha_and_blending

Unfortunately it looks like this would only work with SFML1.6 and has been abandoned.

Could someone help me if this could be modified so that it would work with SFML2 textures or rendertargets?
If that is not possible, is this something that could be done with glsl fragment shaders?

I would really appreciate any assistance on how I could achieve masking sprites in SFML2.

Thanks in advance!
Title: Re: Sprite Masking in SFML2
Post by: Laurent on March 30, 2012, 08:00:14 am
I don't know if you can easily do something with SFML 2.0. I guess you'll have to wait until render masks are officially implemented.
Title: Re: Sprite Masking in SFML2
Post by: ChonDee on March 30, 2012, 08:12:43 am
I don't know if you can easily do something with SFML 2.0. I guess you'll have to wait until render masks are officially implemented.
Thanks for the answer.
I know its hard to know it in advance, but would you have a rough estimate on when might that be implemented, as of possibly in 1-3months, or more likely 6+ or something?
Title: Re: Sprite Masking in SFML2
Post by: Laurent on March 30, 2012, 08:36:04 am
I have no idea, sorry.
Title: Re: Sprite Masking in SFML2
Post by: bobingabout on March 30, 2012, 07:48:41 pm
I actually have a similar dilemma trying to write a space invaders type game. Bombs cause an explsion, and this explosion mark needs to be removed from the defence bunker sprite.
It is possible to write your own function, that iterates through the pixels, and where they line up, set the new pixel with an colour of alpha 0.

I'm not going to write a sample because:
A. I'm using 1.6, not 2.0, so things might have changed
B. I'm not that good at it anyway.
Title: Re: Sprite Masking in SFML2
Post by: ChonDee on March 31, 2012, 01:42:06 am
Well yes, that would work, but if that is dealing with the actual bitmap/image with CPU operations I would assume it would be quite expensive.
If what you said would be possible with a glsl fragment shader I guess it would be faster, but I am just starting to learn GLSL.

In your case however, as you are using SFML1.6, all this is implemented in an SFML wiki entry, that I have linked in the first post, check that out:
http://www.sfml-dev.org/wiki/en/sources/masking_using_alpha_and_blending
I just wanted to get this functionality in 2.0's rendertextures, and received the answer that this is still in development.
Title: Re: Sprite Masking in SFML2
Post by: ChonDee on March 31, 2012, 04:08:32 am
Laurent, (or anyone who could help :) )

Would that be possible for achieving something like this, that I draw the base sprite and the masking sprite of a specific color (magenta lets say), then in some way tell the rendertarget (or SFML globally) that magenta = full transparent?

Title: Re: Sprite Masking in SFML2
Post by: Laurent on March 31, 2012, 10:16:31 am
Quote
Would that be possible for achieving something like this, that I draw the base sprite and the masking sprite of a specific color (magenta lets say), then in some way tell the rendertarget (or SFML globally) that magenta = full transparent?
I don't think so.
Title: Re: Sprite Masking in SFML2
Post by: Dienes on April 11, 2012, 02:54:06 pm
It is actually possible. I made a small test demonstrating it:

http://www.youtube.com/watch?v=RTj_vBkxC4w

And it works exactly like ChonDee's idea. The trick is to use a RenderTexture, draw the part you want to cut out with a specific color (in my test I draw a CircleShape in Color::Magenta), extract the underlying Texture to a Image, apply a mask and use it for a Sprite (instead of using the RenderTexture's Texture):

Code: [Select]
// renderTex is the sf::RenderTexture
// img is a sf::Image
// tex is a sf::Texture
// spr is a sf::Sprite

// Update RenderTexture
renderTex.display();

// Turn the contents with your magenta shape into an Image and apply mask
img = renderTex.getTexture().copyToImage();
img.createMaskFromColor(sf::Color::Magenta);

// Update Texture from Image
tex.loadFromImage(img);

// Update Sprite from Texture
spr.setTexture(tex);

When drawing, just draw the Sprite.
I wrapped this in a class for easy and automated usage.

It might not be very fast, so just do it when really necessary (i.e. when you updated the content).
Title: Re: Sprite Masking in SFML2
Post by: ChonDee on April 12, 2012, 08:07:35 am
Thanks a lot for your reply and code segment, it is very helpful.

I imagine (as you have mentioned too) that dealing with images instead of textures will probably have a noticeable performance impact.

I was actually thinking about replacing what img.createMaskFromColor(sf::Color::Magenta); does with a fragment shader, such that every magenta pixel will be changed to 0.f alpha, that way the image->texture conversion would not be necessary.

I am not sure if this is possible like that though.

I quess it would depend on whether the shader is applied AFTER the image without shader is displayed or BEFORE. If it is after, then even though the magenta will be changed to transparent, whatever used to be in the background is already overwritten. Common sense would tell me that it is applied before, but I don't have experience about it. Could someone perhaps tell me how it works?


Another thing to Dienes:
Did you write your own particle system? Looks like they are checked for collision even between each other, are you doing some tricky optimization for that? Are you using some wider scope physics engine, or just some minimal footprint one for the particles?
Title: Re: Sprite Masking in SFML2
Post by: Laurent on April 12, 2012, 08:48:12 am
Quote
I am not sure if this is possible like that though.
It is possible, yes. And very easy to implement.

Quote
I quess it would depend on whether the shader is applied AFTER the image without shader is displayed or BEFORE. If it is after, then even though the magenta will be changed to transparent, whatever used to be in the background is already overwritten. Common sense would tell me that it is applied before, but I don't have experience about it. Could someone perhaps tell me how it works?
The pixel shader is not applied before nor after, it is used during the rendering of the sprite/texture. It replaces the pixel operations that the graphics card usually performs when there's no active pixel shader.
Title: Re: Sprite Masking in SFML2
Post by: Laurent on April 12, 2012, 09:02:35 am
There's a simpler and more efficient solution to this problem. You can create holes in the render-texture by rendering stuff with alpha = 0 and blend mode set to BlendNone. This way there's no need for a custom pixel shader, transparency is directly and correctly written to the target.

#include <SFML/Graphics.hpp>

int main()
{
    sf::RenderWindow window(sf::VideoMode(800, 600), "SFML holes");
    window.setVerticalSyncEnabled(true);

    sf::RenderTexture target;
    if (!target.create(window.getSize().x, window.getSize().y))
        return -1;

    target.clear(sf::Color::White);
    target.display();

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

        if (sf::Mouse::isButtonPressed(sf::Mouse::Left))
        {
            sf::Vertex point(sf::Vector2f(sf::Mouse::getPosition(window)), sf::Color::Transparent);
            target.draw(&point, 1, sf::Points, sf::BlendNone);
            target.display();
        }

        window.clear();
        window.draw(sf::Sprite(target.getTexture()));
        window.display();
    }

    return 0;
}
Title: Re: Sprite Masking in SFML2
Post by: Dienes on April 12, 2012, 11:32:59 am
To Laurent:
This looks interesting. I currently cannot test it, but would this approach allow for alphas between 0 and 255 to correctly make the RenderTexture's content half transparent?

To ChonDee:
They do not collide with each other. Once a particle comes to rest, I draw it onto the RenderTexture and remove it. Live particles do only collide with what's on the RenderTexture (I maintain a separate Bitfield for that). Physics themselves are just a gravity vector and a force I apply when spawning a particle. No physics engine/library behind it.
Title: Re: Sprite Masking in SFML2
Post by: Laurent on April 12, 2012, 11:44:29 am
Quote
This looks interesting. I currently cannot test it, but would this approach allow for alphas between 0 and 255 to correctly make the RenderTexture's content half transparent?
Yes. But keep this in mind: what you would see semi-transparently from the render-texture is what you draw to create the "hole", not what was originally in the render-texture.
In other words, you can't change only the alpha component of pixels with this technique, you must completely overwrite pixels. That could be done quite easily with OpenGL (either using glBlendFuncSeparate or glColorMask), but SFML doesn't provide this option.
Well, of course you can still do it with a pixel shader.
Title: Re: Sprite Masking in SFML2
Post by: Dienes on April 12, 2012, 02:45:14 pm
Okay, this version prevents you from using a texture to cut out areas. It will remove the complete bounding box of the sprite, no matter what the texture contains (I guess due to the BlendNone thing).

I will stick to my version for now, as it seems to work fine if used sparingly. I'd like to try out the pixel shader one, but I have absolutely no clue how to write shaders.
Title: Re: Sprite Masking in SFML2
Post by: Laurent on April 12, 2012, 02:58:55 pm
Quote
Okay, this version prevents you from using a texture to cut out areas. It will remove the complete bounding box of the sprite, no matter what the texture contains (I guess due to the BlendNone thing).
Yep. Could probably also be solved with the right blending mode.

Quote
I'd like to try out the pixel shader one, but I have absolutely no clue how to write shaders.
Here is a shader that sets alpha = 0 for pixels equal to a predefined colorkey:

uniform sampler2D texture;
uniform vec4 colorkey;

void main()
{
    vec4 pixel = texture2D(texture, gl_TexCoord[0].xy);
    if (pixel == colorkey)
        pixel.a = 0;
    gl_FragColor = pixel;
}

sf::Shader shader;
shader.loadFromFile("colorkey.frag", sf::Shader::Fragment);
shader.setParameter("texture", sf::Shader::CurrentTexture);
shader.setParameter("colorkey", sf::Color(/* whatever (magenta) */));

...

window.draw(sf::Sprite(target.getTexture()), &shader);
 
Title: Re: Sprite Masking in SFML2
Post by: Dienes on April 12, 2012, 03:18:20 pm
Just tested it and it works perfectly, thank you!

Much better and faster solution!
Title: Re: Sprite Masking in SFML2
Post by: ChonDee on April 13, 2012, 07:53:39 am
Wow, thanks very much, that's exactly what I wanted, I am definitely going to make good use of this!

Also thanks for the answers to my questions from both of you.  :)
Title: Re: Sprite Masking in SFML2
Post by: Vovosunt on April 15, 2012, 03:07:56 pm
There's a simpler and more efficient solution to this problem. You can create holes in the render-texture by rendering stuff with alpha = 0 and blend mode set to BlendNone. This way there's no need for a custom pixel shader, transparency is directly and correctly written to the target.

#include <SFML/Graphics.hpp>

int main()
{
    sf::RenderWindow window(sf::VideoMode(800, 600), "SFML holes");
    window.setVerticalSyncEnabled(true);

    sf::RenderTexture target;
    if (!target.create(window.getSize().x, window.getSize().y))
        return -1;

    target.clear(sf::Color::White);
    target.display();

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

        if (sf::Mouse::isButtonPressed(sf::Mouse::Left))
        {
            sf::Vertex point(sf::Vector2f(sf::Mouse::getPosition(window)), sf::Color::Transparent);
            target.draw(&point, 1, sf::Points, sf::BlendNone);
            target.display();
        }

        window.clear();
        window.draw(sf::Sprite(target.getTexture()));
        window.display();
    }

    return 0;
}

What the... I asked about this a week or so ago and we both agreed that it didn't work O_O
BTW just wondering, why are the completely transparent sprites won't do the same thing?
VertexArrays set to Quads will thought, so it doesn't really matter, but nonetheless.
Title: Re: Sprite Masking in SFML2
Post by: Laurent on April 15, 2012, 03:13:52 pm
Quote
What the... I asked about this a week or so ago and we both agreed that it didn't work O_O
No, remember what I said:
Quote
I need to clear a part of a renderTexture, but using transparent sprites/shapes won't work. They have got to have at least 1/255 opacity even with BlendMode set to BlendNone.
I changed this a few days ago, it should work with the latest revision of SFML 2.

Quote
BTW just wondering, why are the completely transparent sprites won't do the same thing?
Completely transparent sprites will work.
Title: Re: Sprite Masking in SFML2
Post by: Vovosunt on April 15, 2012, 04:00:54 pm
Oh. Indeed they do :)
Title: Re: Sprite Masking in SFML2
Post by: Vovosunt on April 15, 2012, 04:48:23 pm
I think the BlendMultiply BlendFunc is incorrect.
Why glBlendFunc(GL_DST_COLOR, GL_ZERO)?
It doesn't work with transparency.
Title: Re: Sprite Masking in SFML2
Post by: Vovosunt on April 15, 2012, 05:33:24 pm
But I guess there's no real way to do it with Alpha.
Thought I think (GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA) is a bit better.
Or maybe you could use the glBlendFuncSeparateEXT and add the alphas together or keeping only the destination alpha?
Seems logical. ???

Blendmodes are sometimes so hard to get you mind around  :-\
Good for reference: http://www.andersriggelsen.dk/glblendfunc.php (http://www.andersriggelsen.dk/glblendfunc.php)
Title: Re: Sprite Masking in SFML2
Post by: Laurent on April 15, 2012, 06:18:21 pm
Quote
Or maybe you could use the glBlendFuncSeparateEXT and add the alphas together or keeping only the destination alpha?
Seems logical.
Hmm yes, sounds like a good idea. However I'd use glBlendFuncSeparateEXT to keep the source alpha.
Title: Re: Sprite Masking in SFML2
Post by: Vovosunt on April 21, 2012, 05:27:26 pm
So, continuing on, if I'm gonna add some blendMode stuff to sfml, do I just insert it into the glCheck? :P
Does stuff like glBlendEquation also work?
And what is glCheck anyway? (couldn't really find anything on the net, but I guess it's something that checks gl states and sets them if they're not set...)
Title: Re: Sprite Masking in SFML2
Post by: Laurent on April 21, 2012, 06:09:26 pm
Quote
Does stuff like glBlendEquation also work?
Yes.

Quote
And what is glCheck anyway? (couldn't really find anything on the net, but I guess it's something that checks gl states and sets them if they're not set...)
It prints a message if the GL function fails. Look at src/Graphics/GLCheck.cpp (it's a SFML function).
Title: Re: Sprite Masking in SFML2
Post by: Vovosunt on April 21, 2012, 06:29:21 pm
It prints a message if the GL function fails. Look at src/Graphics/GLCheck.cpp (it's a SFML function).

I already did, but didn't quite get it it seems. So it's just an error catcher :)

Thanks for the quick answer, Laurent :D
Title: Re: Sprite Masking in SFML2
Post by: Vovosunt on April 21, 2012, 08:03:14 pm
Ok everything works, except that GL_MIN equation sticks around, and everything renders with it...
Were should I reset the original GL_FUNC_ADD?
Title: Re: Sprite Masking in SFML2
Post by: Laurent on April 21, 2012, 08:07:34 pm
Quote
Were should I reset the original GL_FUNC_ADD?
Everytime a blending mode is set that is not the MIN one (in applyBlendMode).
Title: Re: Sprite Masking in SFML2
Post by: vidjogamer on May 05, 2012, 01:11:12 am
Why doesnt this work:

Code: [Select]
sf::CircleShape circle(5,32);
circle.setPosition( sf::Mouse::getPosition(window).x, sf::Mouse::getPosition(window).y );
circle.setFillColor(sf::Color::Transparent);
target.draw(circle,sf::BlendNone);
target.display();


if this works:

Code: [Select]
sf::Vertex point(sf::Vector2f(sf::Mouse::getPosition(window)), sf::Color::Transparent);
target.draw(&point, 1, sf::Points, sf::BlendNone);
target.display();
Title: Re: Sprite Masking in SFML2
Post by: Laurent on May 05, 2012, 10:01:37 am
Because setFillColor(sf::Color::Transparent) has a special meaining (for optimization purpose), it skips drawing the inner part of the shape.