PDA

View Full Version : Alpha layer and fog of war



Casey Abernathey
04-21-2013, 01:59 PM
I am trying to implement a fog of war. I have done so using some very clunky black rectangles over a tilemap. I am looking to make something more fluid and visually appealing. Some searching has revealed that their maybe the possibility of using a large black texture and applying alpha maps to it. I am not an opengl coder by any means. Does anyone have an example or page that I could read up on how I could accomplish this with clanlib calls? Thanks.

rombust
04-22-2013, 08:32 AM
This is the method I would use:

Using GIMP or another graphics package that can draw onto the alpha (transparency) channel.

Create a black image of 512 pixels by 512 pixels. This is the color the "fog" fades to
Add an alpha channel to the image.

I'm not sure about the next bit (without trying it out)
Create a new white image of 512x512 pixels. Draw a gradient circle with the middle radius of 64x64 pixels at black, and gradient black to white to the border.
Use GIMP "color to alpha" ( http://docs.gimp.org/en/gimp-layer-transparency-menu.html ) to convert the image to an alpha channel.
Copy just the alpha channel of this image to the alpha channel of the first image.
Save as PNG

In your application:
Load the png as CL_Image or CL_Sprite and draw scaled to fit the entire window (whilst keeping the aspect ratio)

So in the center of your image, the alpha is 0, therefore no black is drawn.
In the middle of your image, the alpha is 0.5, therefore background blended with black
In the edge of your image, the alpha is 1.0, therefore black is drawn.

There are other ways to do this, but this is the easiest. For a top down game.

It will not work on an isometric game. I would use an OpenGL glsl shader for that, using a height map. Or numerious other methods

rombust
04-22-2013, 08:47 AM
And also someone once did a "CanvasAdvanced" example in ClanLib - http://clanlib.org/wiki/File:Example_canvasadvanced.png that might also be helpful. I don't know the code, so it might not be helpful at all :)

Casey Abernathey
04-22-2013, 07:19 PM
I had actually tried the same method as the canvas advanced but had no luck using this method when combining it with the GUI components. I will do a little more investigation into that code and see if I can combine the GUI and the canvas tutorial.

Casey Abernathey
04-23-2013, 03:23 AM
So here is where I am at. My light object doesn't actually erase the texture but instead it looks like a gradient black circle.

http://pastebin.com/5SUimHNN

This stems from the canvasAdvanced tutorial so the files are the images located in the examples directory with the installed clanlib.

Casey Abernathey
04-23-2013, 04:24 AM
In addition to the above code, I have tried adding the set_frame_buffer call but then none of the lightcontainer is drawn and anything after this doesn't draw.

Casey Abernathey
04-23-2013, 05:25 AM
FYI, the light_container draw is called on a custom gui component who is set to repaint constantly.

Judas
04-23-2013, 07:26 AM
The problem is the blend function you specify:


blender.set_blend_function(cl_blend_zero, cl_blend_one_minus_src_alpha, cl_blend_zero, cl_blend_one_minus_src_alpha);

In math terms this translates to:


dest.rgb = src.rgb * 0 + dest.rgb * 0;
dest.a = src.a * (1-src.a) + dest.a * (1-src.a);

If its the alpha channel of the texture you want to use as light blocker, then you want this math:


dest.rgb = src.rgb * 0 + dest.rgb * (1-src.a);
dest.a = src.a * 0 + dest.a * 1;

Which becomes:


blender.set_blend_function(cl_blend_zero, cl_blend_zero, cl_blend_one_minus_src_alpha, cl_blend_one);

Or if its the RGB color of the texture you want to use as light blocker:


dest.rgb = src.rgb * 0 + dest.rgb * src.rgb;
dest.a = src.a * 0 + dest.a * 1;

blender.set_blend_function(cl_blend_zero, cl_blend_zero, cl_blend_src, cl_blend_one);

Hopefully this clears things up a bit. :)

Casey Abernathey
04-23-2013, 02:55 PM
I'm starting to understand a little bit better thanks to your explanation. I did a little research on the opengl Blending formulas.

Here are a couple questions mixed with some observations.

Note that my graphics are a 256x256 Image with RGBA channels all set to white. My light source is a 256x256 image with a gradient on all RGBA channels from white to black. Let me know if I have something like my gradient backwards, which maybe the case if I read the earlier post correctly.

