0

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

简而言之,混合方程有两个输入:TrasparentTrasparency;前者可以是 RGBA 颜色或纹理,后者只能是浮点值。此外,Transparent可以指定两个混合方程(ColladaFxOpaqueType.AlphaOneColladaFxOpaqueType.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

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

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);
    }
}

粗略地说,上面的代码抽象了 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!!

我想要的是支持基于纹理的透明度(确实删除了那个可怕的NotSupportedException)。通常,这可以通过将纹理附加到输出的片段 alpha 分量,并像往常一样设置混合(Alpha 和 OneMinusAlpha 混合因子)来实现,但遗憾的是,上述方程无法实现这一点(不会混合 alpha 分量,不是是吗?)。

PS您可以注意到我已经使用简单的解决方案实现了混合,但基于恒定的混合颜色(blendFactors代码中的变量)(实际上使用 GL_EXT_blend_color 扩展)。如何通过使用正常的混合功能来消除这种依赖关系?我认为最后一个问题的解决方案可以帮助我了解基于纹理的混合......

4

1 回答 1

0

不太确定我理解你的目的,但我会尝试一下(请随时在评论中跟进)。

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

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

一次查看您要实现的两个方程(为简洁起见,仅使用红色和 alpha):

阿尔法一:

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 个颜色通道。对我来说,这看起来有点奇怪的混合功能,但我想不出它会用来做什么。

于 2012-08-22T05:18:17.013 回答