跳至内容

GLSL编程/GLUT/透明度

来自Wikibooks,开放世界的开放书籍

本教程涵盖了使用Blender中的GLSL着色器对片段进行混合(即合成)。它假设您熟悉前面和背面概念,如剖面教程中所述。

更具体地说,本教程是关于渲染透明物体,例如透明玻璃、塑料、织物等。(更严格地说,这些实际上是半透明物体,因为它们不需要完全透明。)透明物体允许我们透过它们看到;因此,它们的颜色与它们后面任何物体的颜色“混合”。

“OpenGL ES 2.0 管线”的描述中所述,片段着色器为每个片段计算一个RGBA颜色(即gl_FragColor中的红色、绿色、蓝色和alpha分量)(除非片段被丢弃)。然后在“逐片段操作”中处理这些片段。其中一个阶段是混合阶段,它将片段的颜色(如gl_FragColor中指定的那样),称为“源颜色”,与帧缓冲区中已有的对应像素的颜色,称为“目标颜色”(因为结果混合颜色的“目标”是帧缓冲区)结合起来。

混合是一个固定功能阶段,即您可以自定义它,但不能对其进行编程。自定义的方式是指定一个混合方程。您可以将混合方程视为对结果RGBA颜色的定义

vec4 result = SrcFactor * gl_FragColor + DstFactor * pixel_color;

其中pixel_color是当前帧缓冲区中的RGBA颜色,result是混合结果,即混合阶段的输出。SrcFactorDstFactor是可自定义的RGBA颜色(类型为vec4),它们与片段颜色和像素颜色按组件相乘。SrcFactorDstFactor的值在Blender的Python API中使用此函数指定

bge.types.KX_BlenderMaterial.setBlending({SrcFactor的代码},{DstFactor的代码})

