顶点着色器(Vertex Shader)是图形管线(graphics pipeline)中的一个可编程阶段,用于处理三维模型的每个顶点。根据 Khronos Group, 2025,顶点着色器接收顶点的属性 — 坐标、法线、颜色和纹理坐标 — 并根据指定的逻辑对其进行变换。顶点着色器独立地对每个顶点执行,这使得能够在数千个GPU核心上高效地并行计算。
要点
顶点着色器是图形管线(graphics pipeline)中可编程的阶段,逐个处理三维模型的每个顶点。顶点(vertex)是三维空间中的一个点,包含属性:xyz坐标、法线、颜色和UV纹理坐标。
与固定功能管线(fixed-function pipeline)不同,后者按照固定算法执行变换,顶点着色器赋予开发人员对顶点变换的完全控制。这使得可以实现程序化动画、模型变形、骨骼蒙皮以及GPU端的任何数学变换。
OpenGL ES图形管线包含从输入数据到最终图像的一系列阶段。顶点着色器位于顶点装配(vertex assembly)阶段之后、光栅化阶段之前。每个通过顶点缓冲区从CPU发送的顶点经过顶点着色器恰好一次,结果被传递到下一阶段进行图元装配。
| 管线阶段 | 用途 |
|---|---|
| 顶点规范 | 从VBO传输顶点数据和属性 |
| 顶点着色器 | 处理每个顶点,坐标变换 |
| 细分曲面 | 可选地将几何体细分为子图元 |
| 几何着色器 | 可选地生成或删除图元 |
| 光栅化 | 将图元转换为片段以便填充 |
顶点着色器通过CPU代码中配置的顶点属性指针(vertex attribute pointers)从顶点缓冲区(VBO)接收顶点的属性。开发人员使用GLSL(OpenGL着色语言)或类似语言编写着色器代码,由GPU驱动程序编译为针对特定芯片的机器指令。
对于每个顶点,顶点着色器依次执行三个操作:读取输入属性 — 应用开发人员定义的变换 — 将结果写入输出变量。最小必须输出是顶点在裁剪空间中的位置(gl_Position)。所有其他输出数据(颜色、法线、纹理坐标)被插值并传递到下一阶段 — 片段着色器。
#version 300 es
in vec3 aPosition;
in vec2 aTexCoord;
uniform mat4 uMVP;
out vec2 vTexCoord;
void main() {
gl_Position = uMVP * vec4(aPosition, 1.0);
vTexCoord = aTexCoord;
}
顶点着色器解决图形管线中与几何变换相关的广泛任务。每个任务通过对向量和矩阵的数学运算实现,并行地为数千个顶点同时执行。
最基本且最普遍的任务是将顶点坐标乘以模型(model)、视图(view)和投影(projection)矩阵(MVP)。MVP矩阵将顶点从模型的局部空间变换到裁剪空间(clip space),随后在管线的自动阶段中归一化并转换为屏幕坐标。
对于动画角色,顶点着色器实现骨骼动画(蒙皮)。每个顶点以特定的权重绑定到多个骨骼。顶点混合 — 来自绑定骨骼的变换加权和 — 允许在骨骼移动时平滑变形角色的皮肤。
顶点着色器可以根据时间或其他uniform参数在无需CPU参与的情况下修改几何体。波浪变形、基于柏林噪声的噪声位移和水波效果 — 所有这些效果均在顶点着色器中实现,无需从RAM重新加载几何体。
虽然两种着色器都是图形管线的可编程阶段,但顶点着色器和片段着色器解决根本不同的任务并在图像处理的不同阶段工作。
| 特性 | 顶点着色器 | 片段着色器 |
|---|---|---|
| 输入数据 | 来自VBO的顶点属性 | 来自VS的插值数据 |
| 调用次数 | 顶点数量 | 片段数量 |
| 主要任务 | 几何变换 | 计算像素颜色 |
| 计算负载 | 几何复杂度 | 屏幕分辨率 |
| 纹理访问 | 受限(通过纹理查找) | 完全访问 |
关键区别在于调用频率。顶点着色器每个顶点调用一次,而片段着色器为每个片段(像素)调用。在高分辨率场景中,片段着色器的调用次数超过顶点着色器的数千倍,这使得像素着色器的优化更加关键。
顶点着色器性能对于实时应用至关重要,特别是在能耗受限的移动开发中。主要性能因素包括顶点数量、着色器代码复杂度和GPU内存带宽。
为减少顶点着色器负载,采用以下方法。细节层次(LOD) — 当相机远离对象时切换到简化版本的模型。实例化 — 在一次绘制调用(draw call)中使用不同的变换矩阵多次绘制相同的几何体。
// 支持实例化的顶点着色器
in vec3 aPosition;
in mat4 aInstanceMatrix;
uniform mat4 uViewProjection;
void main() {
vec4 worldPos = aInstanceMatrix * vec4(aPosition, 1.0);
gl_Position = uViewProjection * worldPos;
}
顶点着色器在不同图形API中具有相似的逻辑,但语法和命名约定不同。让我们看看在GLSL中用于OpenGL ES 3.0以及在Metal Shading Language中用于iOS和macOS的基本顶点着色器的实现。
#version 300 es
in vec4 position;
in vec3 normal;
in vec2 texcoord;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform mat3 normalMatrix;
out vec3 vNormal;
out vec2 vTexCoord;
out vec3 vPosition;
void main() {
vec4 mvPosition = modelViewMatrix * position;
vPosition = mvPosition.xyz;
vNormal = normalMatrix * normal;
vTexCoord = texcoord;
gl_Position = projectionMatrix * mvPosition;
}
#include <metal_stdlib>
using namespace metal;
struct VertexIn {
float3 position [[attribute(0)]];
float3 normal [[attribute(1)]];
};
struct VertexOut {
float4 position [[position]];
float3 worldNormal;
};
vertex VertexOut
vertexMain(VertexIn in [[stage_in]],
constant float4x4& mvp [[buffer(1)]]) {
VertexOut out;
out.position = mvp * float4(in.position, 1.0);
out.worldNormal = in.normal;
return out;
}
常见问题
顶点着色器是GPU上的一个程序,处理3D模型的每个顶点并将其坐标转换为屏幕坐标。将模型想象为点的网格;顶点着色器根据指定的数学规则移动这些点。
顶点着色器处理顶点并负责几何体,而片段着色器确定每个像素的颜色。前者为每个顶点调用(数千次),后者为每个像素调用(数百万次)。
顶点着色器使用与图形API对应的语言编写:GLSL用于OpenGL ES,Metal Shading Language用于iOS/macOS上的Metal API,HLSL用于Direct3D。概念上它们都对顶点执行相同的操作。
顶点着色器的复杂度直接影响FPS:每个顶点的指令越多 = 处理时间越长。优化包括通过LOD减少顶点数量、使用实例化和最小化着色器代码中的条件跳转。
游戏中的主要任务:通过MVP矩阵进行坐标变换、动画角色的蒙皮、衣物和头发的程序化变形、面部表情之间的变形以及带有GPU动画的粒子效果。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。