【问题标题】:How to blend color of two sprites with constant alpha in DirectX?如何在 DirectX 中将两个精灵的颜色与恒定的 alpha 混合?
【发布时间】:2009-08-20 17:05:37
【问题描述】:

基本上,我想做的(在 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 之前)

Direct3DWrapper.ClearDevice(SlimDX.Direct3D9.ClearFlags.ZBuffer, Color.Transparent, 0);

  1. 所有其他精灵都在 Z=1 处绘制,并为它们禁用 ZBuffer:

device.SetRenderState(RenderState.ZEnable, ZBufferType.DontUseZBuffer);

  1. 力场精灵在 Z=2 处绘制,启用了 ZBuffer 和 ZWrite,ZFunc 为 Less:

device.SetRenderState(RenderState.ZEnable, ZBufferType.UseZBuffer); device.SetRenderState(RenderState.ZWriteEnable, true); device.SetRenderState(RenderState.ZFunc, Compare.Less);

  1. 此时还设置了以下标志,以防止我遇到的黑边伪影:

device.SetRenderState(RenderState.AlphaTestEnable, true); device.SetRenderState(RenderState.AlphaFunc, Compare.GreaterEqual); device.SetRenderState(RenderState.AlphaRef, 55);

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

【问题讨论】:

    标签: graphics 3d directx blend alphablending


    【解决方案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 缓冲区。

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

    Edit3:你渲染力场的边缘是否有某种形式的羽化?该边缘很可能是由于 alpha 略微淡化,然后“轻微 alpha”像素被写入 z 缓冲区,因此任何后续绘制都不会覆盖它们。

    尝试以下设置

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

    要微调边缘周围的羽化,请调整 alpharef,但我怀疑您需要像上面一样保持它。

    【讨论】:

    • 力场是两个独立的对象,这就是问题所在。可能有几十个,有些重叠,有些没有。我根本没有使用 Z 缓冲,因为这只是 2D(即使它在 Direct3D 中),并且所有精灵的 Z 位置都为 0。这些力场在它们自己的批次中被发送到卡作为一个群体,作为一个群体混合在一起,但仅此而已。因此,鉴于此,我似乎应该能够以某种方式将它们组合起来。
    • 我仍然认为 Z-Buffer 方法会提供最佳性能......但我给了你一个基于 alpha 测试的替代方案:)
    • 哇,感谢您在这里提出的所有好建议! SEPERATEALPHABLENDENABLE 对我来说是新的,这似乎很有希望。但是,使用您上面的设置,我得到零绘制。然后,当我将 AlphaRef(在 SlimDX 中调用,不知道 C++ 等效项的名称)设置为 0xff 时,它显示的结果与上面的标准图像 FFActual 完全相同。不知道为什么会这样,确切地说,我不是 Direct3D 内部的专家。
    • 对于 Z 缓冲区,这也是我从未使用过的东西,我不知道它在 2D 游戏中究竟如何工作。基本上我尝试打开 Z 缓冲区(很简单),但这并没有做任何明显的事情。一切的 Z 值为 0,所以我想这是有道理的。所以我将力场的 Z 值更改为 1,这也没有改变任何东西。当谈到模板缓冲区等时,我也没有这方面的经验;周围有教程,我现在会看它们,但它们都在尝试做与我完全不同的事情。
    • 我的错误……我忘记了 D3D 不支持目标 alpha 测试。在这种情况下,您最好的选择确实是使用 Z-Buffer。基本上,您需要将 Z-Buffer 值直接传递给 Z-Buffer。它基本上就像使用适当的矩阵和适当的视口设置设置 Z 值一样简单。然后,您可以将所有内容渲染为 z = 2(例如),然后在 Z 测试上设置 D3DCMP_LESS 并启用 Z 写入,将所有 alpha 内容渲染为 z=1。
    【解决方案2】:

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

    【讨论】:

    • 谢谢——实际上我已经试过了。它似乎根本没有改变结果,这很奇怪。我也尝试过弄乱 Alpha 函数和它的参考值,但也无法得到任何结果。我很好奇 D3DBLENDOP_MAX 与 add 之间实际上没有区别,这让我想知道是否还有其他一些设置需要与它一起设置?不过,我尝试过的任何方法都没有奏效。
    • 确保设置 D3DRS_ALPHABLENDENABLE=TRUE。如果是,这应该可以工作,前提是您的设备上限支持 D3DPMISCCAPS_BLENDOP。 (一些较旧的显卡只支持混合添加)。有关详细信息,请阅读细则:msdn.microsoft.com/en-us/library/bb172599%28VS.85%29.aspx
    • 开启,我使用的是 8800GTS。我认为这可能是某种操作顺序问题——当每个图像都添加到缓冲区时,缓冲区的不透明度正在上升或类似的事情。我真的不是图形编程这方面的专家。问题是,当大约有 12 个重叠时,这些部分透明的图像形成了一个完全不透明的块。
    • 确保设置源和目标混合功能。有很多选择:msdn.microsoft.com/en-us/library/bb173417%28VS.85%29.aspx
    • 我知道——这就是我想要找出的,是要使用的选项。我已经尝试了大量的组合,但没有针对这个特定问题的结果。
    【解决方案3】:

    由于两个力场的颜色相同,因此很难从模型中准确说出您想要完成什么;你想混合颜色并限制 alpha 吗?只取其中一种颜色?

    根据上述讨论,不清楚您是否设置了所有相关的渲染状态:

    D3DRS_ALPHABLENDENABLE = TRUE(默认值:FALSE)

    D3DRS_BLENDOP = D3DBLENDOP_MAX(默认值:D3DBLENDOP_ADD)

    D3DRS_SRCBLEND = D3DBLEND_ONE(默认:D3DBLEND_ONE)

    D3DRS_DESTBLEND = D3DBLEND_ONE(默认值:D3DBLEND_ZERO)

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

    【讨论】:

    • 不幸的是,这仍然行不通。这就是我得到的:arcengames.com/share/FFZeroOne.png 这实际上是错误地将 D3DRS_DESTBLEND 设置为 D3DBLEND_ZERO,但 D3DBLEND_ONE 与它 100% 相同。
    • 哦——至于我想要完成的工作,这就是我在 Photoshop 中的做法: 1. 拍摄两张图像,并移除所有透明度(完全透明的像素除外)。 2. 将它们组合成一层,混合颜色但完全没有部分 alpha。 3. 现在将该层的全局透明度设置为(比如说)40%。结果是看起来有点混合在一起的颜色,但重叠部分的不透明度没有增加。
    猜你喜欢
    • 1970-01-01
    • 2018-02-14
    • 1970-01-01
    • 1970-01-01
    • 2018-12-12
    • 2010-09-05
    • 2014-09-09
    • 1970-01-01
    • 2013-06-05
    相关资源
    最近更新 更多