GLSL 编程/Blender/着色器调试
本教程介绍了 **属性变量**。它建立在 关于最小着色器的教程 和 关于变化变量的 RGB 立方体教程 之上。
本教程还介绍了在 Blender 中调试着色器的主要技术:伪彩色图像,即通过将片段颜色的某个分量设置为该值来可视化一个值。然后,结果图像中该颜色分量的强度可以让您对着色器中的值做出推断。这看起来可能是一个非常原始的调试技术,因为它确实是。不幸的是,Blender 中没有其他选择。
在 RGB 立方体教程 中,您已经看到了片段着色器如何通过变化变量从顶点着色器获取数据。这里的问题是:顶点着色器从哪里获取数据?在 Blender 中,此数据由 **属性窗口** 中的设置(特别是 **对象数据选项卡**、**材质选项卡** 和 **纹理选项卡** 中的设置)为每个选定对象指定。对象的网格的所有数据都在每一帧发送到 OpenGL。(这通常称为“绘制调用”。请注意,每次绘制调用都有一些性能开销;因此,将一个大型网格发送到 OpenGL 比使用多个绘制调用发送多个较小的网格效率更高。)此数据通常包含一个三角形列表,每个三角形由三个顶点定义,每个顶点都具有某些属性,包括位置。这些属性通过属性变量在顶点着色器中可用。
在 Blender 中,大多数标准属性(位置、颜色、表面法线和纹理坐标)都是内置的,即您不需要(事实上也不应该)定义它们。这些内置属性的名称实际上是由 OpenGL“兼容性配置文件”定义的,因为如果将为固定功能管道编写的 OpenGL 应用程序与(可编程的)顶点着色器混合,则需要这样的内置名称。如果您必须定义它们,定义(仅在顶点着色器中)将如下所示
attribute vec4 gl_Vertex; // position (in object coordinates,
// i.e. local or model coordinates)
attribute vec4 gl_Color; // color (usually constant)
attribute vec3 gl_Normal; // surface normal vector
// (usually normalized; also in object coordinates)
attribute vec4 gl_MultiTexCoord0; //0th set of texture coordinates
// (a.k.a. “UV”; between 0 and 1)
attribute vec4 gl_MultiTexCoord1; //1st set of texture coordinates
// (a.k.a. “UV”; between 0 and 1)
...
只有一个属性变量是由 Blender 提供但没有 OpenGL 中的标准名称,即切线向量,即一个与表面法线正交的向量。您应该将此变量自己定义为类型为 vec4
的属性变量,名称为 tangent
,如下面的着色器所示
import bge
cont = bge.logic.getCurrentController()
VertexShader = """
varying vec4 color;
attribute vec4 tangent; // this attribute is specific to Blender
// and has to be defined explicitly
void main()
{
color = gl_MultiTexCoord0; // set the varying to this attribute
// other possibilities to play with:
// color = gl_Vertex;
// color = gl_Color;
// color = vec4(gl_Normal, 1.0);
// color = gl_MultiTexCoord0;
// color = gl_MultiTexCoord1;
// color = tangent;
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
"""
FragmentShader = """
varying vec4 color;
void main()
{
gl_FragColor = color;
}
"""
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)
shader.setAttrib(bge.logic.SHD_TANGENT)
请注意以下行
shader.setAttrib(bge.logic.SHD_TANGENT)
在 Python 脚本中,它告诉 Blender 为着色器提供切线属性。但是,Blender 仅会在 **属性窗口** 中的某些设置下提供几个属性,特别是应在 **对象数据选项卡** 中指定 **UV 贴图**(只需单击“+”按钮),应在 **材质选项卡** 中定义材质,应在 **纹理选项卡** 中定义纹理(例如,任何图像)。
在 RGB 立方体教程 中,我们已经看到了如何通过将片段颜色设置为这些值来可视化 gl_Vertex
坐标。在本例中,片段颜色设置为 gl_MultiTexCoord0
,以便我们可以看到 Blender 为 **属性窗口** 中的某些设置提供的纹理坐标类型。
在尝试理解伪彩色图像中的信息时,重要的是只关注一个颜色分量。例如,如果属性 gl_MultiTexCoord0
被写入片段颜色,那么片段的红色分量会可视化 gl_MultiTexCoord0
的 x
坐标,即输出颜色是最大纯红色还是最大黄色或最大洋红色并不重要,在所有情况下,红色分量都是 1。另一方面,对于红色分量来说,颜色是蓝色、绿色还是青色也不重要,因为在所有情况下红色分量都是 0。如果您从未学会只关注一个颜色分量,这可能很困难;因此,您可能考虑一次只查看一个颜色分量。例如,使用此行在顶点着色器中设置变化量
color = vec4(gl_MultiTexCoord0.x, 0.0, 0.0, 1.0);
这将变化变量的红色分量设置为 gl_MultiTexCoord0
的 x
分量,但将绿色和蓝色分量设置为 0(以及 alpha 或不透明度分量设置为 1,但这在该着色器中并不重要)。
Blender 发送到顶点着色器的特定纹理坐标取决于 **对象数据选项卡** 中指定的 **UV 贴图** 和 **纹理选项卡** 中指定的 **映射**。
纹理坐标特别容易可视化,因为它们像颜色分量一样都在 0 到 1 之间。几乎同样容易的是归一化向量的坐标(即长度为 1 的向量;例如,gl_Normal
通常是归一化的),因为它们始终介于 -1 和 +1 之间。要将此范围映射到 0 到 1 的范围,您需要将 1 加到每个分量,并将所有分量除以 2,例如
color = vec4((gl_Normal + vec3(1.0, 1.0, 1.0)) / 2.0, 1.0);
请注意,gl_Normal
是一个三维向量。黑色对应于坐标 -1,一个分量的全强度对应于坐标 +1。
如果您要可视化的值不在 0 到 1 或 -1 到 +1 的范围内,则必须将其映射到 0 到 1 的范围内,这是颜色分量的范围。如果您不知道预期哪些值,您只需要尝试一下。这里有所帮助的是,如果您将颜色分量指定为不在 0 到 1 的范围内,它们会自动被钳制到此范围内。即,小于 0 的值将被设置为 0,大于 1 的值将被设置为 1。因此,当颜色分量为 0 或 1 时,您至少知道该值小于或大于您假设的值,然后您可以迭代地调整映射,直到颜色分量在 0 到 1 之间。
为了练习着色器的调试,本节包含一些在顶点着色器中将对 color
的赋值替换为它们中的每一个时会产生黑色的行。您的任务是找出每行的结果为什么是黑色的。为此,您应该尝试可视化您不确定的任何值,并将小于 0 或大于 1 的值映射到其他范围内,以便这些值可见,您至少对它们所在的范围有一个概念。请注意,大多数函数和运算符都记录在 “向量和矩阵运算” 中。
color = gl_MultiTexCoord0 - vec4(1.5, 2.3, 1.1, 0.0);
color = vec4(1.0 - gl_MultiTexCoord0.w);
color = gl_MultiTexCoord0 / tan(0.0);
以下行仅适用于球体,需要一些关于点积和叉积的知识
color = dot(gl_Normal, vec3(tangent)) * gl_MultiTexCoord0;
color = dot(cross(gl_Normal, vec3(tangent)), gl_Normal) *
gl_MultiTexCoord0;
color = vec4(cross(gl_Normal, gl_Normal), 1.0);
color = vec4(cross(gl_Normal, gl_Vertex), 1.0);
函数 radians()
是否总是返回黑色?它有什么用?
color = radians(gl_MultiTexCoord0);
请查阅 “Khronos OpenGL ES API 注册表” 中提供的“OpenGL ES 着色语言 1.0.17 规范”中的文档,以了解 radians()
的用途。
属性特定于顶点,即它们通常对不同的顶点具有不同的值。片段着色器也有类似的变量,即对每个片段具有不同值的变量。但是,它们不同于属性,因为它们不是由网格(即三角形列表)指定的。它们也不同于变化变量,因为它们不是由顶点着色器显式设置的。
具体来说,一个四维向量 gl_FragCoord
可用,它包含正在处理的片段的屏幕(或:窗口)坐标 ;有关屏幕坐标系的描述,请参见 “顶点变换”。
此外,提供了一个布尔变量gl_FrontFacing
,用于指定正在渲染三角形的正面还是背面。正面通常朝向模型的“外部”,而背面朝向模型的“内部”;但是,如果模型不是闭合曲面,则没有明显的外部或内部。通常,表面法线向量指向正面的方向,但这不是必需的。实际上,正面和背面是由顶点三角形的顺序指定的:如果顶点以逆时针顺序出现,则正面可见;如果它们以顺时针顺序出现,则背面可见。在切割教程中展示了一个应用程序。
恭喜你,你已经完成了本教程!我们已经看到了
- Blender 中的内置属性列表:
gl_Vertex
、gl_Color
、gl_Normal
、gl_MultiTexCoord0
、gl_MultiTexCoord1
以及特殊的tangent
。 - 如何通过设置输出片段颜色的组件来可视化这些属性(或任何其他值)。
- 片段程序中可用的两个额外特殊变量:
gl_FragCoord
和gl_FrontFacing
。
如果你还想了解更多
- 关于顶点和片段着色器中的数据流,你应该阅读对“OpenGL ES 2.0 管道”的描述。
- 关于向量运算和函数,你应该阅读“向量和矩阵运算”。