「技美之路 第01篇」图形 1.1 渲染流水线

Game艺视界 2021-05-07 47.5k
今日起开始分享学习技美之路专栏,文章来源听课笔记以及业界大佬分享的经验文章,主要来自CSDN_知乎等。技美路漫长 一定要坚持  开始吧!

一.整体流程

整体流程(渲染管线可分为四个阶段)每一个阶段的输出为下一个阶段的输入


应用阶段:准备的是场景,你的对象的基本数据,比如说场景里面的物体他们的位置朝向、大小以及物体对应的模型里边每一个顶点的位置,法线、切线等等;场景光源的位置朝向和一些基本属性,还有摄像机的位置朝向等等。这些数据用处在哪里,进入下一个阶段

几何阶段:首先在顶点着色器里,我们有可能需要计算顶点光照,那么顶点光照就需要知道光源的位置和朝向以及摄像机的位置和朝向,还有当前顶点的世界位置,通过这些数据来进行计算,曲面细分着色器,需要通过现有的顶点来生成更多的顶点,那么也需要知道现有顶点在模型里的位置信息。几何着色器,需要通过现有的图元来做一些几何方面的操作,生成更多的顶点和图元比如对现有图源所在的平面生成法线 那么同样需要知道现有图源的顶点的位置,同样的几何阶段要为光栅化阶段准备数据

比如要干掉看不到的屏幕以外的顶点,这就是顶点裁剪

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

光栅化阶段:拿到映射到2D空间里的顶点位置,我们要把它组装成三角形,这就是三角形设置然后还要知道三角形包含了哪些2D空间的像素点,这就是三角形遍历最后我们要对这些点使用它们包含的数据来着色,并且为后面的逐片元着色准备数据

逐片元操作:我们的操作对象就变成了光栅化操作输出的片元数据,片元可以理解成为屏幕上的某一个像素点,对于这些片元我们需要进行一系列的测试。比如透明度测试,深度测试和模板测试,通过测试的片元就保留起来,否则就丢弃掉,然后在2D屏幕坐标系当中,同一个位置上的像素点有可能会对应于多个不同的片元,那么我们可能还需要把这些通过测试的片元的颜色进行一个混合操作,从而得到像素点最终输出的颜色,逐片元操作完成以后,我们就得到了一个类似于贴图的数据保存在内存里,然后我们对这个数据还可以做一个后处理,可以理解成为图像处理,比如模糊、景深、高光等等,那么这样的话渲染管线每个阶段之间的关系大概就讲清楚了。

二.分阶段介绍


应用阶段


在渲染管线中,应用阶段是在CPU中进行处理的


  • 首先是准备场景,从磁盘或是内存上读取模型或者贴图数据,将其加载进应用程序中,对象为基本数据:包括不限于场景中的物体的位置,朝向,大小,物体网格数据,顶点位置,UV贴图,法线,切线等;场景的光源位置朝向,类型,参数属性等;摄像机位置朝向,模式,视口长宽比等等…

补充:光源和阴影

设置光源:

1.方向光,颜色,方向等
2.点光源:颜色,位置,范围等
3.聚光源:颜色,位置,方向,内外圆锥角等

设置阴影:

1.是否需要阴影:判断该光源可见范围内是否有可投阴影的物体
2.阴影参数:对应光源序号,阴影强度,级联参数,深度偏移,近平面偏移等

逐光源绘制阴影范围:

1.近平面偏移
2.逐级联

计算当前光源+级联对应的观察矩阵,投影矩阵,以及对应到阴影贴图里的视口区域

绘制到阴影贴图

  • 加速算法,粗粒度剔除:处理遮挡的问题,不会看到的,就不渲染,降低渲染成本,提高渲染性能

  • 可见光裁剪:点光和聚光有衰减,聚光锥体有区域;若离相机距离较远,或者光锥与相机的视锥体不相交,则剔除渲染;
  • 可见场景物体裁剪:遮挡啥的,相关算法:八叉树,BSP树,k-D树,PVH包围盒等
  • 说起裁剪就不由想起图形学里的经典裁剪算法:Liang-Barsky算法,Cohen-Sutherland算法(编码裁剪算法)这两个算法是用于将图像裁剪到可见范围内

  • 设置渲染状态,准备渲染参数:渲染UI和场景,其参数和模式可能不一样,通俗解释:场景中的网格如何被渲染,渲染顺序是啥,使用哪个顶点着色器/片元着色器,光源属性,材质,最后会渲染到哪里,渲染模式等等…

  • 绘制设置:使用着色器,合批方式(gpu 动态合批)
  • 绘制物体顺序:相对相机距离,材质RenderQueue, UICanvas等
  • 渲染目标:FrameBuffer, RenderTexture...
  • 渲染模式:前向渲染(ForwardBase ForwardAdd),延迟渲染


  • 调用DrawCall,输出渲染图元到显存:图元从CPU迈向GPU
  • DrawCall是一个命令,发起方是CPU,接收方是GPU。仅指向一个需要被渲染的图元(Primitives)列表。

  • 顶点数据:位置,颜色,法线,纹理uv坐标,其他顶点数据
  • 其他数据:MVP变换矩阵,纹理贴图,其他数据


