【问题标题】:Pixels Overlay With transparency像素叠加透明
【发布时间】:2017-08-18 08:05:52
【问题描述】:

我有 2 个 B8G8R8A8 (32) 格式的像素。 两个像素(顶部和底部)都具有透明度(Alpha 通道

在底部覆盖顶部像素的方式(公式)是什么? (不使用第 3 方)。

我试图做这样的事情

struct FColor
{
public:
    // Variables.
#if PLATFORM_LITTLE_ENDIAN
    #ifdef _MSC_VER
        // Win32 x86
        union { struct{ uint8 B,G,R,A; }; uint32 AlignmentDummy; };
    #else
        // Linux x86, etc
        uint8 B GCC_ALIGN(4);
        uint8 G,R,A;
    #endif
#else // PLATFORM_LITTLE_ENDIAN
    union { struct{ uint8 A,R,G,B; }; uint32 AlignmentDummy; };
#endif
//...
};

FORCEINLINE FColor AlphaBlendColors(FColor pixel1, FColor pixel2)
{
    FColor blendedColor;
    //Calculate new Alpha:
    uint8 newAlpha = 0;
    newAlpha = pixel1.A + pixel2.A * (255 - pixel1.A);

    //get FColor as uint32
    uint32 colora = pixel1.DWColor();
    uint32 colorb = pixel2.DWColor();

    uint32 rb1 = ((0x100 - newAlpha) * (colora & 0xFF00FF)) >> 8;
    uint32 rb2 = (newAlpha * (colorb & 0xFF00FF)) >> 8;
    uint32 g1 = ((0x100 - newAlpha) * (colora & 0x00FF00)) >> 8;
    uint32 g2 = (newAlpha * (colorb & 0x00FF00)) >> 8;
    blendedColor = FColor(((rb1 | rb2) & 0xFF00FF) + ((g1 | g2) & 0x00FF00));


    blendedColor.A = newAlpha;
    return blendedColor;
}

但结果远不是我想要的:-)

我寻找了一些 Alpha 混合公式(我一直不明白如何计算叠加层的新 Alpha)-> 也许我走错了方向?

编辑:

newAlpha 更改为newAlpha = FMath::Min(pixel1.A + pixel2.A, 255); 实际上给出了更好的结果,但是这样计算是否正确?我在这里错过了什么吗?

基于已接受答案的工作示例)

   FORCEINLINE FColor AlphaBlendColors(FColor BottomPixel, FColor TopPixel)
{
    FColor blendedColor;
    //Calculate new Alpha:
    float normA1 = 0.003921568627451f * (TopPixel.A);
    float normA2 = 0.003921568627451f * (BottomPixel.A);

    uint8 newAlpha = (uint8)((normA1 + normA2 * (1.0f - normA1)) * 255.0f);

    if (newAlpha == 0)
    {
        return FColor(0,0,0,0);
    }

    //Going By Straight Alpha formula
    float dstCoef = normA2 * (1.0f - normA1);
    float multiplier = 255.0f / float(newAlpha);

    blendedColor.R = (uint8)((TopPixel.R * normA1 + BottomPixel.R * dstCoef) * multiplier);
    blendedColor.G = (uint8)((TopPixel.G * normA1 + BottomPixel.G * dstCoef) * multiplier);
    blendedColor.B = (uint8)((TopPixel.B * normA1 + BottomPixel.B * dstCoef) * multiplier);

    blendedColor.A = newAlpha;

    return blendedColor;
}

【问题讨论】:

  • 您是否尝试过使用调试器来解决您的问题?从第一次检查来看,newAlpha = pixel1.A + pixel2.A * (255 - pixel1.A); 行看起来不可靠(例如,alpha 100 和 255 会给你newAlpha = 100 + 255 * (255 - 100) = 39625
  • @ZdeněkJelínek 不,抱歉,我没有尝试调试值(实际上它不仅是 2 像素 - 我正在尝试混合重纹理),是的,你是对的(我试过傻了采用float alpha计算并忘记溢出)
  • 如果您只能处理繁重的工作负载,请考虑编写一个单元测试,然后进行调试,该测试将测试足够简单的数据以重现问题。

标签: c++ image-processing overlay


【解决方案1】:

首先假设下面有第三个像素恰好是不透明的。

对于进一步的符号,我将假设 alpha 值在 [0,1] 中。

给定:三个像素,第一个在顶部,颜色 c_1、c_2、c_3,alpha 值 a_1、a_2、a_3 = 1

那么得到的alpha值显然是1,颜色是

(a_1)*c_1 + (1-a_1)(*a_2)*c_2 + (1-a_1)*(1-a_2)*c_3

现在,我们要找到一些值 c_k,a_k 使上面的公式等于

(a_k)*c_k + (1-a_k)*c_3

我们可以分两步解决这个问题:

(1-a_k) = (1-a_1)*(1-a_2)
->
a_k = 1-(1-a_1)*(1-a_2)

(a_k)*c_k = (a_1)*c_1 + (1-a_1)(*a_2)*c_2
->
c_k = [(a_1)*c_1 + (1-a_1)(*a_2)*c_2] / a_k

使用这些公式(alpha 值的范围不同),您就可以得到所需的颜色。

(别忘了抓住 a_k = 0)

编辑:第三个像素的解释:

当你以任何方式使用你的两个像素时,即做某事导致它被用来显示某些东西时,它们将被放置在其他一些不透明的现有颜色上。例如,这可能是背景颜色,但也可能是在某些背景颜色上应用更多透明像素的结果。

我现在将您的两种颜色组合在一起的方法是找到一种与这两种颜色一样的颜色。也就是说,将它放在一些不透明的颜色上应该与将原始的两种颜色放在它上面的结果相同。这就是我对新颜色的要求,从而产生了我使用的公式。

公式只不过是在第三个颜色上连续应用两种颜色的结果。

【讨论】:

  • 嘿,emm 我没明白第三个像素的意思,你能补充一些理论解释吗?
  • 这开始变得有趣了——我还没有完全得到第三个:-(我实际上并不打算渲染这些像素——我想合并 2 个纹理(它们都有渐变 alpha 通道)在 1 个纹理中剔除主线程上的像素过度绘制。还是第三个像素仍然重要。
  • 我想我明天试试,暂时累了。
  • 同样,这只不过是创建一种颜色,其行为类似于其他两种颜色的组合。我通过与其他颜色的交互来定义行为。我只是简单地陈述了一个公式,它将创建一个透明的彩色箔,当你看它时,它会产生与其他两个箔组合时相同的效果。我只是使用了它们的组合公式和新箔的公式,它的值未知,然后将这些公式等同起来。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-15
  • 2015-04-21
  • 2014-10-24
  • 1970-01-01
  • 2014-08-07
  • 1970-01-01
相关资源
最近更新 更多