Question 1. Does it really matter what color the image is if all I want to do is blend the alpha channels?

If I want to disregard all RGB data, the first part of my formula looks like this.

dest.rgb = src.rgb * 0 + dest.rgb * 0;

If I want the gradient to cut away the all white image (essentially only using the alpha from the clip texture), I want to modify the second formula as follows.

dest.a = src.a * 0 + dest.a * 1;

Question 2. How do I know which is my src and which is my dest?
Question 3. Is this simply draw order?
Question 4. If I draw my giant white (turned black using CL_Texture::draw(0,0,0,255) first, is this considered to be the src when doing these formulas?

So the pseudo code looks like this.

gc.set_texture(0, fog);
CL_texture::draw(0,0,0,255); // Fills screen with black
gc.reset_texture();
gc.set_blend()
gc.set_texture(clip)
CL_texture::draw(255,255,255,255); // black part of alpha channel cuts away the filled screen
gc.reset_texture();

Casey Abernathey
04-23-2013, 09:11 PM
A little investigation into this and it sounds like perhaps you have to do this on the frame buffer? However, doing so, prevents anything after that from being drawn properly when my game loop is inside the custom CL_guiComponent's onRender function.

Edit: I am refering to using gc.set_frame_buffer(fb_lightmask); from the canvasAdvanced tutorial

Judas
04-23-2013, 10:26 PM
Question 1. Does it really matter what color the image is if all I want to do is blend the alpha channels?

No, it does not matter. The color of the source (the image) can be anything if the blend function doesn't use it.



If I want to disregard all RGB data, the first part of my formula looks like this.

dest.rgb = src.rgb * 0 + dest.rgb * 0;


Actually, no. Dest is the current value in the frame buffer, which means a formula like the above will store black into the frame buffer regardless of any alpha values or any color values. In my first post I multiply the inverse source alpha (the image) with the dest rgb (current color in the frame buffer). The result is that a fully transparent (alpha = 0) pixel in the source image causes the dest color to be kept, while a fully opaque pixel (alpha = 1) makes the dest color black.

I think what is probably confusing you is what dest and src is. Imagine we are drawing a textured rect (i.e. using CL_Draw::texture). Then for each pixel the rect covers we have a 'source' rgba pixel from the image, and a 'dest' rgba pixel from the frame buffer. The function then returns the new frame buffer value to be stored.

Strictly speaking the source color is the fragment output value from the fragment shader program, which for all the high level functions you are using means it is the image/texture.

Normal image transparency, like you see when using layers in GIMP, uses this blend function:

dest.rgb = src.rgb * src.a + dest.rgb * (1-src.a)

The value stored in dest.a usually doesn't matter that much unless you are rendering to a texture. I recommend that you simply keep dest.a. Its value is 1 in most cases.

dest.a = src.a * 0 + dest.a * 1;


Question 2. How do I know which is my src and which is my dest?
Question 3. Is this simply draw order?
Question 4. If I draw my giant white (turned black using CL_Texture::draw(0,0,0,255) first, is this considered to be the src when doing these formulas?

Hopefully my explanation above about src and dest should answer this. The draw order affects what is in 'dest', bit like if you move layers up and down in photoshop or GIMP.

Judas
04-23-2013, 10:29 PM
A little investigation into this and it sounds like perhaps you have to do this on the frame buffer? However, doing so, prevents anything after that from being drawn properly when my game loop is inside the custom CL_guiComponent's onRender function.

Edit: I am refering to using gc.set_frame_buffer(fb_lightmask); from the canvasAdvanced tutorial

There is always a frame buffer active. If you aren't setting one explicitly then the active frame buffer is the back buffer, which is what you want unless you are trying to render to a texture. Which you aren't in this particular instance. :)

Casey Abernathey
04-26-2013, 03:44 PM
Fixed!

I have to thank Judas for his loads of knowledge. I was finally able to make this work using the canvasAdvanced example and tons of information that Judas bestowed upon me. The key was to use the GuiManager Direct as Judas suggested. I will upload some code later on so that others in my same position of mixing frame buffers and gui components can have an example of what they need to do.

rombust
04-27-2013, 07:54 PM
Yeah, Judas is a very clever programmer. I didn't have a clue!

Judas
04-28-2013, 01:11 AM
Thank you for the kind words. :)