几何阶段

  • 顶点着色器(VertexShader):完全可编程,实现顶点的空间变换、顶点着色等
  • 曲面细分着色器(TessellationShader):可选着色器,用于细分图元
  • 几何着色器(GeometryShader)可选着色器,执行逐图元着色操作
  • 裁剪(Clipping):将不在相机视野内的顶点裁剪掉,可配置
  • 屏幕映射(ScreenMapping):不可配置和编程



几何阶段,光栅化,逐片元这些操作都是在GPU中进行处理的,那么在讲几何阶段前,我们先聊聊为什么要用GPU渲染?

答:GPU的特点是并行性较好。当我们在对顶点数据进行处理时,他们虽然数据不同,但光照,几何运算方式啥都一样的时候,那么我们将他们放在GPU的不同工作单元上进行同时执行,速度会更快。


顶点着色器:必须完成的一个工作:将顶点坐标从模型空间转换到齐次裁剪空间(投影坐标系),同时它还有计算顶点光照的功能,这需要获取应用阶段中光源位置朝向,摄像机位置朝向,当前顶点的世界位置(获取该位置需知道顶点在模型空间的位置,模型本身的位置旋转缩放…);


顶点着色器-视图变换:模型坐标系--(模型变换) --> 世界坐标系--(视图变换)-->视图坐标系 --(投影变换)-->投影坐标系--(视口变换)-->视口坐标系 上图中的前三个变换对应MVP(model view projection)矩阵,在顶点着色器中,顶点从模型坐标系转换到投影坐标系,最后一步由Unity帮忙完成

曲面细分着色器:通过现有顶点生成更多的顶点,需获取顶点在模型中的位置信息

它是一个可选着色器,使用顶点着色器输出的顶点,按照一定规则算法生成更多顶点,将现有的网格和图元细分(其效果类似于MAYA中的“平滑”效果,模型圆滑了但面数增大了)

几何着色器(基于图元):通过现有的图元做些几何方面操作,生成更多顶点和图元,比如对现有图元所在平面生成法线,需获取现有图元顶点位置

图元是啥?

答:可以为顶点,线段,俩顶点,三角形...基础的几何图形

投影:将3D空间投到2D空间


对于顶点在裁剪空间里的位置,xyzw进行透视除法:xyz除以w完成投影。使其从投影坐标系转换到标准设备坐标系(NDC);

由于正交和透视视角下w值不同,故而呈现效果不同,正交显得像截图,透视则近大远小

顶点裁剪:消去屏幕外的顶点;

不在相机视野内的物体不需要被处理。一个图元和相机视野有3种关系:

完全在视野内:继续传递给下一阶段
部分在视野内:需要进行裁剪(Clipping),使用新的顶点来代替
完全在视野外:不会向下传递


- 投影里又说要进行除法操作,当xyz超过-1~1范围,则判断其不在范围内,

舍弃进行经典的图形学裁剪算法;

- 投影和顶点裁剪在《Shader入门精要》中都是归属于裁剪一节中,这个操作过程无法由代码控制,是硬件上固定操作,但可以自定裁剪操作进行配置

- 设备坐标系在opengl和DirectX中不一样,opengl xyz三维度取值范围都是-1~1,DirectX只有xy是-1~1,z为0~1

屏幕映射:

将顶点位置从3D坐标空间转换到2D坐标空间;

屏幕映射(ScreenMapping)的任务是把每个图元x和y坐标转换到屏幕坐标系(ScreenCoordinates)。不处理z坐标。opengl和DirectX原点不同,opengl左下方,DirectX左上方


按照《Shader入门精要》所言:屏幕映射任务是把每个图元的x和y坐标转换到屏幕坐标系。

如应用阶段为几何阶段准备数据一样,几何阶段同样会为光栅化阶段准备数据

光删化阶段

三角形设置:拿到映射于2D空间里的顶点位置,组装成三角形;

三角形遍历:寻找被三角形覆盖的所有像素的过程,知晓包含哪些2D空间像素点

