【技术美术百人计划】图形 1.1 渲染流水线

为什么要介绍渲染流水线

我们做渲染要了解硬件厂商在设计整个渲染流水线的过程中做了什么事情
但是每个硬件厂商都有自己的一套东西, 不太统一
所以大概会讲最基础的那一部分渲染管线的内容
《进阶应用》(第三章) 后续章节
大概最基础的那部分渲染管线的内容
会讲清楚很多硬件厂商他们常用的优化手段

然后讲清楚手机上的GPU运作构架, GPU流水线构架

为什么要说这些东西呢?
因为只有当你很了解这些东西的时候, 才可以对相应的内容进行优化, 然后才可以底层去了解, 采用更好的优化流线

RenderPipeline

图形渲染管线是实时渲染的核心组件, 一个场景里有很多要素, 包括3D场景, 光源, 摄像机等等

这些场景要素是要在显示屏或者输出设备上显示出来的, 元素是3D的, 而屏幕是2D的, 渲染管线的作用就是, 通过一系列的处理, 把这些3D元素转化成屏幕上的2D图像

渲染管线之所以叫管线, 因为它的工作流程是线性的, 假设数我们把它划分成几个阶段, 那么它每一个阶段的输出就会称为下一阶段的输入

渲染管线的划分方法有很多中, 不同的教程或者说不同的文章有不同的划分方法
这里采用比较通用的划分方式, 就分成4个阶段:

  1. 应用阶段
  2. 几何阶段
  3. 光栅化阶段
  4. 逐片元操作
  5. 后处理

(常用的分法将逐片元操作归入光栅化中)
在这里插入图片描述

每一个阶段都是环环相扣的, 每一个阶段都要准备好下一个阶段的数据

应用阶段

准备场景里的数据, 你的对象的基本数据, 比如说场景中的物体, 他们的位置朝向, 大小以及物体对应的模型里边的每一个顶点的位置, 法线, 切线等等

再比如场景光源的位置朝向和一些基本属性, 还有摄像机的位置朝向

准备的数据有什么用呢

几何阶段要用

几何阶段

顶点着色器

在顶点着色器里要计算顶点光照, 那么顶点光照就需要光源的位置以及摄像机的朝向, 还有当前顶点的世界位置
那么当前顶点的世界位置有需要直到顶点在模型空间里的位置, 以及模型本身的位置旋转缩放等
然后通过这些数据来进行计算

曲面细分着色器

需要通过现有的顶点来生成更多的顶点, 也需要直到现有顶点在模型里面的信息

几何着色器

通过现有的图元来做一些几何方面的操作, 生成更多的顶点和图元
比如对现有图元所在的平面生成法线, 那么同样需要直到现有图元的顶点位置,

顶点裁剪

同样的几何阶段要为光栅化准备数据, 要干掉看不到的屏幕意外的顶点, 这就是顶点裁剪,

屏幕映射

还需要把顶点位置从3D坐标空间转换成2D坐标空间, 这就是屏幕映射

光栅化阶段

三角形设置

拿到映射到2D空间的顶点位置, 我们要把它组装成三角形, 这就是三角形设置

三角形遍历

然后还要知道三角形包含了哪些2D空间的像素点, 这就是三角形遍历

片段着色器

最后我们要对这些点使用他们包含的数据来着色, 并且为后面的逐片元着色器准备数据

逐片元操作阶段

我们的操作对象从光栅化输出的片元数据
片元: 可以理解为屏幕上的某一个像素点, 对于这些片元我们需要进行一系列的测试
比如: 透明度测试, 深度测试, 模版测试, 通过测试的片元就保留起来, 否则就丢弃掉

然后在2D屏幕坐标系中, 同一个位置上的像素点可能会对应于多个不同的片元

那么还需要把这些通过测试的片元进行一个混合操作, 从而得到像素点最终输出的颜色

逐片元操作完成以后, 我们就得到了一个类似于贴图的数据保存在内存里
然后我们对这个数据还可以进行后处理

后处理

可以理解为图像处理, 比如模糊, 景深, 高光等等

以上就是渲染管线每个阶段之间的关系

详细看下每个阶段在干什么
在这里插入图片描述

大致介绍

应用阶段(Application)

准备场景数据

一般是在CPU上完成的, 比如说从磁盘或者内存上读取模型或者贴图数据, 然后加在到应用程序里
以游戏为例, 在一个游戏场景里可能会加在山脉房屋角色的模型或者贴图
然后在游戏场景里, 首先会做一个粗粒度的剔除

粗粒度剔除

