Direct3D rendering 2D images with “multiply” blending mode and alpha

后端 未结 4 1981
暖寄归人
暖寄归人 2021-01-03 04:31

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 worki

相关标签:
4条回答
  • 2021-01-03 05:28

    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.

    0 讨论(0)
  • 2021-01-03 05:30

    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) )
    
    0 讨论(0)
  • 2021-01-03 05:31
    dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR);
    dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
    

    Will do the trick. You cannot use the 'alpha' from the diffuse vertex color anymore though. Setting a low alpha on the vertex colors will actually brighten your overlaying pixels.

    0 讨论(0)
  • 2021-01-03 05:33

    OK this is not as simple as you would think. I would use an Effect & two renderTargets for this... I'm amusing your using one render pass to try to do this, which will not work. Photoshop has layers & each layers have an alpha channel. BTW it would be nice to know what kind of app your making.

    So first in D3D I would create 2 RGBA_32bit renderTargets of the same size as your window & clear them to color white. Make it an array like so (new RenderTarget[2];) for swapping.

    Now set the blending state to (AlphaFunc=Add, Src=SrcAlpha, Dst=InvSrcAlpha). For the first circle you draw it into renderTarget[0] using renderTarget[1] as a texture/sampler input source. You will render the circle with an Effect that will take the circles color & multiply it with renderTarget[1]'s sampler color. After you draw circle one you swap the renderTarget[0] with renderTarget[1] by simple indexing, so now renderTarget[1] is the one you draw to & renderTarget[0] is the one you sample from. Then you repeat the drawing process for circle 2 & so on.

    After you draw ever circle you copy the last drawn renderTarget to the backBuffer & present the scene.

    Here is an example of logically how you would do it. If you need reference for coding http://www.codesampler.com/ 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's 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 讨论(0)
提交回复
热议问题