How to blend the color of two sprites with constant alpha in DirectX?

Essentially what I want to do (in DirectX) is to take two partially transparent images and merge them together. This works great with default blending as they both appear as overlapping etc. However, the problem is that the opacity increases noticeably where the two intersect. This leads to more problems as there is a greater overlap of sprites. What I would like to do is keep the blending the same, except that the global opacity for all of these sprites gets blended no matter how they overlap.

It looks like there would be a render setting for that (all those sprites are left alone in their batch of sprites, which makes this part easier), but if so, then I don't know. Right now I'm filming in the dark and I've tried a lot of different things and none of them looked right. I know I probably need some kind of D3DBLENDOP variant, but I just don't know what settings I really need (I've tried a lot, but at this point everyone's guessing).

Here's a screenshot of what's actually going on with standard blending (the best I can get): http://arcengames.com/share/FFActual.png Here's a screenshot with a mockup of how I would like the blending to work ( force fields were added to the same layer in Photoshop and then given a generic alpha value): http://arcengames.com/share/FFMockup.png

Here's how I did it in Photoshop: 1. Take two images and remove all transparency (excluding fully transparent pixels). 2. Merge them into a single layer that blends the color but does not have partial alpha. 3. Now set the global opacity for this layer to (say) 40%.

The result is something that looks like blended in color, but does not increase the opacity on the overlapping sections.

UPDATE: Ok, thanks to Gotsu below who suggested using Z-Buffer. It works! The blending is, by and large, perfect and exactly what I would like. The only remaining problem? Using this new technique, there is a huge artifact around the edge of the force field image that is displayed last. See this: http://www.arcengames.com/share/FFZBuffer.png

UPDATE: Below is the final solution in C # (SlimDX)

  • Resetting ZBuffer to black, transparent, or white once for each frame has the same effect (this is correct before calling BeginScene)

Direct3DWrapper.ClearDevice (SlimDX.Direct3D9.ClearFlags.ZBuffer, Color.Transparent, 0);

  1. All other sprites are drawn at Z = 1, with ZBuffer disabled for them:

device.SetRenderState (RenderState.ZEnable, ZBufferType.DontUseZBuffer);

  1. Force field symbols are drawn with Z = 2, with ZBuffer and ZWrite enabled, and ZFunc as Less:

device.SetRenderState (RenderState.ZEnable, ZBufferType.UseZBuffer); device.SetRenderState (RenderState.ZWriteEnable, true); device.SetRenderState (RenderState.ZFunc, Compare.Less);

  1. The following flags are also set at this time to prevent the detection of the black edge artifact:

device.SetRenderState (RenderState.AlphaTestEnable, true); device.SetRenderState (RenderState.AlphaFunc, Compare.GreaterEqual); device.SetRenderState (RenderState.AlphaRef, 55);

Note that the AlphaRef is at 55 due to the alpha levels set in the particular original image I used. If my original image had a higher alpha then the AlphaRef should be higher as well.

+2


source to share


3 answers


It is best to say that force fields are a whole object. Why not make them last, in reverse order and with Z-buffering enabled. This will give you the effect you are after.

those. its non-mixing settings that don't address your problem at all.

Edit: can you use render-to-texture? IF this way you can easily do what you did in Photoshop. Select them all together into a texture and then blend the texture across the screen.

Edit2: How about

ALPHATESTENABLE  = TRUE;
ALPHAFUNC = LESS

ALPHABLENDENABLE = TRUE;
SRCBLEND = SRCALPHA;
DESTBLEND = INVSRCALPHA;

SEPERATEALPHABLENDENABLE = TRUE;
SRCBLENDALPHA = ONE;
DESTBLENDALPHA = ZERO;

      

You need to make sure the alpha is cleared to 0xff in the frame every frame. Then you do a standard alpha blend. passing the alpha value directly to the buffer buffer. This, however, is where the alpha test comes in. You are checking the final value of the alpha function against that in the back buffer. If this is less than what the buffer has, then that pixel has not yet been blended and will be placed in the framebuffer. If it is equal (or greater), then it has already been blended and the alpha value will be discarded.

That said ... using a Z-Buffer will cost you a load of RAM, but will be faster as it can discard pixels much earlier in the pipeline. Seeing that all screens just have to be recorded on a given Z-plane, you wouldn't even need to go through the hell I suggested earlier. If the resulting Z value is less than what is already there, it will display it, if it is greater or equal, then it will undo it, fortunately, before the mix calculation is done.



That said ... you can also do this using a stencil buffer, which will require a Z-buffer anyway.

Anyway ... hope one of these methods helps.

Edit3: Are you rendering a force field with some form of plumage around the edge? This edge is most likely caused by the alpha fading slightly and then "slightly alpha" pixels being written to the z-buffer, and hence any subsequent draw does not overwrite them.

Try the following settings

ALPHATESTENABLE = TRUE
ALPHAFUNC = GREATEREQUAL // if this doesn't work try less .. i may be being a retard
ALPHAREF = 255

      

To fine tune the feathers around the edge, edit the alpharef, but I suspect you need to keep it as above.

+1


source


You can specify D3DBLENDOP to use when blending two images for the alpha channel. It looks like what you are D3DBLENDOP_ADD

currently using - try switching it to D3DBLENDOP_MAX

, as this will just use the opacity of the "most opaque" image.



+1


source


It's hard to tell exactly what you are trying to accomplish from your layout, since both force fields are the same color; do you want to mix colors and close the alpha? Take one of the flowers?

Based on the above discussion, it is not clear if you will tweak all the relevant render states:

D3DRS_ALPHABLENDENABLE = TRUE (default: FALSE)

D3DRS_BLENDOP = D3DBLENDOP_MAX (default: D3DBLENDOP_ADD)

D3DRS_SRCBLEND = D3DBLEND_ONE (default: D3DBLEND_ONE)

D3DRS_DESTBLEND = D3DBLEND_ONE (default: D3DBLEND_ZERO)

It looks like you're installing the first two, but what about the last two?

+1


source







All Articles