COLLADA混合:基于纹理的透明度

时间:2012-08-21 22:47:08

标签: c# opengl opengl-3 collada

我现在要做的是让我的COLLADA导入器在一些透明的COLLADA模型上工作。模型混合由COLLADA specification在第7章“渲染/确定透明度”段落中指定。

简而言之,混合方程有两个输入: Trasparent Trasparency ;前者可以是RGBA颜色或纹理,后者可以只是浮点值。此外,透明可以指定两个混合方程式( ColladaFxOpaqueType.AlphaOne ColladaFxOpaqueType.RgbZero ):

以下是两个混合方程式:

// AlphaOne 
//
// result.r = fb.r * (1.0f - transparent.a * transparency) + mat.r * (transparent.a * transparency)
// result.g = fb.g * (1.0f - transparent.a * transparency) + mat.g * (transparent.a * transparency)
// result.b = fb.b * (1.0f - transparent.a * transparency) + mat.b * (transparent.a * transparency)
// result.a = fb.a * (1.0f - transparent.a * transparency) + mat.a * (transparent.a * transparency)

// RgbZero
//
// result.r = fb.r * (transparent.r * transparency) + mat.r * (1.0f -transparent.r * transparency)
// result.g = fb.g * (transparent.g * transparency) + mat.g * (1.0f -transparent.g * transparency)
// result.b = fb.b * (transparent.b * transparency) + mat.b * (1.0f -transparent.b * transparency)
// result.a = fb.a * (luminance(transparent.rgb) * transparency) + mat.a * (1.0f - luminance(transparent.rgb) * transparency)

where
- result: draw framebuffer
- fb: destination blend color
- mat: source blend color
- transparent: COLLADA parameter described above
- transparency: COLLADA parameter described above
- luminance: function to average color following ITU-R Recommendation BT.709-4

我现在实现的是在 Transparent 表示颜色(以及两个混合方程式)的情况下混合几何体。下面是实现此功能的特殊代码:

internal void CompileBlendStateParameters(ColladaShaderParameters shaderParameters, ColladaFxCommonContext commonContext)
{
    if (shaderParameters == null)
        throw new ArgumentNullException("shaderParameters");
    if (commonContext == null)
        throw new ArgumentNullException("commonContext");

    // Apply alpha blending, if required
    if ((Transparent != null) || (Transparency != null)) {
        BlendState blendState = null;
        ColorRGBAF blendFactors = new ColorRGBAF(1.0f); // No effect value
        float trasparency = 1.0f;                       // No effect value

        if (Transparency != null)
            trasparency = Transparency.GetValue(commonContext);

        if ((Transparent != null) && (Transparent.IsFixedColor(commonContext) == true)) {
            switch (Transparent.Opaque) {
                case ColladaFxOpaqueType.AlphaOne:

                    // Equation from COLLADA specification:
                    //
                    // result.r = fb.r * (1.0f - transparent.a * transparency) + mat.r * (transparent.a * transparency)
                    // result.g = fb.g * (1.0f - transparent.a * transparency) + mat.g * (transparent.a * transparency)
                    // result.b = fb.b * (1.0f - transparent.a * transparency) + mat.b * (transparent.a * transparency)
                    // result.a = fb.a * (1.0f - transparent.a * transparency) + mat.a * (transparent.a * transparency)

                    // Determine blend factor constant color
                    blendFactors = new ColorRGBAF(Transparent.GetFixedColor(commonContext).Alpha);
                    // Modulate constant color
                    blendFactors = blendFactors * trasparency;
                    // Create blend state
                    blendState = new BlendState(BlendState.BlendEquation.Add, BlendState.BlendFactor.ConstColor, BlendState.BlendFactor.ConstColorComplement, blendFactors);
                    break;
                case ColladaFxOpaqueType.RgbZero:

                    // Equation from COLLADA specification:
                    //
                    // result.r = fb.r * (transparent.r * transparency) + mat.r * (1.0f -transparent.r * transparency)
                    // result.g = fb.g * (transparent.g * transparency) + mat.g * (1.0f -transparent.g * transparency)
                    // result.b = fb.b * (transparent.b * transparency) + mat.b * (1.0f -transparent.b * transparency)
                    // result.a = fb.a * (luminance(transparent.rgb) * transparency) + mat.a * (1.0f - luminance(transparent.rgb) * transparency)

                    // Determine blend factor constant color
                    blendFactors = new ColorRGBAF(Transparent.GetFixedColor(commonContext));
                    // Define alpha blend factor as luminance
                    blendFactors.Alpha = blendFactors.Red * 0.212671f + blendFactors.Green * 0.715160f + blendFactors.Blue * 0.072169f;
                    // Modulate constant color
                    blendFactors = blendFactors * trasparency;
                    // Create blend state
                    blendState = new BlendState(BlendState.BlendEquation.Add, BlendState.BlendFactor.ConstColorComplement, BlendState.BlendFactor.ConstColor, blendFactors);
                    break;
            }
        } else if ((Transparent != null) && (Transparent.IsTextureColor(commonContext) == true)) {

            throw new NotSupportedException();

        } else {
            // Modulate constant color
            blendFactors = blendFactors * trasparency;
            // Create blend state
            blendState = new BlendState(BlendState.BlendEquation.Add, BlendState.BlendFactor.ConstColor, BlendState.BlendFactor.ConstColorComplement, blendFactors);
        }

        if (blendState != null)
            shaderParameters.RenderState.DefineState(blendState);
    }
}

