使用Vector4预乘Alpha组合

时间:2016-12-07 02:14:17

标签: c# vector graphics

我正在尝试让以下Alpha Composition formula为两个Vector4实例工作,但输出与我预期的结果不符。有人可以解决我出错的地方吗?

更新

我已经简化了代码,它看起来像我正在遵循示例公式,但结果仍然不是我所期望的。 (Photoshop使用普通混合加75%不透明度)

void Main()
{
    // Returns <0.75, 0.75, 0.75, 0.75>
    // Should be <1, 1, 1, 0.75>
    Vector4 result1 = PremultipliedLerp(Vector4.Zero, Vector4.One, .75F);
    //Vector4 result1 = PremultipliedLerp(new Vector4(1, 0, 0, 1), new Vector4(0, 1, 0, 1), .75F);
    result1.Dump();
}

/// <summary>
/// Linearly interpolates from one vector to another based on the given weighting. 
/// The two vectors are premultiplied before operating.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="amount">
/// A value between 0 and 1 indicating the weight of the second source vector.
/// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param>
/// <returns> 
/// The <see cref="Vector4"/>
/// </returns>
public static Vector4 PremultipliedLerp(Vector4 backdrop, Vector4 source, float amount)
{
    amount = amount.Clamp(0, 1);

    // Premultiply the two vectors.
    backdrop = new Vector4(backdrop.X, backdrop.Y, backdrop.Z, 1) * backdrop.W;
    source = new Vector4(source.X, source.Y, source.Z, 1) * source.W * amount;

    // This should be implementing the following formula
    // https://en.wikipedia.org/wiki/Alpha_compositing
    // Vout =  Vs + Vb (1 - Vsa)
    // Aout = Vsa + Vsb (1 - Vsa)
    Vector3 inverseW = new Vector3(1 - source.W);
    Vector3 xyzB = new Vector3(backdrop.X, backdrop.Y, backdrop.Z);
    Vector3 xyzS = new Vector3(source.X, source.Y, source.Z);
    return new Vector4(xyzS + (xyzB * inverseW), source.W + (backdrop.W * (1 - source.W)));
}

OLD VERSION

void Main()
{
    // Returns <0.75, 0.75, 0.75, 0.75>
    // Should be <1, 1, 1, 0.75>
    Vector4 result1 = PremultipliedLerp(Vector4.Zero, Vector4.One, .75F);

    result1.Dump();
}

// Define other methods and classes here
/// <summary>
/// Linearly interpolates from one vector to another based on the given weighting. 
/// The two vectors are premultiplied before operating.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="amount">
/// A value between 0 and 1 indicating the weight of the second source vector.
/// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param>
/// <returns> 
/// The <see cref="Vector4"/>
/// </returns>
public static Vector4 PremultipliedLerp(Vector4 backdrop, Vector4 source, float amount)
{
    amount = Clamp(amount, 0, 1);

    // Premultiply the two vectors. The source by the designated alpha percentage
    backdrop = backdrop * (new Vector4(backdrop.X, backdrop.Y, backdrop.Z, 1) * backdrop.W);
    source = source * (new Vector4(source.X, source.Y, source.Z, 1) * amount);

    // This should be implementing the following formula
    // https://en.wikipedia.org/wiki/Alpha_compositing
    // Vout =  Vs + Vb (1 - Vsa)
    // Aout = Vsa + Vsb (1 - Vsa)
    return new Vector4(
        BlendLerp(backdrop.X, source.X, amount),
        BlendLerp(backdrop.Y, source.Y, amount),
        BlendLerp(backdrop.Z, source.Z, amount),
        BlendLerp(backdrop.W, source.W, amount));
}

/// <summary>
/// Performs linear interpolation of the backdrop component, depending on the alpha value.
/// </summary>
/// <param name="b">The backdrop component.</param>
/// <param name="s">The source component.</param>
/// <param name="a">The alpha value.</param>
/// <returns>
/// The <see cref="float"/>.
/// </returns>
private static float BlendLerp(float b, float s, float a)
{
    return s + (b * (1 - a));
}

/// <summary>
/// Restricts a <see cref="float"/> to be within a specified range.
/// </summary>
/// <param name="value">The The value to clamp.</param>
/// <param name="min">The minimum value. If value is less than min, min will be returned.</param>
/// <param name="max">The maximum value. If value is greater than max, max will be returned.</param>
/// <returns>
/// The <see cref="float"/> representing the clamped value.
/// </returns>
public static float Clamp(float value, float min, float max)
{
    if (value > max)
    {
        return max;
    }

    if (value < min)
    {
        return min;
    }

    return value;
}

1 个答案:

答案 0 :(得分:2)

因此,当背景或源图像alpha值为零时,诀窍是提前返回。有趣的是,我发现没有一个例子提到这一点。

另请注意,当背景具有低透明度的像素时,预乘背景颜色会导致出现伪影。

/// <summary>
/// Linearly interpolates from one vector to another based on the given weighting. 
/// The two vectors are premultiplied before operating.
/// </summary>
/// <param name="backdrop">The backdrop vector.</param>
/// <param name="source">The source vector.</param>
/// <param name="amount">
/// A value between 0 and 1 indicating the weight of the second source vector.
/// At amount = 0, "from" is returned, at amount = 1, "to" is returned.
/// </param>
/// <returns> 
/// The <see cref="Vector4"/>
/// </returns>
public static Vector4 PremultipliedLerp(Vector4 backdrop, Vector4 source, float amount)
{
    amount = amount.Clamp(0, 1);

    // Santize on zero alpha
    if (Math.Abs(backdrop.W) < Epsilon)
    {
        source.W *= amount;
        return source;
    }

    if (Math.Abs(source.W) < Epsilon)
    {
        return backdrop;
    }

    // Premultiply the source vector.
    // Oddly premultiplying the background vector creates dark outlines when pixels
    // Have low alpha values. 
    source = new Vector4(source.X, source.Y, source.Z, 1) * (source.W * amount);

    // This should be implementing the following formula
    // https://en.wikipedia.org/wiki/Alpha_compositing
    // Vout =  Vs + Vb (1 - Vsa)
    // Aout = Vsa + Vsb (1 - Vsa)
    Vector3 inverseW = new Vector3(1 - source.W);
    Vector3 xyzB = new Vector3(backdrop.X, backdrop.Y, backdrop.Z);
    Vector3 xyzS = new Vector3(source.X, source.Y, source.Z);

    return new Vector4(xyzS + (xyzB * inverseW), source.W + (backdrop.W * (1 - source.W)));
}

这是使用该算法混合的两个图像的示例。

Blended Image