我现在要做的是让我的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扩展)。如何使用常规混合函数删除此依赖项?我认为最后一个问题的解决方案可以帮助我进行基于纹理的混合......
答案 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个颜色通道。这对我来说看起来有些奇怪的混合功能,我想不出它会用到什么。