0

What I'm trying to do now is to get my COLLADA importer working on some transparent COLLADA model. The model blending is specified by the COLLADA specification, at charapter 7, paragraph "Rendering/Determining Transparency".

In short, there are two inputs for blend equation: Trasparent and Trasparency; the former can be a RGBA color or a texture, and the latter can be only a floating-point value. Additionally, Transparent can specify two blending equations (ColladaFxOpaqueType.AlphaOne and ColladaFxOpaqueType.RgbZero):

Here are the two blending equations:

// 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

What I've implemented now, is to get the geometry blended in the case Transparent represent a color (and both blending equations). Below there is the peculiar code implementing this feature:

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, the code above abstracts the OpenGL layer, being equivalent to:

 // 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!!

What I'd like is to support trasparencies based on texture (indeed removing that horrible NotSupportedException). Normally this would be implemented by attaching a texture to the outputted fragment alpha component, and setup blending as usual (Alpha and OneMinusAlpha blend factors), but sadly this is not possible with the above equations (alpha component wouln't be blended, isn't it?).

P.S. You can note that I've implemented blending by using a straightforward solution, but based on constant blend color (blendFactors variable in code) (indeed using GL_EXT_blend_color extension). How can I remove this dependency by using normal blending functions? I think that the solution to the last question could help me about blending based on texture...

Luca
  • 11,646
  • 11
  • 70
  • 125

1 Answers1

0

I'm not quite sure I understand what you're going for, but I'll take a stab at it (feel free to follow up in the comments).

You want to implement the AlphaOne and RgbZero equations with standard opengl blending, and instead of using a constant color, you want your blend function to be evaluated for each texel of an image. The typical blend function for transparency (SRC_ALPHA, ONE_MINUS_SRC_ALPHA) uses the alpha value of the incoming fragment, and is evaluated as:

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

Looking one at a time at the two equations you want to implement (just red and alpha for brevity):

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

If we look at this equation, we see that it looks very similar to the initial equation posted. All we have to do is substitute transparent.a*transparency for src_alpha.

This means if you take a pixel shader, with the value of transparent.a coming from a texture sample, and transparency as a uniform float, it will implement the AlphaOne function:

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

This shader submits transparent.a * transparency as the src_alpha value to be used in the blend equation.

I believe that this shows that you can easily implement this algorithm with typical opengl blending.

However the RGBZero function looks tougher to me, I don't believe there is any blending function that will achieve that. Just one oddball idea I have would be to draw the four color channels one at a time (lock G,B,A for editing and just draw R, with output alpha as your R blend factor, then repeat for the other 3 color channels. That looks like somewhat of an odd blending function to me, I can't think of what it would be used for though.

Tim
  • 35,413
  • 11
  • 95
  • 121
  • I think that the question cannot be resolved without using multipass rendering, even for AlphaOne blending: your solution overwrite the 'mat.a' information (that comes from previous computation). The solution would be a 2-pass blending: the first for RGB values, the second for only alpha value (rendering occour by masking color buffer). The RGBZero blending is applicable by using GL_blend_equation_separate extension, and applying a different alpha blending function. – Luca Aug 22 '12 at 14:20