Graphics Programming(一) —— 渲染流水线
现代渲染管线分为三个主要阶段:
应用程序阶段:
主要和 CPU,内存打交道. 该阶段末端, 渲染所需的数据(渲染图元 rendering primitives)通过数据总线传送到 GPU
- 将渲染图元读取到显存中: 从硬盘 HDD --> 内存 RAM --> 显存 VRAM
-
确定网格怎样被渲染在Unity中, 通常是使用MeshRender来将信息传递给GPU.
特别的, 在3D模型中:skin mesh render: 带蒙皮的骨骼
mesh filter/ mesh render: 将哪个mesh信息传递给GPU/ 没有骨骼的模型
-
调用Draw Call: CPU 通知 GPU 渲染CPU向一个队列"命令缓冲区"push命令, 而GPU从其中pull命令.
这样实现了CPU和GPU的并行工作
通常性能的优化都会减少Draw Call的个数
几何阶段:
主要和 GPU 打交道. 该阶段末端, 得到经过变换和投影之后的顶点坐标, 颜色及纹理坐标
-
顶点着色器: 实现顶点空间变换, 顶点着色, 光照等(可编程)它的输入来自于CPU. 每个顶点都会调用一次顶点着色器.
顶点着色器本身本可以创建和销毁任何顶点, 而且 顶点顶点之间是相互独立的.
它最基本且必须完成的工作是: 将顶点坐标从 模型空间 转换到 齐次裁剪坐标系 下
-
曲面细分着色器: 用于细分图元(可选着色器, 可编程)很遗憾, 此部分并没有学到.
-
几何着色器: 执行逐图元的着色(Per-Primitive)或产生更多图元(可选着色器, 可编程)很遗憾, 此部分并没有学到.
-
裁剪: 视锥裁剪 (可配置)确认视锥(即选择投影类型)并裁剪
裁剪并不一定是通过GPU来运算. 也可以通过CPU运算 后直接跳过该步骤.
-
屏幕映射: 将CVV 转换为Clip and Project space屏幕映射(Screen Mapping)并不会处理深度值Z.
值得注意的是, OpenGL的坐标系将"屏幕的左下角"当作原点(0,0); 而DirectX的坐标系将"屏幕的左上角"当作原点(0,0)
附录:坐标空间: 依照先后顺序Object space物体坐标系 --> World space世界坐标系 --> View space观察坐标系 --> Project space屏幕坐标系Object space 模型建立时得到的坐标. 与其它物体没有任何参照关系 World space 以一个固定的坐标原点经行参照确定物体位置 View space 以Camera为原点, 组成的正交坐标系 Project space 屏幕坐标系
光栅阶段:
基于几何阶段输出的数据, 雾化, 透明及为pixel正确配色. 经行单个像素的操作, 每个pixel的信息存储在颜色缓冲器(color buffer 或frame buffer)中
-
三角形设置: 计算光栅化一个三角网格所需的信息上一阶段输出的是"三角网格的顶点"即每条边的两个端点. 如果需要得到三角网格对pixel的覆盖情况, 需要计算每条边的像素坐标.
三角形设置(Triangle Setup)通过计算三角形边界的表示形式来确定每条边的坐标
-
三角形遍历三角形遍历(Triangle Traversal)确定每个三角网格所覆盖的pixel, 并使用顶点信息对覆盖区域进行差值
遍历完成后的输出就称之为片元(fragment), 包含了深度值Z, 法线, 纹理等信息, 可以说是"收集完全但未计算的像素信息"
-
片元着色器: 实现逐片元的着色(Per-Fragment)(可编程)这一阶段包含许多的渲染技术. 最重要的就是纹理采样(将贴图, 法线等颜色信息给片元)
但它仅能影响单个片元, 无法将结果发送给它周围的pixel.(法线信息除外)
-
逐片元操作: 片元的可见性, 深度缓冲, 颜色混合等(可配置)在OpenGL中称为逐片元操作(Per-Fragment Operations), 在DirectX中称为输出合并阶段(Output-Merger)
需要经过两个测试, 来决定片元的可见性问题模板测试 --> 深度测试模板测试 通常用于限制渲染的区域. 也可以用来渲染阴影, 轮廓渲染等 深度测试 通常用于视野深度计算
之后需要经行混合(Blend)操作, 决定此次操作和前一次操作的关系(实现不透明(全覆盖)或半透明(局部覆盖))
-
屏幕图像使用通常所说的双缓冲技术(Double Buffering). 前置缓冲(front buffer)和后置缓冲(frame buffer|back buffer)交替显示