Alpha 与两个渲染目标(DirectX 或 OpenGL)混合

Alpha Blending With Two Rendering Targets (DirectX or OpenGL)

我正在尝试执行以下操作:

请注意,T 的 alpha 分量将为零,而 Q 的 alpha 分量可能小于一。

我需要两个 alpha 混合方程,这样如果我将 Q 的多个实例渲染到 T(使用第一个混合方程)然后渲染 T(使用第二个混合方程),这将与渲染 Q 的多个实例相同直接到屏幕。

我使用这个混合方程

color = src * srcAlpha + dst * (1 - srcAlpha)
alpha = 1 * srcAlpha + 0 * destAlpha

当我将 Qs 直接渲染到屏幕上,但当我先渲染到 T 时无法定义两个实现相同结果的混合方程。

请注意,T 的像素最初是完全透明的 (alpha = 0),因为我不希望它在未被绘制时覆盖屏幕。 Q 在其每个像素中可能具有任何透明度级别。

您所问的问题在数学上使用线性插值是不可能的。

假设您有两个透明物体,AB。您有纹理 T 和屏幕 SBlend 是混合方程。

渲染到 T 的目的是:

T = Blend(B, Blend(A, T))
S = Blend(T, S)

//Therefore
S = Blend(Blend(B, Blend(A, T)), S)

看到这里有 3 个单独的混合操作了吗?你不能用 two 混合操作来做到这一点。如果渲染 AB.

,这就是你所拥有的

线性插值都不是commutative nor associative。混合操作的顺序 很重要 。如果没有该中介,您通过写入临时中间并将其混合到最终缓冲区无法实现的效果。

存在结合和交换的混合操作。例如添加剂。但这几乎肯定不会达到您想要的视觉效果,因为它会将颜色加在一起。如果你正在做 HDR 照明,那可能是你可以使用的东西。但也有很多情况是不合适的。

加法混合将涉及以下形式的混合方程:

glBlendFunc(GL_SRC_ALPHA, GL_ONE);

使用源 alpha 确定要添加的传入颜色的百分比。

我认为这在将 Q 渲染成 T 时有效:

color = src * srcAlpha + dst * (1 - srcAlpha)
alpha = (1 - destAlpha) * srcAlpha + 1 * destAlpha

编辑:考虑一下,当您直接渲染到屏幕上时,我的回答不会导致屏幕的 Alpha 通道与您描述的 Alpha 通道相匹配。直接渲染到屏幕时的 alpha 混合模式非常不寻常。我不确定您是否真的希望屏幕的 alpha 代表您渲染的最后一个纹理的 alpha,或者您是否真的不关心屏幕的 alpha 通道看起来像什么。在大多数渲染情况下,人们并不关心后台缓冲区的 alpha 通道的状态,希望这里就是这种情况,我的回答对你有用。

我已经解决了这个问题,鉴于我在互联网上的其他地方没有看到过,我希望这对其他人有所帮助...

对于第一个混合方程,四边形 Q 渲染到 T:

Q -> T

color = src * srcAlpha + dst * (1 - srcAlpha)
alpha = 1 * srcAlpha + (1 - srcAlpha) * destAlpha

对于第二个混合方程,T 进入屏幕:

T -> S

color = src * 1 + dst * (1 - srcAlpha)
alpha = doesn't matter as screen alpha isn't used

说明

对于 Q -> T,颜色分量的方程式与正常的 alpha 混合方程式相同:

color = src * srcAlpha + dst * (1 - srcAlpha)

但是我们需要确保为第二部分设置了 alpha 通道。因为当使用这个混合方程渲染到 T 时,我们实际上是 pre-multiplying Q 的 alpha,所以我们只需要第二阶段的 alpha 来确定渲染 T 时屏幕颜色的颜色变暗多少,因此第一个等式中的 alpha 是:

alpha = 1 * srcAlpha + (1 - srcAlpha) * destAlpha

但我们只使用它来淡化第二个等式中 T 颜色后面的目标颜色(我们使用 1 作为 src 颜色,因为这是等式 1 pre-multiplied):

color = src * 1 + dst * (1 - srcAlpha)

在 DirectX 11 中,无论使用等式 1 直接渲染到屏幕,还是使用两个等式先渲染到 T 然后再渲染到屏幕,结果都相同。

DirectX 11 代码(适合喜欢真实代码的人):

第一个等式:

D3D11_BLEND_DESC blend{};

blend.AlphaToCoverageEnable = FALSE;

auto& target = blend.RenderTarget[0];

target.BlendEnable = TRUE;
target.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;

target.SrcBlend = D3D11_BLEND_SRC_ALPHA;
target.DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
target.BlendOp = D3D11_BLEND_OP_ADD;

target.SrcBlendAlpha = D3D11_BLEND_ONE;
target.DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
target.BlendOpAlpha = D3D11_BLEND_OP_ADD;

第二个方程:

D3D11_BLEND_DESC blend{};

blend.AlphaToCoverageEnable = FALSE;

auto& target = blend.RenderTarget[0];

target.BlendEnable = TRUE;
target.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;

target.SrcBlend = D3D11_BLEND_ONE;
target.DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
target.BlendOp = D3D11_BLEND_OP_ADD;

target.SrcBlendAlpha = D3D11_BLEND_ZERO;
target.DestBlendAlpha = D3D11_BLEND_ONE;
target.BlendOpAlpha = D3D11_BLEND_OP_ADD;

T 最初被清除为 {0, 0, 0, 0}。