• Advertisement
Sign in to follow this  

Alpha mask dynamically rotated triangle

This topic is 816 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey!

Here is my scenario. I have a normal scene rendered and I want to render a masked image on top of it. Quick sketch:

01jLgSG.png

or:

wfEN78m.png

 

So image area must be on a white area. triangle's angle will be adjusted as player will be aiming. What strategy should I follow? Should I just create an alpha mask texture every time the angle changes, pass it to the shader and use it? Or should I check if pixel is outside a triangle in fragment shader and draw only then? I assume a second solution would be performing slower. But generating an alpha mask texture, 60 times a second at most, also doesn't seem so fast. Also, I want those faded edges around a traingle! Which would probably mean second solution would require some distance checking for every pixel or something.

Edited by zgintasz

Share this post


Link to post
Share on other sites
Advertisement

Is this mask the size of the screen?

 

Create an off-screen buffer, fill it to white, draw the black triangles, blur it gently to get soft edges, then use that offscreen buffer as an alpha mask for whatever texture you want to render to the real screen.

 

You don't have to check if a pixel is black, just multiply the color against the alpha of the texture you are drawing (or if your texture is fully opaque, just use it as the alpha, instead of multiplying it).

 

Instead of blurring it (which can be slow), you can make the offscreen buffer half (or even a quarter) the resolution of the screen, and stretch the alpha mask, giving it soft edges. It'd be a lower quality blur, but since you are using it as an alpha mask and not drawing it directly, it'd probably be good enough because the texturing would disguise it.

 

Don't destroy/recreate the offscreen buffer every frame, but keep it in memory so all you have to do is clear and re-draw the black triangles, instead of reallocating the texture every frame.

 

Other than that, I wouldn't bother about performance unless you actually measure it to be a problem.

Share this post


Link to post
Share on other sites
Hmm, so you suggest to generate an alpha mask texture. I guess rendering it 1/2x screen size will make it look really cheap, won't it? What are my other options on fading edges? I guess I could check pixel's distance to the closest point in triangle, but that would probably be expensive.

Share this post


Link to post
Share on other sites

You can always create a blurry alpha texture and map it to a triangle. The blurry alpha texture only need to be created once. You could simply blur it in photoshop and assing the proper UV coordinates by hand or by using a 3D art tool.

 

Clear the entire screen to all black, (0,0,0,0) or half black (0,0,0,.5). However dark you want the black portion to be.

Draw a textured triangle. Apply rotation and position to the players position. Use the pre-made blurry alpha texture applied to triangle. Render it as (1,1,1,0).

Save the screen to texture. or if using an FBO you have drawn to a texture already.

Draw the scene as normal with applying the mask texture however you wish. Either in a shader or by doing a full-screen blend operation. All the white triangle portions will have 0 alpha, meaning it is fully transparent and all rendered parts of your world will not be affected. THe black porstions can be scaled darker or lighter by chaning the clear scren color's alpha in the first ste.

Share this post


Link to post
Share on other sites

 

Okay, I will try, I think it will work. But what if I needed a dynamic size triangle?

 

Then in my scenario the texture edges will stretch a bit, but should be negligible. Since the triangle stretches bigger, the alpha texture on the triangle would stretch maybe a few pixels or so.

Share this post


Link to post
Share on other sites

Alright, here is my progress so far. I'm simply rendering it like this(using libgdx by the way:

Texture maska = new Texture(Gdx.files.internal("res/textures/unpacked/sniper_aim_alpha_mask.png"));
gameWorld.getBatch().getSpriteBatch().setProjectionMatrix(gameWorld.getOrthographicCamera().combined);
        gameWorld.getBatch().getSpriteBatch().begin();
        sniperAimShader = new ShaderProgram(Gdx.files.internal(Constants.SHADER_SNIPERAIM_VERTEX), Gdx.files.internal(Constants.SHADER_SNIPERAIM_FRAGMENT));
        if(!sniperAimShader.isCompiled())
            Debug.log("ERROR COMPILING SNIPER AIM SHADER:" + sniperAimShader.getLog());
        sniperAimShader.begin();
        maska.bind(1);
        sniperAimShader.setUniformi("u_mask", 1);
        sniperAimShader.end();
        Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0);


        TextureRegion blurred = ((Gantown)gameWorld.getMap()).getBlurredMap();
        gameWorld.getBatch().getSpriteBatch().setShader(sniperAimShader);
        gameWorld.getBatch().getSpriteBatch().draw(blurred, 0, 0, blurred.getRegionWidth() * Constants.UNITSCALE, blurred.getRegionHeight() * Constants.UNITSCALE);
        gameWorld.getBatch().getSpriteBatch().end();