Rougly,上面的代码抽象了OpenGL层,相当于:

 // AlphaOne equation
 glEnable(GL_BLEND);
 glBlendEquation(GL_FUNC_ADD);
 glBlendFunc(GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR);
 glBlendColor(blendFactors.Red, blendFactors.Green, blendFactors.Blue, blendFactors.Alpha);

 // RgbZero equation
 glEnable(GL_BLEND);
 glBlendEquation(GL_FUNC_ADD);
 glBlendFunc(GL_ONE_MINUS_CONSTANT_COLOR, GL_CONSTANT_COLOR);
 glBlendColor(blendFactors.Red, blendFactors.Green, blendFactors.Blue, blendFactors.Alpha);

 // Having calculated blendFactor appropriately!!

我想要的是支持基于纹理的trasparencies(确实删除那些可怕的 NotSupportedException )。通常这可以通过将纹理附加到输出的片段alpha分量,并像往常一样设置混合(Alpha和OneMinusAlpha混合因子)来实现,但遗憾的是,这不可能用上述方程式(alpha组件不能混合,isn'是吗?)。

P.S。您可以注意到我已经使用简单的解决方案实现了混合,但是基于常量混合颜色(代码中的blendFactors变量)(实际上使用了GL_EXT_blend_color扩展)。如何使用常规混合函数删除此依赖项?我认为最后一个问题的解决方案可以帮助我进行基于纹理的混合......

1 个答案:

答案 0 :(得分:0)

我不是相当我确定我明白你的目标,但我会抓住它(随意在评论中跟进)。

您希望使用标准opengl混合实现AlphaOne和RgbZero等式,而不是使用常量颜色,您希望为图像的每个纹素评估混合函数。透明度的典型混合函数(SRC_ALPHA,ONE_MINUS_SRC_ALPHA)使用传入片段的alpha值,并计算为:

result = dst * (1-src_alpha) + src * src_alpha

一次查看一个你要实现的两个方程式(为了简洁,只需要红色和alpha):

AlphaOne:

result.r = fb.r * (1.0f - transparent.a * transparency) + mat.r * (transparent.a * transparency);
result.a = fb.a * (1.0f - transparent.a * transparency) + mat.a * (transparent.a * transparency);

如果我们看一下这个等式,我们看到它看起来与发布的初始等式非常相似。我们所要做的就是将transparent.a*transparency替换为src_alpha

这意味着如果你使用像素着色器,值transparent.a来自纹理样本,transparency作为一个统一浮点数,它将实现AlphaOne函数:

sampler2D tex;
uniform transparency;

main() {
   vec4 texel = texture2D(tex,uv);
   vec3 out_rgb = texel.rgb;
   float out_alpha = texel.a * transparency;
   gl_FragColor = vec4(out_rgb, out_alpha);
}

此着色器提交transparent.a * transparency作为要在混合方程中使用的src_alpha值。

我相信这表明您可以使用典型的opengl混合轻松实现此算法。

然而RGBZero功能对我来说看起来更难,我不相信有任何混合功能可以实现这一点。我只有一个奇怪的想法是一次绘制一个四个颜色通道(锁定G,B,A进行编辑,只绘制R,输出alpha作为R混合因子,然后重复其他3个颜色通道。这对我来说看起来有些奇怪的混合功能,我想不出它会用到什么。