5

我正在尝试使用 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

4 回答 4

5

您可以通过在像素着色器中预乘 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) )

您会注意到,单次通过就不可能进行乘法运算,因为对源颜色有两个操作。但是,如果您在着色器中预乘 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) )
于 2014-10-16T22:40:20.730 回答
1

听起来你想要:

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

您将使用 D3DRS_BLENDOP 来执行此操作,但不幸的是没有 D3DBLENDOP_MULTIPLY。我认为如果没有片段着色器,这个操作是不可能的。

于 2009-10-23T15:12:38.077 回答
0

好的,这并不像你想象的那么简单。我会为此使用一个效果和两个渲染目标...我很高兴您使用一个渲染通道来尝试执行此操作,但这是行不通的。Photoshop 有图层,每个图层都有一个 Alpha 通道。顺便说一句,很高兴知道您制作了什么样的应用程序。

因此,首先在 D3D 中,我将创建 2 个与窗口大小相同的 RGBA_32 位渲染目标并将它们清除为白色。使它成为一个这样的数组 (new RenderTarget[2];) 用于交换。

现在将混合状态设置为 (AlphaFunc=Add, Src=SrcAlpha, Dst=InvSrcAlpha)。对于第一个圆圈,您使用 renderTarget[1] 作为纹理/采样器输入源将其绘制到 renderTarget[0] 中。您将使用一个效果来渲染圆圈,该效果将采用圆圈颜色并将其与 renderTarget[1] 的采样器颜色相乘。画完一个圆圈后,您可以通过简单的索引将renderTarget[0] 与renderTarget[1] 交换,因此现在renderTarget[1] 是您绘制的对象,而renderTarget[0] 是您从中采样的对象。然后你重复圆圈 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;
}
于 2011-03-04T21:09:30.720 回答
0
dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR);
dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

会成功的。不过,您不能再使用漫反射顶点颜色中的“alpha”。在顶点颜色上设置低 alpha 实际上会使您的叠加像素变亮。

于 2016-08-30T12:44:02.373 回答