比如说一个物体会被挡道另一个物体的后面, 那么被挡住的物体就不需要显示, 也不需要提交到管线的后面一个阶段去进行处理, 然后还需要设置一些基本的渲染参数和状态

设置基本的渲染参数

比如渲染UI和渲染场景, 他们的渲染参数和模式可能是不一样的

调用DrawCall

这些做完以后, 我们要把渲染所需要的数据通过DrawCall传递给显存交给GPU处理

以下三个阶段一般会放在GPU上处理

GPU的特性是并行性能比较好, 比如一个物体上面同一个物体可能有很多个顶点需要做光照或者着色计算, 而且这些顶点他们只是数据不同, 但是他们的光照模型, 还有几何运算方式都是一样的, 那么就可以把他们分配到GPU的不同工作但愿上面去同时执行, 这样的化速度就很快. 光栅化和逐片元操作也是同理
##几何阶段(Geometry Processing)

光栅化(Rasterization)

逐片元操作(Pixel Processing)

应用阶段(详细)

在这里插入图片描述

准备场景数据

  • 场景物体数据
  • 摄像机数据
  • 光源及阴影数据
  • 其他全局数据
    它首先需要准备好场景的基本数据, 比如说在场景里面有哪些需要渲染的物体, 模型, 房屋, 山川, 树木, 河流等等, 然后是摄像机的数据, 它代表的是观察视角

然后是光源, 以及一些全局性的数据, 准备好这些需要渲染的场景对象之后, 我们还要做一些优化

在这里插入图片描述

场景物体数据

  • 物体变换数据: 位置, 旋转, 缩放
  • 物体网格数据: 顶点位置, UV贴图

摄像机参数

  • 位置, 方向, 远近裁剪平面
  • 正交/透视(FOV)
  • 视口比例/尺寸等

光源信息:

  • 光源类型: 方向光(颜色, 方向), 点光(颜色, 位置, 范围, 衰减系数), 聚光(颜色, 位置, 方向, 内外圆锥角)
  • 位置, 方向, 角度等其他参数
  • 设置阴影:
    • 是否需要阴影: 判断该光源可见范围是否有可投射的物体
    • 阴影参数: 对应光源序号, 阴影强度, 级联参数, 深度偏移, 近平面偏移
  • 追光源绘制阴影贴图:
    • 近平面偏移
    • 逐级联
      • 计算当前光源 + 级联对应的观察矩阵, 投影矩阵, 以及对应到阴影贴图里视口区域
      • 绘制到阴影贴图
        在这里插入图片描述

加速算法

  • 碰撞检测
  • 加速算法
    • 可见光裁剪
    • 可见场景物体裁剪
      • 八叉树
      • BSP树
      • K-D tree
      • BVH
  • 遮挡剔除
  • 其他算法
    在这里插入图片描述

比如说算法上的加速, 或者说干掉一些看不到的不需要渲染的物体, 做一些剔除

设置渲染状态准备渲染参数

  • 绘制设置
    • 使用着色器
    • 合批方式

对于不同物体使用不同着色器渲染, 或者说对于不同的选择对象使用一些合批方式, 比如GPU instance或者动态批处理等等

  • 绘制顺序
    • 相对摄像机的距离
    • 材质 RenderQueue
    • UICanvas其他方式等
  • 渲染目标
    • FrameBuffer
    • RenderTexture
    • 也有可能输出到多个渲染目标, 然后通过一些方式合并
  • 渲染模式
    • 前向渲染(???)
    • 延迟渲染(???)

这里就是将对于前面渲染好的数据, 我们要用什么样的方式去渲染
比如对于场景物体, 我们可能是由远到近去渲染, 还是说先渲染不透明的, 再渲染半透明的, 这个就是绘制顺序

再比如对于不同的物体, 用不同的Shader去渲染, 这个可能涉及到一些绘制设置, 以及最后渲染完后以后, 我们要把渲染得到的结果输出到哪里? 输出到RenderTexture还是FrameBuffer? 这是渲染目标

RenderTexutre 和 FrameBuffer的区别
区别

还有一些渲染模式, 比如unity常见的成像渲染或者延迟渲染等等.

调用DrawCall

设置渲染状态和参数以后, 我们就会调用DrawCall把带有数据的图元输出到显存交给GPU处理

  • 顶点数据
    • 位置
    • 颜色
    • 法线
    • 纹理uv坐标
    • 其他顶点数据
  • 其他数据
    • MVP变换矩阵
    • 纹理贴图
    • 其他数据
      在这里插入图片描述

