Graphics Pipeline
图形管线
到目前为止,我们依次学习了 MVP、光栅化、着色等知识,将这些操作合在一起基本就是从三维场景到二维图像的整个过程。而这一系列操作的结合,就叫做图形管线(Graphics Pipeline,更准确地说应该叫实时渲染管线:Real-time Rendering Pipeline)。
-
Application(CPU):先由应用程序在 CPU 上预处理模型数据,包括创建几何体、纹理、光照等信息,生成一个或多个场景(三维物体的所有顶点数据)。
-
Vertex Processing & Triangle Processing(GPU):根据顶点数据构建三角形,并进行 MVP变换(视图变换、投影变换、视口变换等)把三角形映射到屏幕空间。
-
Rasterization(GPU):进行光栅化,包括抗锯齿等处理。
-
Fragment Processing(GPU):Fragment 解释为“片元”、“片段”,是 OpenGL 里面的概念,其实就相当于像素的意思。在像素处理阶段会进行 Z-buffer 的判断处理。也会进行像素着色处理,如果实际情况是要进行顶点着色,还需要跟 Vertex Processing 阶段进行结合。
-
Framebuffer Operation(GPU):在 OpenGL 中,Framebuffer Operation 阶段通常被称为渲染到纹理(Render-To-Texture),是指将图形数据渲染到一个内存区域(FrameBuffer Object)中的过程。这个内存区域包含了颜色缓存、深度缓存、模板缓存等信息。因此,Framebuffer Operation 阶段不仅包括输出操作,还包括光栅化阶段和像素着色器阶段(Pixel Shader Stage),其中最重要的工作是将光栅化后的像素写入颜色缓存、深度缓存等缓存中。(这是 ChatGPT 给我的答案,因为这块闫老师似乎没有展开讲)
-
Display(GPU):将渲染好的图像显示在屏幕上。这个阶段通常会利用双缓冲机制来避免画面闪烁。尽管这个过程涉及到了 CPU 和 GPU 之间的数据传输,但实际上大部分的工作都是由 GPU 完成的。(这也是 ChatGPT 给我的答案~)
Shader
我们可以看到,整个图形渲染管线的工作几乎都是在 GPU 上处理的。在现代 GPU 里面,渲染管线中有些部分是可编程的。你可以自行去定义顶点或像素如何进行着色,也就是你可以写代码让 GPU 生成各种各样的着色结果,这部分代码就叫 Shader。
以 OpenGL 的着色语言 GLSL 举例:
uniform sampler2D myTexture; // 纹理(全局变量)
uniform vec3 lightDir; // 光照方向
varying vec2 uv;
varying vec3 norm; // 插值出来每个像素的法线
void diffuseShader() {
vec3 kd;
kd = texture2d(myTexture, uv); // 获取像素漫反射系数
kd *= clamp(dot(–lightDir, norm), 0.0, 1.0); // 进行简单漫反射,范围 [0, 1]
gl_FragColor = vec4(kd, 1.0); // 返回结果
}
上述是一个简单的像素着色器的例子,它的作用是确定像素的最终颜色。这里面有一点需要注意:对每一个像素(或顶点,如果是顶点着色器的话),都会应用一遍 Shader 代码。即 Shader 是通用的。
通过 Shader 编程可以实现许多神奇的效果,提供一个测试网站:
https://www.shadertoy.com/view/ld3Gz2
在这个网站里面进行测试,可以让你免去 OpenGL 或者 DirectX 背后的复杂机制,专注于 Shader 的编写。
随着 GPU 的发展,也伴随着一些新的着色器的产生:
- Geometry Shader:可动态产生三角形
- Compute Shader:可做通用 GPU 计算