💡 本系列文章收录于个人专栏 ShaderMyHead:juejin.cn/column/7505…
一、Shader 简介
Shader(着色器)是一段运行在 GPU 上的程序,开发者通过编写程序,可以在图形渲染的过程中实现高度自定义的图形效果(如光照、材质、后处理等),因此 Shader 是游戏开发中必备的一项重要技能。
示例图(一个使用 Shader 实现的溶解效果):
二、为何需要 Shader?
因为 Shader 是利用 GPU 来执行渲染图像的程序,比起走 CPU 绘制图像的形式,GPU 渲染图形的效率会高得多。
对于一帧图像来说,它会由非常多的像素点构成,每个像素点的位置、颜色都需要经过计算处理并绘制到屏幕上。
CPU 的计算能力自然是非常强大的,如果仅有少数的像素需要进行计算,CPU 可以非常快速地得到结果。但问题在于一个图像需要计算的像素数量实在太庞大了,而 CPU 的核心数量较少,导致绘制完一帧图像的总时长会非常高。
而 GPU 专为大规模并行计算设计,其包含数千个小型计算核心(如 NVIDIA RTX4090 具备 16,384 个 CUDA 核心),尤其适合处理高度重复、可并行的任务(如顶点变换、片元着色)。
英伟达的专家曾通过机器串行/并行喷射绘画的展示,来科普 GPU 高效绘图的能力:
首先以串行喷射的绘画,来模拟 CPU 绘图的形式:
接着是以万箭齐发喷射的绘画,来模拟 GPU 绘图的形式:
因此在图像处理领域,擅长与 GPU 打交道的 Shader 是一门必备的技能。
三、如何编写 Shader?
3.1 GLSL
GLSL(OpenGL Shading Language) 是当下最主流的 Shader 编程语言,它基于 OpenGL ES / WebGL 标准。
下方是一段简单的 GLSL 代码示例:
in vec2 a_position;
in vec4 a_color;
out vec4 v_color;
void main() {
v_color = a_color;
gl_Position = vec4(a_position, 0.0, 1.0);
}
Cocos Creator 也是使用的 GLSL 来开发 Shader,我们会在下篇文章学习 GLSL 语法。
3.2 其它 Shader 开发语言(仅做了解)
除了 GLSL,还可以使用如下语言来开发 Shader:
- HLSL (High-Level Shading Language) :
微软 DirectX 的 Shader 语言,需通过工具转换为 GLSL 来跨平台。 - MSL (Metal Shading Language) :
苹果 Metal 的 Shader 语言。 - WGSL (WebGPU Shading Language) :
下一代的跨平台 WebGPU Shader 语言,未来可能成为 Web 标准,截止本文发布时还处于草案阶段,兼容性较差(特别是移动端)。
四、渲染管线
渲染管线(Rendering Pipeline)是图形渲染的核心流程,它描述了从 数据 到最终 屏幕像素图像 的完整处理过程,而 Shader 程序用于控制渲染管线中的特定阶段,因此理解渲染管线阶段和协作机制,是掌握 Shader 编程、性能优化和高级渲染效果的基础。
4.1 渲染管线的核心阶段
一个经典的渲染管线会按顺序依次执行如下几个阶段:
1. 应用阶段(Application Stage)
-
输入:2D Sprite 数据,或者 3D 模型、纹理、材质、灯光等数据。
-
处理:
- CPU 准备数据(如模型矩阵、摄像机参数)。
- 调用图形 API,将数据提交给 GPU。
2. 顶点处理阶段(Vertex Processing)
-
输入:顶点数据(Vertex Data)。
-
处理:通过顶点着色器(Vertex Shader) 计算和设置每个顶点的位置、尺寸等属性。
3. 图元装配(Primitive Assembly)
-
输入:顶点数据。
-
处理:
- 将顶点组装成基本图元(点、线、三角形)。
- 执行裁剪(Clipping):剔除屏幕外的图元。
4. [可选] 几何着色器处理(Geometry Shading)
-
输入:图元数据。
-
处理:接收完整的图元(如一个三角形、一条线段),可以修改、丢弃或生成新的图元。
5. 光栅化(Rasterization)
-
输入:图元(如三角形)。
-
处理:
- 将连续的几何图形离散化为片元(Fragment) ,即像素候选。
- 计算每个片元的位置、深度、插值后的属性(如 UV、颜色)。
💡 进一步了解片元
- 片元是光栅化阶段的产物:
当几何图形(如三角形)被光栅化时,它会被分割成许多小单元,每个单元对应一个携带了信息的候选像素,即片元。- 片元携带的信息:
包括颜色、深度(Z值)、纹理坐标、模板值等。- 片元 ≠ 最终像素:
片元只是潜在的像素 (所以才叫候选像素),后续可能被丢弃(如被遮挡)或被修改(如混合半透明颜色)。
6. 片元处理阶段(Fragment Processing)
-
输入:光栅化后的片元数据。
-
处理:通过片元着色器(Fragment Shader) 计算每个片元的颜色(纹理采样、光照计算等)。
7. 测试与混合(Testing & Blending)
-
输入:片元颜色和深度信息。
-
处理:
- 执行深度测试(Depth Test)、模板测试(Stencil Test)。
- 将通过测试的片元进行混合(Blending):与帧缓冲区中已有的颜色按混合方程(如 alpha 混合)合成。
- 写入帧缓冲区(Frame Buffer)。
8. 输出到屏幕
- 处理:帧缓冲区的像素数据最终显示在屏幕上。
4.2 Shader 在渲染管线中的作用
上述的渲染管线流程中,顶点处理阶段和片元处理阶段都需要 Shader 的参与:
- 在顶点处理阶段,使用 Vertex Shader(顶点着色器) 对顶点进行处理;
- 在几何着色器处理阶段,使用 Geometry Shader(几何着色器) 对已组装的图元进行进一步增/删处理(进而生成新图元),留意此阶段并非渲染管线必经阶段,且移动端对几何着色器的支持有限;
- 在片元处理阶段,使用 Fragment Shader(片元着色器) 对片元数据进行处理。
我们可以将渲染管线流程简化为:
数据 → Vertex Shader(顶点着色器)→ 图元装配 → [可选] Geometry Shader(几何着色器)→ 光栅化 → Fragment Shader(片元着色器)→ 测试与混合
该流程示意图如下(蓝色模块表示可通过 GLSL 编程的 Shader 处理模块):
在下篇文章,我们会在 Cocos Creator 中,通过 Shader 来将一个节点染色。