Direct3D使用“乘法”混合模式和alpha渲染2D图像

时间:2009-10-23 13:51:50

标签: c++ graphics photoshop direct3d alphablending

我正在尝试使用Direct3D复制Photoshop滤镜。我一直在阅读和谷歌搜索不同的渲染状态,我的效果几乎正常。问题在于它忽略了纹理的alpha值。

这是一张解释情况的图片:

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

我找到了一个解决方案,即保存没有透明度和白色背景的图像。但我对这个解决方案不满意。问题是我真的需要使用alpha值。我想逐渐淡出图像。如果混合模式忽略alpha值,我就无法做到这一点。

所以问题是如何使用alpha渲染图像?

这是混合模式代码:

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

编辑添加了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 个答案:

答案 0 :(得分:4)

您可以通过在像素着色器中预乘alpha或使用预乘Alpha的纹理一步实现此效果。

例如,如果您有一个着色器的3种可能的混合操作,并且您希望每个操作都考虑到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) )

您会注意到单次传递不可能使用Multiply,因为源颜色上有两个操作。但是如果你在着色器中预乘alpha,你可以从混合操作中提取alpha分量,并且可以在同一个着色器中混合所有三个操作。

在像素着色器中,您可以手动预乘Alpha。或者使用像DirectXTex texconv这样的工具来修改纹理。

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

操作变为:

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

答案 1 :(得分:1)

这听起来像你想要的:

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

你会使用D3DRS_BLENDOP来做到这一点,但不幸的是没有D3DBLENDOP_MULTIPLY。如果没有片段着色器,我认为这种操作是不可能的。

答案 2 :(得分:0)

好的,这并不像你想象的那么简单。我会用一个效果&两个renderTargets用于此... 我很有趣你使用一个渲染过程来尝试这样做,这是行不通的。 Photoshop有图层和图层。每个图层都有一个alpha通道。顺便说一句,知道你制作什么样的应用程序会很好。

所以首先在D3D中我会创建2个RGBA_32bit renderTargets,其大小与窗口和放大器相同。将它们清除为白色。使它像一个数组(new RenderTarget [2];)进行交换。

现在将混合状态设置为(AlphaFunc = Add,Src = SrcAlpha,Dst = InvSrcAlpha)。对于第一个圆圈,使用renderTarget [1]作为纹理/采样器输入源将其绘制到renderTarget [0]中。您将使用效果渲染圆圈,该圆圈将采用圆圈颜色和圆圈。将它与renderTarget [1]的采样器颜色相乘。在绘制圆形之后,通过简单的索引将renderTarget [0]与renderTarget [1]交换,所以现在renderTarget [1]是你绘制的那个& renderTarget [0]是您从中抽样的。然后重复第2圈和第2圈的绘制过程。等等。

绘制圆圈后,将最后绘制的renderTarget复制到backBuffer&介绍现场。

以下是逻辑上如何执行此操作的示例。如果您需要参考编码http://www.codesampler.com/是一个好地方。

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;
}

答案 3 :(得分:0)

dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR);
dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

会做的伎俩。你不能使用' alpha'然而,从漫反射顶点颜色。在顶点颜色上设置低alpha值实际上会使叠加的像素变亮。