这两个因子的可能代码在下面的表格中进行了总结(另请参见OpenGL文档

代码 结果因子(SrcFactorDstFactor
GL_ONE vec4(1.0)
GL_ZERO vec4(0.0)
GL_SRC_COLOR gl_FragColor
GL_SRC_ALPHA vec4(gl_FragColor.a)
GL_DST_COLOR pixel_color
GL_DST_ALPHA vec4(pixel_color.a)
GL_ONE_MINUS_SRC_COLOR vec4(1.0) - gl_FragColor
GL_ONE_MINUS_SRC_ALPHA vec4(1.0 - gl_FragColor.a)
GL_ONE_MINUS_DST_COLOR vec4(1.0) - pixel_color
GL_ONE_MINUS_DST_ALPHA vec4(1.0 - pixel_color.a)

“向量和矩阵运算”中所述,vec4(1.0)只是vec4(1.0, 1.0, 1.0, 1.0)的简写方式。还要注意,混合方程中所有颜色和因子的所有分量都限制在0到1之间。

Alpha混合

[编辑 | 编辑源代码]

混合方程的一个具体示例称为“alpha混合”。在OpenGL中,它是这样指定的

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

这将SrcFactor设置为vec4(gl_FragColor.a),将DstFactor设置为vec4(1.0 - gl_FragColor.a)。因此混合方程变为

vec4 result = vec4(gl_FragColor.a) * gl_FragColor + vec4(1.0 - gl_FragColor.a) * pixel_color;

这使用gl_FragColor的alpha分量作为不透明度。即,片段颜色越不透明,其不透明度和alpha分量越大,因此混合到结果中的片段颜色越多,帧缓冲区中像素的颜色越少。完全不透明的片段颜色(即alpha分量为1)将完全替换像素颜色。

此混合方程有时被称为“over”运算,即“gl_FragColor over pixel_color”,因为它对应于在像素颜色顶部放置具有特定不透明度的片段颜色的半透明层。(想象一下,在另一种颜色的东西上面放一层彩色玻璃或彩色半透明塑料。)

由于alpha混合的流行,颜色的alpha分量通常被称为不透明度,即使没有使用alpha混合。此外,请注意,在计算机图形学中,颜色透明度的常见正式定义是该颜色的1 - 不透明度

预乘Alpha混合

[编辑 | 编辑源代码]

alpha混合有一个重要的变体:有时片段的颜色已经将其alpha分量预乘到颜色分量中。(您可以将其视为已包含增值税的价格。)在这种情况下,alpha不应该再次相乘(增值税不应该再次添加),正确的混合是

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)

对应于

vec4 result = vec4(1.0) * gl_FragColor + vec4(1.0 - gl_FragColor.a) * pixel_color;

叠加混合

[编辑 | 编辑源代码]

混合方程的另一个示例是

glBlendFunc(GL_ONE, GL_ONE)

这对应于

vec4 result = vec4(1.0) * gl_FragColor + vec4(1.0) * pixel_color;

它只是将片段颜色添加到帧缓冲区中的颜色。请注意,alpha分量根本没有使用;尽管如此,此混合方程对于许多类型的透明效果非常有用;例如,当粒子系统表示火焰或其他透明并发出光的物体时,它通常用于粒子系统。在与次序无关的透明度教程中更详细地讨论了叠加混合。

着色器代码

[编辑 | 编辑源代码]

这是一个简单的着色器,它使用alpha混合和不透明度为0.3的绿色。

attribute vec3 v_coord;
uniform mat4 m, v, p;

void main()
{
  vec4 v_coord4 = vec4(v_coord, 1.0);
  mat4 mvp = p*v*m;
  gl_Position = mvp * v_coord4;
}
void main()
{
  gl_FragColor = vec4(0.0, 1.0, 0.0, 0.3); 
  // fourth component (alpha) is important: 
  // this is semitransparent green
}
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

通常,在所有不透明网格渲染完成后,应该使用禁用写入深度缓冲区的设置渲染半透明网格。这样,任何不透明物体(应该透过半透明物体发光)的片段都不会因为前面的半透明物体而深度测试失败。(深度测试在“逐片段操作”部分进行了讨论。)

您可能需要从后到前对三角形进行渲染。这是必要的,因为使用禁用写入深度缓冲区的设置渲染透明网格并不总是能解决所有问题。如果片段混合的顺序无关紧要,它可以完美地工作;例如,如果片段颜色只是使用叠加混合添加到帧缓冲区中的像素颜色,则片段混合的顺序并不重要;请参见与次序无关的透明度教程。但是,对于其他混合方程,例如alpha混合,结果将根据片段混合的顺序而有所不同。(如果您透过几乎不透明的绿色玻璃观察几乎不透明的红色玻璃,您将主要看到绿色,而如果您透过几乎不透明的红色玻璃观察几乎不透明的绿色玻璃,您将主要看到红色。同样,将几乎不透明的绿色混合到几乎不透明的红色上将与将几乎不透明的红色混合到几乎不透明的绿色上不同。)为了避免伪影,Z透明度从后到前渲染三角形。另一种方法是使用叠加混合或(预乘)alpha混合和较小的不透明度(在这种情况下,目标因子DstFactor接近1,因此alpha混合接近叠加混合)。

包含背面

[编辑 | 编辑源代码]

前面的着色器没有渲染物体的“内部”。但是,由于我们可以透过透明物体的外部看到,因此我们也应该渲染内部。如剖面教程中所述,可以通过禁用背面剔除来渲染内部。但是,如果我们只是禁用剔除,我们可能会遇到麻烦:如上所述,透明片段渲染的顺序通常很重要,但在没有任何剔除的情况下,来自内部和外部的重叠三角形可能会以随机顺序渲染,这会导致恼人的渲染伪影。因此,如上一节所述,激活三角形排序非常重要。

恭喜你完成了本教程!关于渲染透明物体的一个有趣之处在于,它不仅仅是混合的问题,还需要了解背面剔除和深度缓冲区。具体来说,我们已经了解了

  • 什么是混合以及如何在OpenGL中指定它。
  • 如何渲染包含透明和不透明物体的场景。

进一步阅读

[编辑 | 编辑源代码]

如果你还想了解更多



< GLSL编程/GLUT

除非另有说明,否则本页面上的所有示例源代码均归入公有领域。
返回OpenGL编程 - 光照章节 返回GLSL编程 - GLUT章节
华夏公益教科书