着色器脚本

着色器脚本整体功能

这个脚本的总体目标是:接收一个带有纹理坐标的 2D 几何体(比如一个正方形),并将一个 2D 纹理(图片)“贴”到这个几何体上进行渲染。


第一部分:顶点着色器 (Vertex Shader)

1
2
3
4
5
6
7
8
9
10
11
12
#shader vertex
#version 330 core
layout(location = 0) in vec4 position;
layout(location = 1) in vec2 texCoord;

out vec2 v_TexCoord;

void main()
{
gl_Position = position;
v_TexCoord = texCoord;
};

# "#shader vertex"

  • 这是一个自定义的标识符(不是标准的 GLSL),用于在 C++ 代码中解析和分离顶点着色器与片段着色器的源码。

# version 330 core

  • 含义: 声明此着色器代码遵循 OpenGL 3.3 版本的核心模式 (Core Profile) 规范。这是现代 OpenGL 的标准做法。

# layout(location = 0) in vec4 position;

  • in vec4 position: 定义一个名为 position输入变量。它是一个四维向量 (vec4),用于接收每个顶点的位置坐标 (x, y, z, w)。
  • layout(location = 0): 将这个输入变量绑定到顶点属性的**第 0 个插槽 (location)**。这使得 C++ 代码可以通过 glVertexAttribPointer(0, ...) 直接将 VBO 中的数据喂给它。

# layout(location = 1) in vec2 texCoord;

  • in vec2 texCoord: 定义另一个名为 texCoord 的输入变量。它是一个二维向量 (vec2),用于接收每个顶点的纹理坐标 (通常称为 u, v 或 s, t)。
  • layout(location = 1): 将其绑定到顶点属性的第 1 个插槽,对应 C++ 中的 glVertexAttribPointer(1, ...)

# out vec2 v_TexCoord;

  • out vec2 v_TexCoord: 定义一个名为 v_TexCoord输出变量
  • 作用: 这是顶点着色器与片段着色器之间传递数据的桥梁。这个变量的值将被发送到渲染管线的下一个阶段(光栅化),并为每个片段进行插值。变量名前的 v_ (vertex) 是一个常见的命名约定,表示这个变量来自顶点着色器。

# void main() { ... }

  • 着色器的入口函数,对每个顶点执行一次。
  • gl_Position = position;:
    • gl_Position 是一个内置的、必须被赋值的特殊输出变量。
    • 它定义了顶点在裁剪空间 (Clip Space) 中的最终位置。
    • 这里我们直接将输入的 position 赋值给它,意味着不对顶点做任何变换(如模型、视图、投影变换)。这通常用于渲染一个简单的、直接映射到屏幕空间的 2D 图形。
  • v_TexCoord = texCoord;:
    • 将从 VBO 输入的 texCoord 原封不动地传递给输出变量 v_TexCoord。这个值将用于后续的插值。

第二部分:片段着色器 (Fragment Shader)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#shader fragment
#version 330 core

layout(location = 0) out vec4 color;

in vec2 v_TexCoord;

uniform vec4 u_Color;
uniform sampler2D u_Texture;

void main()
{
vec4 texColor = texture(u_Texture, v_TexCoord);
color = texColor;
};

# "#shader fragment"

  • 自定义的解析标识符。

# version 330 core

  • 与顶点着色器版本保持一致。

# layout(location = 0) out vec4 color;

  • out vec4 color: 定义一个名为 color输出变量。它的值将是这个片段(像素)的最终颜色。
  • layout(location = 0): 将这个输出写入到帧缓冲区的第 0 个颜色附件,这通常就是你的屏幕。

# in vec2 v_TexCoord;

  • in vec2 v_TexCoord: 定义一个输入变量
  • 关键点: 这个变量的名字 v_TexCoord 必须与顶点着色器中对应的 out 变量的名字完全匹配
  • 它的值是什么? 它的值是光栅化阶段根据三角形三个顶点的 v_TexCoord 输出值插值计算得出的,精确地代表了当前这个片段在纹理上的采样位置。

# uniform vec4 u_Color;

  • uniform: 定义一个全局统一变量。它的值由 C++ 代码在绘制调用之前设置,并且对于处理同一批次的所有顶点/片段来说,它的值是恒定不变的
  • u_Color 在当前代码中没有被使用,但它展示了如何从 CPU 传递一个统一的颜色值。

# uniform sampler2D u_Texture;

  • sampler2D: 这是 GLSL 中一个特殊的不透明类型,专门用来表示一个 2D 纹理的采样器
  • u_Texture: 它的值也是由 C++ 代码设置的(通过 glUniform1i),这个值是一个整数,代表要从哪个纹理单元 (Texture Unit) 进行采样。

# void main() { ... }

  • 片段着色器的入口函数,对每个被几何体覆盖的片段执行一次。
  • vec4 texColor = texture(u_Texture, v_TexCoord);:
    • texture(...): 这是一个 GLSL 内置的纹理采样函数
    • u_Texture: 第一个参数,告诉函数使用哪个纹理采样器(即从哪个纹理单元中绑定的纹理进行采样)。
    • v_TexCoord: 第二个参数,提供了进行采样的纹理坐标
    • texColor: 函数的返回值,是一个 vec4,包含了从纹理的 v_TexCoord 位置采样得到的 RGBA 颜色值。
  • color = texColor;:
    • 将从纹理中采样到的颜色 texColor 作为当前片段的最终输出颜色。

数据流总结

  1. C++ -> VBO -> Vertex Shader: positiontexCoord 数据被发送。
  2. Vertex Shader -> Rasterizer: 计算出的 gl_Position 和透传的 v_TexCoord 被发送。
  3. Rasterizer -> Fragment Shader: 光栅化器为每个片段插值计算出自己的 v_TexCoord
  4. C++ -> uniform -> Fragment Shader: u_Texture (纹理单元索引) 被发送。
  5. Fragment Shader: 使用插值后的 v_TexCoordu_Texture 从纹理中采样,计算出最终的 color 并输出。