如何在DirectX中将两个sprite的颜色与常量alpha混合?

时间:2009-08-20 17:05:37

标签: graphics 3d directx blend alphablending

基本上,我想要做的(在DirectX中)是拍摄两个部分透明的图像并将它们混合在一起。这种方法适用于默认混合,只要它们都显示为重叠等。但是,问题是不透明度显着上升到两者相交的位置。随着更多精灵重叠,这会导致问题增加。我想做的是保持混合相同,除了保持混合所有这些精灵的全局不透明度,无论它们如何重叠。

似乎会有一个渲染设置(所有这些精灵都在他们的精灵批处理中,这使得这部分很容易),但如果是这样我就不知道了。现在我有点在黑暗中射击,我尝试了很多不同的东西,但没有一个看起来正确。我知道我可能需要一些D3DBLENDOP的变种,但我只是不知道我真正需要什么样的设置(我尝试了很多东西,但是在这个阶段都是猜测。)

以下是标准混合实际发生的事情的屏幕截图(我能得到的最好):http://arcengames.com/share/FFActual.png这是一个截图,其中包含了我希望如何混合的模型(力场是添加到Photoshop中的同一图层,然后给出共享的alpha值):http://arcengames.com/share/FFMockup.png

这就是我在Photoshop中的表现: 1.拍摄两张图像,然后删除所有透明度(完全透明的像素除外)。 2.将它们组合成一层,它混合颜色但根本没有部分alpha。 3.现在将该图层的全局透明度设置为(例如)40%。

结果看起来有些颜色混合在一起,但在重叠部分的不透明度没有增加。

更新:好的,非常感谢Goz,他建议使用Z-Buffer。这样可行!总的来说,混合是完美的,也是我想要的。唯一剩下的问题?使用这种新方法,在最后渲染的力场图像的边缘周围存在巨大的伪影。请参阅:http://www.arcengames.com/share/FFZBuffer.png

更新:以下是C#(SlimDX)

中的最终解决方案
  1. 每帧将ZBuffer清除为黑色,透明或白色一次都具有相同的效果(这是在调用BeginScene之前)
  2. Direct3DWrapper.ClearDevice(SlimDX.Direct3D9.ClearFlags.ZBuffer,Color.Transparent,0);

    1. 所有其他精灵都是在Z = 1时绘制的,ZBuffer为它们禁用:
    2. device.SetRenderState(RenderState.ZEnable,ZBufferType.DontUseZBuffer);

      1. 强制场精灵在Z = 2时绘制,启用ZBuffer并启用ZWrite,ZFunc为Less:
      2. device.SetRenderState(RenderState.ZEnable,ZBufferType.UseZBuffer); device.SetRenderState(RenderState.ZWriteEnable,true); device.SetRenderState(RenderState.ZFunc,Compare.Less);

        1. 此时还设置了以下标志,以防止遇到黑色边框神器:
        2. device.SetRenderState(RenderState.AlphaTestEnable,true); device.SetRenderState(RenderState.AlphaFunc,Compare.GreaterEqual); device.SetRenderState(RenderState.AlphaRef,55);

          请注意,AlphaRef为55,因为我使用的特定源图像中设置了alpha级别。如果我的源图像具有更高的alpha值,那么AlphaRef也需要更高。

3 个答案:

答案 0 :(得分:1)

您可以指定将两个图像混合在一起用于Alpha通道时使用的D3DBLENDOP。这听起来像是你当前正在使用D3DBLENDOP_ADD - 尝试将其切换为D3DBLENDOP_MAX,因为这只会使用“最不透明”图像的不透明度。

答案 1 :(得分:1)

由于两个力场都是相同的颜色,因此很难准确地说明你想要从模拟中完成什么。你想混合颜色并盖上阿尔法吗?只取一种颜色?

根据上述讨论,如果您要设置所有相关的渲染状态,则不清楚:

D3DRS_ALPHABLENDENABLE = TRUE(默认值:FALSE)

D3DRS_BLENDOP = D3DBLENDOP_MAX(默认值:D3DBLENDOP_ADD)

D3DRS_SRCBLEND = D3DBLEND_ONE(默认值:D3DBLEND_ONE)

D3DRS_DESTBLEND = D3DBLEND_ONE(默认值:D3DBLEND_ZERO)

听起来你正在设置前两个,但最后两个呢?

答案 2 :(得分:1)

我能说的最好的是力场是一个完整的物体。为什么不在前后顺序渲染它们,并启用Z缓冲。这会给你效果。

即它没有混合设置就是你的问题。

编辑:你可以使用渲染到纹理吗?如果这样你可以轻松地做你在photoshop下做的事情。将它们全部渲染到纹理中,然后将纹理混合回屏幕。

Edit2:怎么样

ALPHATESTENABLE  = TRUE;
ALPHAFUNC = LESS

ALPHABLENDENABLE = TRUE;
SRCBLEND = SRCALPHA;
DESTBLEND = INVSRCALPHA;

SEPERATEALPHABLENDENABLE = TRUE;
SRCBLENDALPHA = ONE;
DESTBLENDALPHA = ZERO;

您需要确保每帧在帧缓冲区中将alpha清除为0xff。然后你做标准的alpha混合。同时将alpha值直接传递给后备缓冲区。但是,这是进行alpha测试的地方。您可以针对后台缓冲区中的最终alpha值进行测试。如果它小于后备缓冲区中的什么,那么该像素尚未被混合并将被放入帧缓冲器中。如果它相等(或更大),那么它已经被混合,并且将丢弃阿尔法值。

那就是说...使用Z-Buffer会花费你一大笔RAM,但整体速度会更快,因为它可以在管道中更早地丢弃像素。看到所有的盾牌只需要写入给定的Z平面,你甚至不需要经历我之前建议的地狱。如果它接收到的Z值小于已经存在的那么它将会渲染它,如果它大于或等于它将丢弃它,幸运的是在进行混合计算之前。

那说......你也可以通过使用模板缓冲区来完成它,无论如何都需要一个Z缓冲区。

无论如何......希望其中一种方法有所帮助。

编辑3:你是否在边缘附近呈现某种形状的羽状渲染力场?最有可能的是,边缘是由于α略微淡化然后“略微α”像素被写入z缓冲区,因此任何后续绘制都不会覆盖它们。

尝试以下设置

ALPHATESTENABLE = TRUE
ALPHAFUNC = GREATEREQUAL // if this doesn't work try less .. i may be being a retard
ALPHAREF = 255

要微调边缘周围的羽化调整alpharef,但我怀疑你需要保持它如上所述。