views:

565

answers:

3

In the process of trying to port my 2D shadow rendering technique from Directx to Opengl, I've run across a problem where I can't seem to get fine enough access to the opengl blender.

But first, so that the following makes sense, my algorithm:

I sort and render all sprites from back to front without using a depth buffer. Shadow sprites are drawn just before the objects that cast them and use the second blendstate below, gShadowBlendState. That makes them draw only their alpha values to the alpha channel on the framebuffer.

Sprites for regular objects use gBlendState specified first below, which is just a regular alpha blend to the backbuffer. However, they also remove alpha from the backbuffer alpha channel to the extent that they are opaque; this makes sure that shadows don't show through opaque objects that are in front of them.

The third thing that is drawn, and which is drawn after everything else, is a fullscreen quad that renders using the shadow colour and the final blend state below. It uses the backbuffer's alpha channel for opacity, effectively bringing out all the shadow information inscribed earlier.

The whole point of this exercise is to get shadows that don't get darker where they overlap.

 gBlendState.SrcBlend       = D3DBLEND_SRCALPHA;
 gBlendState.BlendOp        = D3DBLENDOP_ADD;
 gBlendState.DestBlend      = D3DBLEND_INVSRCALPHA;
 gBlendState.SrcBlendAlpha  = D3DBLEND_ZERO;
 gBlendState.BlendOpAlpha   = D3DBLENDOP_ADD;
 gBlendState.DestBlendAlpha = D3DBLEND_INVSRCALPHA;

 gShadowBlendState.SrcBlend       = D3DBLEND_ZERO;
 gShadowBlendState.BlendOp        = D3DBLENDOP_ADD;
 gShadowBlendState.DestBlend      = D3DBLEND_ONE;
 gShadowBlendState.SrcBlendAlpha  = D3DBLEND_ONE;
 gShadowBlendState.BlendOpAlpha   = D3DBLENDOP_MAX;
 gShadowBlendState.DestBlendAlpha = D3DBLEND_ONE;

 gShadowFillBlendState.SrcBlend       = D3DBLEND_DESTALPHA;
 gShadowFillBlendState.BlendOp        = D3DBLENDOP_ADD;
 gShadowFillBlendState.DestBlend      = D3DBLEND_INVDESTALPHA;
 gShadowFillBlendState.SrcBlendAlpha  = D3DBLEND_ONE;
 gShadowFillBlendState.BlendOpAlpha   = D3DBLENDOP_MAX;
 gShadowFillBlendState.DestBlendAlpha = D3DBLEND_ONE;

Anyway. I'm trying to do this now in Opengl. Currently I have alpha blending enabled with

 glEnable(GL_BLEND);
 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

but I'm not sure how to give it separate instructions for how to calculate the colour and alpha components. So my question is this: Is there a way to get finer control over the blender in OpenGL than just glBlendFunc() ?

+1  A: 

I'm not sure that you can get finer control, but you should be able to achieve most of what you want to do through using glColorMask to first render the color channels and then render the alpha channel. The last step seems like it should remain unchanged.

Eric
Thanks Eric, this does look like another way around the problem. I'm not that enthusiastic about rendering my geometry twice but this is a way of isolating the alpha channel that I hadn't considered.
cheesewiggle
+4  A: 

http://www.opengl.org/registry/specs/EXT/blend_func_separate.txt

Goz
Thanks, Goz, this is along the lines of what I'm after. Any idea how to get the D3DBLENDOP_MAX operation in addition to the basic add?
cheesewiggle
sorry for the slow response hve been awat on work in the states... however i think the extension you are after is http://www.opengl.org/registry/specs/EXT/blend_logic_op.txt
Goz
A: 

I left the answer here:

http://pastie.org/542369

Except there's one problem: Replace all the GL_ ADD with GL_ FUNC_ ADD. My mistake.