《Shader入门精要》:三角形遍历阶段将检查每个像素是否被一个三角网格所覆盖,若覆盖,则生成一个片元。

片元非像素,它是包含很多状态的集合,这些状态用于计算每个像素的最终颜色,这些状态包含不限于,屏幕坐标,深度信息,从几何阶段输出的顶点信息,法线,纹理坐标等。屏幕同一个像素位置可能有对应多个三角形的不同片元(如两片重叠的交集)

抗锯齿(MSAA):

1.SSAA:

渲染到一个分辨率放大n倍的buffer:屏幕分辨率1024x1024,渲染得到buffer可为2048x2048,放大四倍,对其采样后再输出屏幕

对方打n倍的buffer下采样

2.MSAA

只有它发生在光栅化阶段

计算多个覆盖样本:覆盖测试看子采样点是否在三角形以内,遮挡测试看这个子采样度的深度和,即与深度缓存中数值进行比较,看能否通过,若能通过两测试,则说明采样点属于三角形,得到覆盖信息并保存,用于之后的着色混合

3.FXAA/TXAA

后处理技术


逐片元操作


决定可见性:

模板测试(StencilTest):将片元位置的模板值,和参考值进行比较

深度测试(DepthTest):将片元的深度值,和深度缓冲区的深度值进行比较

合并颜色。对于不透明物体,可以关闭混合,用片元着色器得到的颜色值覆盖颜色缓冲区的像素值。对于半透明物体,要混合。

如果在执行片元着色器之前就进行这些测试,可以提高GPU性能,早点知道那些片元会被舍弃。深度测试提前执行的技术Early-Z。透明度测试会导致禁用提前测试,使性能下降。

为了避免我们看到正在进行光栅化的图元,GPU会使用双重缓冲(DoubleBuffering)。渲染在幕后,在后置缓冲(BackBuffer)中,一旦完成,GPU会交换后置缓冲区和前置缓冲(FrontBuffer)的内容。

后处理

Bloom,HDR,FXAA,景深,边缘检测,径向模糊

什么是OpenGL/DirectX

OpenGL/DirectX图像应用编程接口。运行在CPU上的应用程序->调用OpenGL/DirectX等图形接口,将数据存在显存,

发出DrawCall->显卡驱动翻译成GPU能理解的代码。


什么是HLSL、GLSL、CG

着色语言(ShadingLanguage):

DirectX的HLSL(HighLevelShadingLanguage)

OpenGL的GLSL(OpenGLShadingLanguage)

NVIDIA的CG(C for Graphic)

在UnityShader中,可以选择使用哪种,但有所区别。

什么是DrawCall

DrawCall,CPU调用图像编程接口,以命令GPU进行渲染的操作。

问题一:CPU和GPU是如何实现并行工作的?

命令缓冲区(CommandBuffer),让CPU和GPU可以并行工作。CPU向其中添加命令,GPU从中读取命令,添加和读取的过程是相互独立的。


问题二:为什么DrawCall多了会影响帧率?

GPU渲染能力很强,速度往往快于CPU提交命令的速度。如果DrawCall的数量太多,CPU会耗费大量时间造成过载。


问题三:如何减少DrawCall?

这里讨论批处理方法(Batching)。把很多小的DrawCall合并成一个大的DrawCall,更适合合并静态的物体,因为只需合并一次。


为了减少DrawCall的开销,需要注意:

避免使用大量很小的网格,合并小网格

避免使用过多的材质,尽量在不同网格之间共用同一个材质

什么是固定管线渲染

固定函数的流水线(Fixed-Function Pipeline),简称为固定管线,只给开发者一些配置操作。随着时代的发展,可编程渲染管线应运而生,如顶点着色器、片元着色器。

那么,你明白什么是Shader了吗

Shader所在阶段,就是渲染流水线的一部分:

GPU流水线上一些可高度编程的阶段,由着色器编译出来的最终代码会在GPU上运行

有一些特定类型的着色器,如顶点着色器、片元着色器

依靠着色器我们可以控制流水线中的渲染细节

相关阅读:
「技美之路 第02篇」图形 1.2.1 向量基础

文献地址:感谢作者无私分享
CSDN:古守音
https://blog.csdn.net/qq_43210334/article/details/114646000
知乎:天剑行风
https://zhuanlan.zhihu.com/p/346384550
参考《Shader入门精要》·冯乐乐女神著
B站视频 “技术美术百人计划”·霜狼_may

来源:Game艺视界
原文:https://mp.weixin.qq.com/s/qbTAGtATracbyI4W_HPjxw

相关推荐

最新评论
暂无评论
参与评论

商务合作 查看更多

编辑推荐 查看更多