Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Direct3D rendering 2D images with "multiply" blending mode and alpha

I'm trying to replicate the Photoshop filter multiply with Direct3D. I've been reading and googling about the different render states and I've got the effect almost working. The problem is that it's ignoring the alpha value of the textures.

Here's an image that explains the sitution:

http://www.kloonigames.com/petri/stackoverflow_doesnt_allow_.jpg

I found one solution to this, which was to save the images with no transparency and white background. But I'm not satisfied with this solution. The problem is that I really need to use the alpha value. I want to fade out the images gradually. And I cannot do this if the blending mode is ignoring the alpha value.

So the question is how to render the images with alpha?

Here's the blending mode code:

dev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR);

Edit added the SetTextureStageState

dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
dev->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
dev->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
dev->SetTextureStageState(0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE);
like image 940
user35268 Avatar asked Oct 23 '09 13:10

user35268


2 Answers

You can achieve this effect in one step by premultipling alpha in your pixel shader, or by using textures with pre-multiplied alpha.

For example if you have 3 possible blend operations for a shader, and you want each one to take alpha into account.

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

You'll notice that Multiply is impossible with a single pass because there are two operations on the source color. But if you premultiply alpha in your shader you can extract the alpha component from the blending operation and it becomes possible to blend all three operations in the same shader.

In your pixel shader you can pre-multiply alpha manually. Or use a tool like DirectXTex texconv to modify your textures.

return float4(color.rgb*color.a, color.a);

The operations become:

Blend = ( src.rgb ) + ( dest.rgb * (1-src.a) )
Add = ( src.rgb ) + ( dest.rgb )
Multiply = ( src.rgb * dest.rgb ) + (dest.rgb * (1-src.a) )
like image 166
George Davison Avatar answered Sep 30 '22 17:09

George Davison


It sounds like you want:

dst.rgb = (src.a * src.rgb) * ((1 - src.a) * dst.rgb)

You would use D3DRS_BLENDOP to do that, but unfortunately there isn't a D3DBLENDOP_MULTIPLY. I don't think this operation is possible without a fragment shader.

like image 27
noonat Avatar answered Sep 30 '22 16:09

noonat