GLSL 编程/Blender/透明度
本教程介绍了使用 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
是混合的结果,即混合阶段的输出。SrcFactor
和DstFactor
是可自定义的 RGBA 颜色(类型为vec4
),它们与片段颜色和像素颜色进行逐组件相乘。SrcFactor
和DstFactor
的值是在 Blender 的 Python API 中使用以下函数指定的
bge.types.KX_BlenderMaterial.setBlending(
{SrcFactor
的代码},{DstFactor
的代码})
两个因子的可能代码在以下表格中总结(另请参见Blender 文档)
代码 | 结果因子(SrcFactor 或DstFactor ) |
---|---|
bge.logic.BL_ONE |
vec4(1.0)
|
bge.logic.BL_ZERO |
vec4(0.0)
|
bge.logic.BL_SRC_COLOR |
gl_FragColor
|
bge.logic.BL_SRC_ALPHA |
vec4(gl_FragColor.a)
|
bge.logic.BL_DST_COLOR |
pixel_color
|
bge.logic.BL_DST_ALPHA |
vec4(pixel_color.a)
|
bge.logic.BL_ONE_MINUS_SRC_COLOR |
vec4(1.0) - gl_FragColor
|
bge.logic.BL_ONE_MINUS_SRC_ALPHA |
vec4(1.0 - gl_FragColor.a)
|
bge.logic.BL_ONE_MINUS_DST_COLOR |
vec4(1.0) - pixel_color
|
bge.logic.BL_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 混合”。在 Blender 中,它是这样指定的
mat.setBlending(bge.logic.BL_SRC_ALPHA, bge.logic.BL_ONE_MINUS_SRC_ALPHA)
mat
是一个类型为bge.types.KX_BlenderMaterial
的材质(请参见Blender 文档;有关完整代码,请参见下文)。这将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 不应该再次乘以(增值税不应该再次添加),正确的混合是
mat.setBlending(bge.logic.BL_ONE, bge.logic.BL_ONE_MINUS_SRC_ALPHA)
这对应于
vec4 result = vec4(1.0) * gl_FragColor + vec4(1.0 - gl_FragColor.a) * pixel_color;
混合方程的另一个示例是
mat.setBlending(bge.logic.BL_ONE, bge.logic.BL_ONE)
这对应于
vec4 result = vec4(1.0) * gl_FragColor + vec4(1.0) * pixel_color;
这只是将片段颜色添加到帧缓冲区中的颜色。注意,alpha 分量根本没有使用;尽管如此,这个混合方程对许多种透明效果非常有用;例如,它通常用于粒子系统,当它们表示火焰或其他透明且发光的物体时。在关于顺序无关透明度的教程中更详细地讨论了叠加混合。
这是一个简单的着色器,它使用 alpha 混合,颜色为绿色,不透明度为 0.3。请注意,您必须将视窗着色设置为纹理,在3D 视图菜单中才能使混合生效。
import bge
cont = bge.logic.getCurrentController()
VertexShader = """
void main()
{
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
"""
FragmentShader = """
void main()
{
gl_FragColor = vec4(0.0, 1.0, 0.0, 0.3);
// fourth component (alpha) is important:
// this is semitransparent green
}
"""
mesh = cont.owner.meshes[0]
for mat in mesh.materials:
shader = mat.getShader()
if shader != None:
if not shader.isValid():
shader.setSource(VertexShader, FragmentShader, 1)
mat.setBlending(bge.logic.BL_SRC_ALPHA,
bge.logic.BL_ONE_MINUS_SRC_ALPHA)
通常,半透明网格应该在所有不透明网格渲染后使用停用的深度缓冲区写入进行渲染。这样,不透明物体(应透过半透明物体发出光)的任何片段都不会因为其前面的半透明物体而导致深度测试失败。(深度测试在关于“每个片段操作”部分中讨论。)为了在 Blender 中激活此技术,您必须在3D 视图中选择该物体,然后转到属性窗口并选择材质选项卡。如果您还没有,请创建一个新材质。在材质选项卡中,选中透明度并选择Z 透明度。
此设置还应按从后到前的顺序渲染三角形。这是必要的,因为使用禁用的深度缓冲区写入渲染透明网格并不总是能解决所有问题。如果片段混合的顺序无关紧要,它会完美运行;例如,如果片段颜色只是用加性混合添加到帧缓冲区中的像素颜色,则片段混合的顺序并不重要;参见关于顺序无关透明度的教程。但是,对于其他混合方程式,例如 alpha 混合,结果将根据片段混合的顺序而不同。(如果你透过几乎不透明的绿色玻璃看几乎不透明的红色玻璃,你主要会看到绿色,而如果你透过几乎不透明的红色玻璃看几乎不透明的绿色玻璃,你主要会看到红色。类似地,将几乎不透明的绿色颜色混合在几乎不透明的红色颜色之上,将不同于将几乎不透明的红色颜色混合在几乎不透明的绿色颜色之上。)为了避免出现伪影,**Z 透明度**按从后到前的顺序渲染三角形。另一种方法是使用加性混合或(预乘)alpha 混合,混合度较小(在这种情况下,目标因子DstFactor
接近 1,因此 alpha 混合接近加性混合)。
前面的着色器没有渲染物体的“内部”。但是,由于我们可以透过透明物体的外部看到内部,因此我们也应该渲染内部。如关于切口的教程中所述,可以通过禁用背面剔除来渲染内部。但是,如果我们只是禁用剔除,我们可能会遇到麻烦:如上所述,透明片段渲染的顺序通常很重要,但是没有任何剔除,来自内部和外部的重叠三角形可能会以随机顺序渲染,这会导致令人讨厌的渲染伪影。因此,通过选择**Z 透明度**(如上一节所述)来激活三角形排序非常重要。
恭喜,您完成了本教程!关于渲染透明物体的一件有趣的事情是,它不仅仅是关于混合,还要求了解背面剔除和深度缓冲区。具体来说,我们已经研究了
- 什么是混合以及如何在 Blender 中指定它。
- 带有透明和不透明物体的场景是如何渲染的。
如果你还想了解更多
- OpenGL 管道,您应该阅读“OpenGL ES 2.0 管道”的描述。
- 关于 OpenGL 管道中的每个片段操作(例如混合和深度测试),您应该阅读“每个片段操作”。
- 关于背面剔除,您应该阅读关于切口的教程。
- 关于如何在 Blender 中指定混合,您将在此 Blender 类中找到该函数,并在此页面上找到常量。