3

I am attempting to create a "hole in the fog" effect. I have a background grid image, overlapped onto that I have a "fog" texture that I use to show that certain areas are not in view. I am attempting to cut a chunk out of "fog" that will show the area that is currently in view. I am trying to "mask" a part of the fog off the screen.

I made some images to help explain what I am after:
Background:

BG

"Mask Image" (The full transparency has to be on the inside and not the outer rim for what I am going to use it for):

Mask

Fog (Sorry, hard to see.. Mostly Transparent):

Fog

What I want as a final Product:

Final Product

I have tried:

  • Stencil-Buffer: I got this fully working except for one fact... I wasn't able to figure out how to retain the fading transparency of the "mask" image.
  • glBlendFunc: I have tried many different version of the parameters and many other methods with it (glColorMask, glBlendEquation, glBlendFuncSeparate) I started by using some parameter that I found on this website: here. I used the "glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA);" as this seemed to be what I was looking for but... This is what ended up happening as a result: (Its hard to tell what is happening here but... There is fog covering the grid in the background. Though, the mask is just ending up as an fully opaque black blob when its supposed to be a transparent part in the fog.
    Result of glBlendFunc

Some previous code:

glEnable(GL_BLEND); // This is not really called here... It is called on the init function of the program as it is needed all the way through the rendering cycle.
renderFogTexture(delta, 0.55f); // This renders the fog texture over the background the 0.55f is the transparency of the image. 
glBlendFunc(GL_ZERO, GL11.GL_ONE_MINUS_SRC_ALPHA); // This is the one I tried from one of the many website I have been to today.
renderFogCircles(delta); // This just draws one (or more) of the mask images to remove the fog in key places.

(I would have posted more code but after I tried many things I started removing some old code as it was getting very cluttered (I "backed them up" in block comments))

genpfault
  • 51,148
  • 11
  • 85
  • 139
Zachar543
  • 311
  • 3
  • 13

2 Answers2

3

This is doable, provided that you're not doing anything with the alpha of the framebuffer currently.

Step 1: Make sure that the alpha of the framebuffer is cleared to zero. So your glClearColor call needs to set the alpha to zero. Then call glClear as normal.

Step 2: Draw the mask image before you draw the "fog". As Tim said, once you blend with your fog, you can't undo that. So you need the mask data there first.

However, you also need to render the mask specially. You only want the mask to modify the framebuffer's alpha. You don't want it to mess with the RGB color. To do that, use this function: glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE). This turns off writes to the RGB part of the color; thus, only the alpha will be modified.

Your mask texture seems to have zero where it is visible and one where it isn't. However, the algorithm needs the opposite, so you should either fix your texture or use a glTexEnv mode that will effectively flip the alpha.

After this step, your framebuffer should have an alpha of 0 where we want to see the fog, and an alpha of 1 where we don't.

Also, don't forget to undo the glColorMask call after rendering the mask. You need to get those colors back.

Step 3: Render the fog. That's easy enough; to make the masking work, you need a special blend mode. Like this one:

glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA, GL_ZERO, GL_ONE);

The separation between the RGB and A blend portions is important. You don't want to change the framebuffer's alpha (just in case you want to render more than one layer of fog).

And you're done.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • If you use the RGB blend equation (ONE_MINUS_DST_ALPHA, DST_ALPHA), and as you said "alpha is 0 where we want to see the fog", then the result color will be ((1-0)*fog_rgb + 0*dst_rgb). This will make the fog totally opaque, will it not? Doesn't the alpha value of the fog layer need to be used somewhere in the RGB calculation? – Tim Jun 06 '12 at 17:02
  • I am about to test this, though, I believe that inverting the mask may not work well for me. renderFogCircles() renders more than one circle and they may or may not be overlapping. Would this not end in strange graphical issues with the alpha inverted? Anyway... I will test it soon to make sure. – Zachar543 Jun 06 '12 at 18:51
  • The current mask (but white instead of black) seems to give the desired effect. Inverting the alpha on the above mask seems to give the exact opposite effect. (the rims are transparent) Though, there is still something wrong... This code seems to chance all of the fog into an opaque white no matter what I use for transparency values. http://img707.imageshack.us/img707/751/screenshot2012060615012.png (I undo the above glBlendFunc with the parameters that I use elsewhere "glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);") The fog no longer seems to be blending with the background. – Zachar543 Jun 06 '12 at 19:04
  • @NicolBolas Tim had it right in the first comment on this answer. I got pretty far into this and found that there was a major problem with your suggested glBlendFunc parameters. No matter what I change these settings seem to make the fog image (Destination) completely opaque. (I cannot change the opacity even if I replace the fog drawing with a plain full screen quad) Not only that but it seems to make the fog a solid color. – Zachar543 Jun 07 '12 at 03:19
1

The approach you're currently taking will not work, as once you draw the fog over the whole screen, there's no way to 'erase' it.

If you're using fixed pipeline:

You can use multitexturing (glTexEnv) with fixed pipeline to combine the fog and circle textures in a single pass. This function is probably kind of confusing if you haven't used it before, you'll probably have to spend some time studying the man page. You'll do something like bind fog to glActiveTexture 0, and mask to glActiveTexture 1, enable multitexturing, and then combine them with glTexEnv. I don't remember exactly the right parameters for this.

If you're using shaders:

Use a multitexturing shader where you multiply the fog alpha with the circle texture (to zero out the alpha in the circle region), and then blend this combined texture into the background in a single pass. This is probably a more conceptually easy approach, but not sure if you're using shaders.

I'm not sure there's a way you can do this where you draw the fog and the mask in separate passes, as they both have their own alpha values, it will be difficult to combine them to get the right color result.

Tim
  • 35,413
  • 11
  • 95
  • 121
  • Does multitexturing allow me to move where the mask is in realtime? Does it allow for multiple mask images? – Zachar543 Jun 06 '12 at 18:52
  • 1
    If you want to use a lot of masks, I might try rendering all the masks to a FBO texture, and then use that instead. It will be difficult to use multitexturing with an arbitrary number of texcoords. You could get away with it for one or two, but more than that I think it's not really feasible. – Tim Jun 06 '12 at 19:02
  • @NicolBolas I converted my system to prerender the masks to a FBO. Though, I seem to be having some issues. http://img204.imageshack.us/img204/6534/screenshot2012060621210.png is the result of drawing the FBO on the side of my screen. (with only red channel and alpha turned on) If I turn off the alpha channel, through "glColorMask", the strange overlap does not exist any more. I am then left with an opaque image. Though, when I turn the alpha on I get the issue in the image above. The second drawn mask seems to overwrite the alpha already drawn into the FBO. Blending does not seem to work – Zachar543 Jun 07 '12 at 01:27
  • I ended up fixing the image above... Though [this problem](http://img707.imageshack.us/img707/751/screenshot2012060615012.png) still exists. (The fog is completely opaque) – Zachar543 Jun 07 '12 at 20:00