注: 本文部分内容可能涉及侵权, 若发生此情况联系本人, 本人会及时删除相关侵权内容. 本着分享的心情记录一些内容, 无意冒犯.
注: 本文主描述渲染的主要流程, 没有描述渲染中部分可选过程. 意在方便同学理解渲染流程. 有错误的地方大家可以指出, 本人尽快更正.
GPU 的人海战术
GPU 擅长做大量的简单运算, 是一种专门做图形图像相关工作的处理器. 相当于是富士康, 堆人干活就完了.
CPU 的算力是能力. GPU 的算力是力量(人多力量大).
示例视频: https://www.zhihu.com/zvideo/1263089283423965185
渲染流水线
为提高效率, GPU 引入了流水线概念, 众多计算单元构成一条流水线, 每一部分只做非常简单的控制逻辑.
ps: 一般将复杂的渲染逻辑称为渲染管线 (Rendering Pipeline), 将基本的渲染流程叫做渲染流水线. 本质都是说的一个东西.
渲染流水线主要分为两个阶段: 图元渲染阶段和屏幕空间处理阶段.
图元渲染阶段
几何处理 (Geometry Processing): 这个阶段主要涉及顶点着色器的执行, 负责对输入的 3D 顶点数据进行变换, 投影和变形等操作.
图元装配 (Primitive Assembly): 在这里, 处理单个顶点形成几何图元, 如点, 线, 三角形等.
光栅化 (Rasterization): 将图元转换为像素的过程, 确定哪些像素将被着色.
片段着色 (Fragment Shading): 片段着色器负责计算每个像素的最终着色.
屏幕空间处理阶段
提交测试与混合 (Depth Test and Blending): 在这个阶段, 处理片段的深度值, 并根据深度测试决定是否将新的片段像素写入深度缓冲区. 混合操作用于处理半透明效果.
帧缓冲输出 (Framebuffer Output): 将最终的像素颜色输出到帧缓冲, 形成最终的图像.
几何处理
顶点着色器的处理单位是顶点, 其对于每个输入的顶点都会执行一次, 因此它在渲染管线中是高度并行化的阶段, 利用 GPU 的并行计算能力, 大大提高了图形渲染的效率.
顶点着色器的工作内容包括以下几个主要方面:
顶点变换
顶点着色器通过矩阵变换 (Model-View-Projection) 将顶点从对象空间转换到裁剪空间. 这个变换包括平移, 旋转, 缩放等, 以确保场景中的物体和几何图形正确地在屏幕上显示.
顶点属性处理
顶点着色器可以处理与顶点相关联的属性, 例如法线向量, 颜色, 纹理坐标等. 这些属性可能在光照, 纹理贴图等渲染过程中发挥重要作用.
投影变换
在将顶点从对象空间转换到裁剪空间后, 进行透视投影变换, 使得远处的物体在屏幕上显得较小, 近处的物体显得较大, 从而产生真实的透视效果.
裁剪
在裁剪空间中, 对超出视锥体 (视景体) 的顶点进行裁剪, 以排除不可见的部分, 优化渲染性能.
生成顶点输出
经过上述处理后, 顶点着色器生成裁剪空间中经过变换的顶点数据和相关属性.
图元装配
图元装配阶段是图形渲染管线中的一个重要阶段, 紧随顶点着色器之后. 在这个阶段, 图形渲染管线将顶点着色器输出的顶点数据组装成图元 (Primitives). 图元是构成图形的基本几何形状, 如点, 线和三角形.
图元装配阶段的工作内容主要包括以下几个方面:
图元类型划分
根据渲染引擎或应用程序的设置, 确定图元的类型. 常见的图元类型有点 (Point), 线 (Line) 和三角形 (Triangle). 对于线段和三角形, 还有可能有不同的拓扑类型, 如线带 (Line Strip), 线环 (Line Loop), 三角带 (Triangle Strip) 等.
图元组装
将顶点着色器输出的顶点数据根据图元类型进行组装, 将它们组合成完整的图元. 例: 如果要绘制一个三角形, 图元装配阶段会将三个顶点组合成一个三角形图元.
顶点数据传递
在图元装配阶段, 图元的顶点数据会被传递到几何着色器 (如果有的话) 或者光栅化阶段, 以进行下一步的处理. 几何着色器可以进一步处理几何图形, 例如生成新的顶点, 创建细分表面或者执行其他几何操作.
图元重复
在某些情况下, 渲染引擎可能需要重复使用同一组顶点数据来绘制多个图元. 例: 在绘制大量相同形状的粒子或草地时, 可以通过在图元装配阶段重复使用顶点数据来实现效率优化.
丢弃无效图元
在图元装配阶段, 有时会根据裁剪操作丢弃一些在视锥体之外的图元, 以避免不必要的片段着色计算, 提高渲染性能.
光栅化
光栅化是个离散化的过程, 是将连续的物体转化为离散屏幕像素点的过程.
光栅化包括了三角形设置和三角形遍历两个阶段.
三角形设置
要得到三角形对像素的覆盖情况, 就必须计算每条边上的像素坐标. 为了能够计算边界像素的坐标信息, 就需要得到三角形边界的表达方式. 这样的过程就叫做三角形设置.
三角形遍历
三角形遍历阶段会根据上一阶段的计算结果来判断一个三角形网格覆盖了哪些像素, 并使用三角形网格的 3 个顶点信息对整个区域的像素进行插值.
这一步的输出就得到一个片元序列. 片元并不是真正意义上的像素, 而是包含了很多状态的集合, 这些状态用于计算每个像素点的最终颜色. 其中包括但不限于屏幕坐标, 顶点颜色,深度值以及其他几何阶段输出的顶点信息. 如法线, 纹理坐标等.
片段着色器
光栅化阶段会确定图元所覆盖的片段, 利用顶点属性插值得到片段的属性信息, 然后送到片段着色器上进行颜色计算. 片段着色器的任务就是着色, 这个阶段会进行纹理贴图, 光照计算以及阴影处理.
纹理贴图
纹理贴图也称为纹理映射, 是将图像信息映射到三角形网格的技术. 我们将贴图上的每个像素称为纹素(texel, 纹理像素 texture pixel 的意思, 用于和像素进行区分), 纹理映射其实就是进行纹素和像素对应的过程.
提交测试与混合
Alpha 测试
通过片元数据可以获取该片元的 alpha 值. 如果 alpha 小于某个值的话, 则直接丢弃.
模板测试
如果开启了模板测试, GPU 会先读取模板缓冲区中该片元位置的模板值, 然后将该值和读到的参考值进行比较. 可以设定超出内容舍弃或内部内容舍弃.
深度测试
近处物体会遮挡远处物体, 这种效果可以通过深度测试来实现. 它通过将深度缓存中的值和当前片元的深度进行比较, 计算是否需要更新深度缓存和颜色缓存, 如果不需要则将该片元丢弃.
混合模式
半透明物体的绘制需要遵循画家算法由远及近进行绘制, 因为半透明的混合跟物体的顺序有严格的对应关系.
R 通道: Cr = f(Green_r, Red_r), G 通道: Cg = f(Green_g, Red_g), B 通道: f(Green_b, Red_b).
帧缓冲输出
可以理解为是一个临时的画布, GPU 渲染完的信息会存放在帧缓冲区等待使用. 上面的测试也是在帧缓冲区进行的.
帧缓冲区主要包含颜色缓冲区和深度缓冲区, 例:
下面右侧蓝色框是一个帧缓冲区, 需要对蓝色和红色三角形的片元进行渲染. 红色片元的深度值是 0.8, 蓝色片元的深度值是 0.5.
渲染红色片元时, 由于它的深度值小于初始值, 所以它的 z-buffer 值和 collor buffer 值会覆盖帧缓冲区对应位置的值.
渲染蓝色片元时, 由于它的深度值比帧缓冲区的深度值小. 所以可以覆盖缓冲区中的内容.
当模型的图元经过了上面层层计算和测试后, 就会显示到我们的屏幕上. 我们的屏幕显示的就是颜色缓冲区中的颜色值. 但是为了避免我们看到那些正在进行光栅化的图元, GPU 会使用双重缓冲的策略. 对场景的渲染是在幕后发生的, 即在后置缓冲区中. 一旦场景已经被渲染到了后置缓冲区中, GPU 就会交换后置缓冲区和前置缓冲区中内容. 而前置缓冲区是之前显示在屏幕上的图像. 由此保证里我们看到的图像总是连续的.