Here is my fragment shader:

//"in" attributes from our vertex shader
varying vec4 vColor;
varying vec2 vTexCoord;


//our different texture units
uniform sampler2D u_mask;
uniform sampler2D u_texture; //default GL_TEXTURE0, expected by SpriteBatch

void main(void) {
    //sample the colour from the first texture
    vec4 texColor0 = texture2D(u_texture, vTexCoord);

    //get the mask; we will only use the alpha channel
    vec4 mask = texture2D(u_mask, vTexCoord);

    //interpolate the colours based on the mask
    gl_FragColor = vec4(texColor0.rgb, mask.a);
}

How can I assign a mask position, so that it's not in default bottom left corner(as I guess)? And also, how can I scale it? As you can see, blurred image is drawn "* Constants.UNITSCALE", I don't know how I could do that in a shader.

I assume, if I managed to scale it(what I currently don't), it would look like this:

amebkru.png

I need to be able to move it somehow, e.g. to the center...

Edited by zgintasz

Share this post


Link to post
Share on other sites

Never heard of libgdx so I can't fully help you, but if you don't know how to positions and scale a sprite, I would assume you can find some tutorials with it. Or look at some basic openGL tutorials. It seems like you are going question to question on some basic things that if you looked at some tutorials on how openGL/DirectX work, you might build up a lot of knowledge and understanding. I'm assuming this is a top down shooter in 2D?

Share this post


Link to post
Share on other sites

Hmm, so you suggest to generate an alpha mask texture.

I suggest using OpenGL to generate the alpha mask texture, yes.
 

I guess rendering it 1/2x screen size will make it look really cheap, won't it?

Perhaps, but I think it might look good enough because your alpha would be 1/2 size, not the texture you are rendering; so the texture should disguise the lower-res alpha.
 

What are my other options on fading edges?

Blur, either with a pass over the mask texture itself, or by sampling multiple positions within the fragment shader when using the mask.
 

Here is my fragment shader:

//"in" attributes from our vertex shader
varying vec4 vColor;
varying vec2 vTexCoord;

//our different texture units
uniform sampler2D u_mask;
uniform sampler2D u_texture; //default GL_TEXTURE0, expected by SpriteBatch

void main(void) {
    //sample the colour from the first texture
    vec4 texColor0 = texture2D(u_texture, vTexCoord);

    //get the mask; we will only use the alpha channel
    vec4 mask = texture2D(u_mask, vTexCoord);

    //interpolate the colours based on the mask
    gl_FragColor = vec4(texColor0.rgb, mask.a);
}

Why are you using the alpha channel of your fully visible not transparent black and white image? smile.png 
 
You want to use any channel except the alpha channel. Just use mask.r instead of mask.a, but leave it in the same position in the vec4.

You want to use the colored greyscale texture as the alpha channel of your other texture.
 


How can I assign a mask position, so that it's not in default bottom left corner(as I guess)? And also, how can I scale it?
[...]
I need to be able to move it somehow, e.g. to the center...
 
I thought you wanted the mask to fill the entire screen? If so, you don't need to position the texture - use gl_FragCoord to index into the mask texture. You may need to normalize gl_FragCoord first. I do it like this:
//Normalize the screen coord, so we get screen coordinates from 0.0 to 1.0
vec2 viewportCoord = (gl_FragCoord.xy / ViewportSize);

//Invert the screen coordinate, because gl_FragCoord.y is the opposite of what we want (0.0 is the bottom of the screen?).
viewportCoord.y = (1 - viewportCoord.y);

Then you'd do something like:

//Use the viewport pixel positions to read from the mask, so the mask fills the entire viewport.
vec4 mask = texture2D(u_mask, viewportCoord); 

Share this post


Link to post
Share on other sites

Different tex-coords for the mask, provided through the vertices already, transformed or reconstructed (as Servant has shown) are warranted here. Maybe you wouldn't need those triangles at all: If you look at it as a post-process (it sounds like you're after some sort of vignetting) you just draw a screen quad/triangle and do all the magic in the fragment shader.

 

Here is an example I wrote for D3D11, hopefully providing some food for thought.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement