【Unity Shader入门精要】渲染流水线
原作者博客链接:http://blog.****.net/candycat1992/article/
书籍链接:http://product.dangdang.com/23972910.html
目录
应用阶段(CPU、输出渲染图元)
-
摄像机
https://docs.unity3d.com/Manual/class-Camera.html
Depth | 数值越大渲染优先级越高(多个摄像机的情况下) | ||
Occlusion Culling | Occlusion Culling:防止OverDraw(与视椎体剔除不同) https://docs.unity3d.com/Manual/OcclusionCulling.html | ||
Rendering Path | 通常一个项目一个渲染路径,如果当前显卡不支持所选择的渲染路径,Unity会自动使用更低一级的渲染路径。如不支持Defered则使用Forward。 | ||
Use Player Settings | 用项目统一设置的渲染路径 | ||
Vertex Lit | 逐顶点计算光照,计算速度最快,但效果最差 | ||
Forward | ForwardBase | 计算环境光、最亮的平行光、逐顶点/SH光源和Lightmaps | |
ForwardAdd | 计算额外的逐像素光源,一个光一个Pass | ||
Defered |
除了渲染颜色缓冲和深度缓冲外,还会利用GBuffer,其包含我们所关心的表面及其几何信息。 延迟渲染主要使用两个Pass渲染
|
||
Target Texture |
将相机的画面输出到一张贴图中,通常作为后处理使用。 设置了相关RT后,该相机无法再将结果输出到屏幕上。 |
||
-
模型
-
Mesh Filter
https://docs.unity3d.com/Manual/class-Mesh.html
顶点位置、法线、切线、顶点色等;
-
Mesh Renderer
https://docs.unity3d.com/Manual/class-MeshRenderer.html
Cast Shadows | 如何处理阴影 | |
On | 向其他物体投射阴影 | |
Off | 不向其他物体投射阴影 | |
Two Side | 两个面都投射阴影,GI和Lightmap不支持双面投射阴影 | |
Shadows Only | 只投射阴影,自己不显示 | |
Receive Shadows | 接收其他物体投射过来的阴影。 | |
Light Probes | ||
Reflection Probes | ||
Anchor Override | ||
Lightmap | ||
-
Skinned Mesh Renderer
https://docs.unity3d.com/Manual/class-SkinnedMeshRenderer.html
-
光源
https://docs.unity3d.com/Manual/class-Light.html
Mode | Realtime | |
Mixed | ||
Baked | ||
Indirect Multiplier | 间接光(在物体间弹射的光)的强度。 | |
Shadow Type | No Shadow、Hard Shadow、Soft Shadow | |
Cookie | 指定一个纹理,如用来做窗户投射的遮罩。 | |
Draw Halo | ||
Flare | ||
Render Mode | Auto | |
Important | 逐像素进行光照计算 | |
Not Important | 逐顶点进行光照计算 | |
(CPU和GPU通信)
-
1、导入Mesh信息
将几何阶段的数据从硬盘(HDD)加载到内存(RAM),再到显存(VRAM)。
因为显卡对显存的访问速度更快,而且大多数情况下显卡没有访问内存(RAM)的权限。
-
2、设置渲染状态(Mesh Renderer、光源、相机)
-
3、调用Draw Call(通常是一个Pass)
-
CommandBuffer(CPU和GPU并行工作)
如果没有流水线化,那么CPU需要等到GPU完成上一个渲染任务才能再次发送渲染命令。但这种方法显然会造成效率低下。使用一个命令缓冲区(Command Buffer)可以让CPU和GPU并行工作。
命令缓冲区包含了一个命令队列,由CPU向其中添加命令,而由GPU从中读取命令,添加和读取的过程是互相独立的。命令缓冲区使得CPU和GPU可以相互独立工作。当CPU需要渲染一些对象时,它可以向命令缓冲区中添加命令,而当GPU完成了上一次的渲染任务后,它就可以从命令队列中再取出一个命令并执行它。
命令缓冲区中的命令有很多种类,而Draw Call是其中一种,其他命令还有改变渲染状态等(例如改变使用的着色器,使用不同的纹理等)。
-
DrawCall多了影响速率?
在每次调用Draw Call之前,CPU需要向GPU发送很多内容,包括数据、状态和命令等。在这一阶段,CPU需要完成很多工作,例如检查渲染状态等。而一旦CPU完成了这些准备工作,GPU就可以开始本次的渲染。GPU的渲染能力是很强的,渲染200个还是2000个三角网格通常没有什么区别,因此渲染速度往往快于CPU提交命令的速度。如果Draw Call的数量太多,CPU就会把大量时间花费在提交Draw Call上,造成CPU的过载。
-
如何减少DrawCall?
尽管减少Draw Call的方法有很多,但我们这里仅讨论使用批处理(Batching)的方法。
由于提交大量很小的Draw Call会造成CPU的性能瓶颈,即CPU把时间都花费在准备Draw Call的工作上了。那么,一个很显然的优化想法就是把很多小的DrawCall合并成一个大的Draw Call,这就是批处理的思想。
需要注意的是,由于我们需要在CPU的内存中合并网格,而合并的过程是需要消耗时间的。因此,批处理技术更加适合于那些静态的物体。当然,我们也可以对动态物体进行批处理。但是,由于这些物体是不断运动的,因此每一帧都需要重新进行合并然后再发送给GPU,这对空间和时间都会造成一定的影响。
-
OpenGL或DirectX
-
图像编程接口
开发者直接访问GPU是一件非常麻烦的事情,我们可能需要和各种寄存器、显存打交道。而图像编程接口在这些硬件的基础上实现了一层抽象。OpenGL和DirectX就是这些图像应用编程接口,这些接口用于渲染二维或三维图形。可以说,这些接口架起了上层应用程序和底层GPU的沟通桥梁。一个应用程序向这些接口发送渲染命令,而这些接口会依次向显卡驱动(Graphics Driver)发送渲染命令。
-
显卡驱动
显卡驱动是真正知道如何和GPU通信的角色,正是它们把OpenGL或者DirectX的函数调用翻译成了GPU能够听懂的语言,同时它们也负责把纹理等数据转换成GPU所支持的格式。一个比喻是,显卡驱动就是显卡的操作系统。
-
Shading Language
HLSL | DirectX | 几乎都是微软自己的产品 |
由微软控制着色器的编译,就算使用了不同的硬件,同一个着色器的编译结果也是一样的(前提是版本相同)。而在其他平台上没有可以编译HLSL的编译器。 |
GLSL | OpenGL | 跨平台性 |
由于OpenGL没有提供着色器编译器,而是由显卡驱动来完成着色器的编译工作。 GLSL的编译结果将取决于硬件供应商。世界上有很多硬件供应商—NVIDIA、ATI等,他们对GLSL的编译实现不尽相同,这可能会造成编译结果不一致的情况,因为这完全取决于供应商的做法。 |
CG | Nvidia | 真正意义上的跨平台 |
它会根据平台的不同,编译成相应的中间语言。 CG语言的跨平台性很大原因取决于与微软的合作,这也导致CG语言的语法和HLSL非常相像,CG语言可以无缝移植成HLSL代码。 但缺点是可能无法完全发挥出OpenGL的最新特性。 |
几何阶段(输出屏幕空间的顶点信息)
-
顶点着色器
模型空间 | 左手坐标系 | |
世界空间 | 左手坐标系 | |
相机空间 | 右手坐标系 |
-
曲面细分着色器
https://docs.unity3d.com/Manual/SL-SurfaceShaderTessellation.html
细分曲面着色器可以使用一个新的几何图元类型,称为patch(斑点、碎片),来生成一个三角形网格。
-
几何着色器
几何着色器以一个或多个表示为一个单独基本图形(primitive)即图元的顶点作为输入,比如可以是一个点或者三角形。几何着色器在将这些顶点发送到下一个着色阶段之前,可以将这些顶点转变为它认为合适的内容。
-
裁剪
视椎体裁剪 | 在NDC中剔除不在范围内的图元。 |
前/后向剔除 |
|
-
屏幕映射
光栅化阶段(最终画面)
-
三角形设置(画线算法)
上阶段得到的是三角形的顶点位置信息,为了得到其覆盖的像素信息,需要首先计算三角形边界的表达式。
-
三角形遍历(扫描线相关)
检查像素是否被三角形覆盖,并对深度信息、法线、纹理坐标等进行差值处理。
-
(Early-Z)
目的:提前进行深度测试,知道哪些片元会被舍弃,从而不在片元着色器中对其进行计算,以提高GPU性能。
问题:如前方物体进行了透明度测试丢弃了部分片元,而透过这些片元部分的后方物体因为没有通过深度测试而无法显示。
解决:现代的GPU会判断片元着色器中的操作是否和提前测试发生冲突,如果有冲突,就会禁用提前测试。但是,这样也会造成性能上的下降,因为有更多片元需要被处理了。这也是透明度测试会导致性能下降的原因。
-
片元着色器
-
逐个片元操作(测试+混合)
DoubleBuffer