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

I am trying to replicate a Photoshop filter multiply by Direct3D. I read and searched for information about different rendering states, and my effect almost worked. The problem is that it ignores the alpha value of the textures.

Here's an image that explains satura:

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

I found one solution for this to save images without transparency and white background. But I am not satisfied with this decision. The problem is that I really need to use the alpha value. I want to gradually put out the images. And I cannot do this if the blend mode ignores the alpha value.

So the question is how to make images with alpha?

Here's the blend mode code:

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

Edit Added by 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); 
+4
source share
4 answers

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

For example, if you have 3 possible blending operations for a shader, and you want each one to accept alpha.

 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 will notice that Multiply is not possible with a single pass, because there are two operations in the original color. But if you surpassed alpha in your shader, you can extract the alpha component from the blend operation, and it becomes possible to mix 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); 

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) ) 
+4
source

It looks like you want:

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

You must use D3DRS_BLENDOP for this, but unfortunately there is no D3DBLENDOP_MULTIPLY. I don’t think this operation is possible without a fragmented shader.

+1
source

OK, this is not as easy as you think. I would use an effect and two renderTargets for this ... I make sure that I use one rendering step to try to do this, and that will not work. Photoshop has layers, and each layer has an alpha channel. By the way, it would be nice to know which application you are making.

So, first in D3D I would create 2 RGBA_32bit renderTargets of the same size as your window, and clear them to white. Make the array the same (new RenderTarget [2];) for sharing.

Now set the blending state to (AlphaFunc = Add, src = SrcAlpha, Dst = InvSrcAlpha). For the first circle, you draw it in renderTarget [0], using renderTarget [1] as the input source for the texture / sampler. You will have a circle with an effect that will take the color of the circles and multiply it by the color of the sample using renderTarget [1]. After you draw circle one, you change renderTarget [0] with renderTarget [1] by simple indexing, so now renderTarget [1] is the one you draw and renderTarget [0] is the one you choose. Then you repeat the drawing process for circle 2, etc.

After you draw a circle, you copy the last drawn rendering into the backBuffer and represent the scene.

Here is a logical example of how you do this. If you need a coding link http://www.codesampler.com/ , this is a good place.

 void TestLayering() { bool rtIndex = false; RenderTarget* renderTarget = new RenderTarget[2]; Effect effect = new Effect("multiplyEffect.fx"); effect.Enable(); BlendingFunc = Add; BlendingSource = SrcAlpha; BlendingDest = InvSrcAlpha; for(int i = 0; i != circleCount; ++i) { renderTarget[rtIndex].EnableAsRenderTarget(); renderTarget[!rtIndex].EnableAsSampler(); circle[i].Draw(); rtIndex = !rtIndex; } //Use D3D9 StretchRect for this... backBuffer.CopyFromSurface(renderTarget[rtIndex]); } //Here is the effects pixel shader float4 PS_Main(InStruct In) : COLOR { float4 backGround = tex2D(someSampler, In.UV); return circleColor * backGround; } 
0
source
 dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR); dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); 

Will do the trick. However, you cannot use alpha from the diffuse color of the vertices. Setting low alpha to vertex colors will actually lighten your overlay pixels.

0
source

All Articles