几何阶段(详细)

在这里插入图片描述

顶点着色器

模型坐标系->(模型变换) 世界坐标系->(视图变换)->视图坐标->(投影变换)->投影坐标系->(视口变换)->视口坐标系

可选顶点处理

曲面细分着色器(可选)

使用顶点着色器输出的顶点按照一定的规则和算法生成更多的顶点, 将现有的网格和图元进行细分

几何着色器(基于图元的操作)

几何着色器操作对象是一个图元(顶点, 线段, 两个顶点, 也可能是多个顶点构成的连续线段, 也有可能是三角形)
通过给定图元, 生成更多的图元

投影

对于顶点在裁剪空间里的位置, xyzw, 我们会对它进行透视除法, 把xyz除以w, 来完成透视操作, 这样就从投影坐标系转换到的标准设备坐标系(NDC)

裁剪

将不在裁剪空间里的部分剔除, 如果部分在裁剪空间意外事件, 还会生成额外的顶点补全图元

屏幕映射

标准坐标系x, y范围是[-1, 1], 而屏幕坐标系是输出设备的长和宽

光栅化阶段

在这里插入图片描述

三角形设置

对于一个图元, 一条直线或者一个三角形, 知道顶点怎么知道屏幕的覆盖像素呢
需要知道图元的边界信息, 而计算边界信息的过程, 这叫三角形设置

三角形遍历

在得到三角形边界信息之后, 就会遍历相关的像素
寻找三角形网格所改的所有像素过程—三角形遍历
使用三角形的每一个顶点, 对每个覆盖的像素线性插值
然后得到当前三角形在这个像素对应的片元数据

注意⚠️: 这里的片元不等同于像素, 因为在屏幕的同一个像素位置, 有可能对应于多个三角形片元, 他们重叠在一起, 那么各自着色后, 需要考虑是否保留, 保留哪一些以及剩下保留下来的那些如何混合

抗锯齿(MSAA)

在这里插入图片描述

SSAA

渲染到一个分辨率放大n倍buffer

比如屏幕的分辨率是1024*1024, 渲染的分辨率可以设置成2048* 2048, 这样相当于我们会渲染到一个放大四倍的buffer上, 然后对buffer进行采样, 再输出到屏幕

对放大的n倍buffer下采样

MSAA

在光栅化阶段
计算多个覆盖样本

MSAA会对每个像素设置多个子采样点, 然后对每个子采样点进行覆盖测试和遮挡测试
覆盖测试就是看这个子采样点是否在三角形内, 遮挡测试是对这个子采样点的深度 和 Zbuufer(深度缓存)比较, 看是否能通过
如果能通过这两个测试, 说明这个子采样点属于这个三角形, 那么最后我们就能得到当前像素被三角形覆盖的信息, 并且覆盖信息会被保存起来, 到后面的逐片元操作里面, 用于后序的着色混合
比如有两个子采样点属于三角形, 那么后序的三角形着色比例就会取三角形的一般以及不属于三角形的一般(图上右下角粉色着色)

FXAA/TXAA

后处理阶段, 不在这个阶段

逐片元操作

光栅化阶段结束后, 得到了被三角形覆盖的片元序列
在这里插入图片描述

片元着色(可编程)

三个顶点进行线性插值

颜色混合

当屏幕上有多个片元的时候, 如何取舍, 那么我们需要对其进行一系列的测试, 透明度测试, 深度测试, 模板测试, 通过了才能留下来

透明度测试

透明小于给定阈值的片元就会被舍弃

那么通过的片元, 需要用什么方式进行混合, 这一步是可配置的

在这里插入图片描述

深度测试

测试片元的深度值
将深度值与Zbuffer里的深度值比较

例子中是近处的覆盖远处, 但是并不是一定是近处能覆盖远处, 因为设置可以配置, 也可以是远处覆盖近处
也就是说深度比较的方式可以是 <, >, = 三种情况
配置完后, 还可以配置是否向深度缓冲写入我们覆盖的深度值

模板测试

比较的是模板值
先将立方体放大, 然后写入模板值, 然后再绘制原来大小的立方体, 把立方体的模版值和描边模板值进行比较, 只要模板值是描边模板值, 就覆盖掉, 实现立方体永远在描边之上
同样模板测试 可以是<, >, =

混合

在这里插入图片描述

目标缓冲区

…好像没讲

片元着色器详细流程
在这里插入图片描述

流程总结:
在这里插入图片描述

后处理

在这里插入图片描述

参考文献

在这里插入图片描述

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值