【问题标题】:Alpha Blending With Two Rendering Targets (DirectX or OpenGL)使用两个渲染目标(DirectX 或 OpenGL)进行 Alpha 混合
【发布时间】:2016-01-22 17:10:16
【问题描述】:

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

  • 创建一个名为 T 的透明纹理

  • 将名为 Q 的纹理四边形渲染到 T

  • 将 T 渲染到屏幕上

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

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

我使用这个混合方程

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

对于我将 Qs 直接渲染到屏幕,但无法定义两个在我先渲染到 T 时实现相同效果的混合方程的情况。

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

【问题讨论】:

  • 抱歉,我已经更正了我的问题,零应该是一。

标签: opengl colors directx


【解决方案1】:

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

假设您有两个透明对象,AB。你有你的纹理T,和屏幕S。而Blend 是混合方程。

渲染到T 的目的是:

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

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

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

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

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

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

glBlendFunc(GL_SRC_ALPHA, GL_ONE);

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

【讨论】:

  • 感谢您抽出宝贵时间回复。我已经设法弄清楚了。也许我问的问题很糟糕,但我认为混合方程只做线性插值的假设是不正确的。在解决方案中,我的第二个方程的形式为color = src * 1 + dst * (1 - srcAlpha),它不是线性插值。仅当混合函数 f(x) 的形式为 a * (x) + b * (1 - x) 而不是 a + b * (1 - x) 时,它才类似于 lerping。
【解决方案2】:

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

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

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

【讨论】:

    【解决方案3】:

    我已经弄清楚了,鉴于这不是我在互联网上其他地方看到的东西,我希望这对其他人有所帮助......

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

    问 -> 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 时有效地预乘了 Q 的 alpha,所以我们只需要第二阶段的 alpha 来确定渲染 T 时屏幕颜色的暗淡程度,因此 alpha 在第一个等式是:

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

    但我们只使用它来淡化第二个等式中 T 颜色后面的目标颜色(我们使用 1 表示 src 颜色,因为它是由等式 1 预乘的):

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

    【讨论】:

    • 我认为这是不对的。查看您的 Q->T alpha 方程(alpha = 1 * srcAlpha + (1 - srcAlpha) * destAlpha)。如果你要渲染两个四边形,都是 0.5 alpha,将使 T 的 alpha 为 1.0,而它应该是 0.75。
    • @Columbo,我认为你不正确。如果四边形是一个像素并且具有color {c, c, c, 0.5},则渲染到 T 的第一个四边形的 alpha 将为:alpha = 1 * srcAlpha + (1 - srcAlpha) * destAlpha 其中srcAlpha = 0.5destAlpha = 0 因为destAlpha 是 T 的 alpha,它最初为零。在顶部渲染的第二个 Q 将是:srcAlpha = 0.5,现在在 T 中 destAlpha = 0.5(来自之前在 T 中渲染的四边形),它给出了来自 alpha = 1 * srcAlpha + (1 - srcAlpha) * destAlpha0.75 的正确 alpha。我也试过了,发现它有效:-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-16
    • 1970-01-01
    • 1970-01-01
    • 2018-04-21
    • 1970-01-01
    • 2011-12-02
    相关资源
    最近更新 更多