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