【Unity Shader入门精要】渲染流水线

原作者博客链接:http://blog.****.net/candycat1992/article/ 
书籍链接:http://product.dangdang.com/23972910.html

目录

 应用阶段(CPU、输出渲染图元)

摄像机

模型

Mesh Filter

Mesh Renderer

Skinned Mesh Renderer

光源

(CPU和GPU通信)

1、导入Mesh信息

2、设置渲染状态(Mesh Renderer、光源、相机)

3、调用Draw Call(通常是一个Pass)

CommandBuffer(CPU和GPU并行工作)

DrawCall多了影响速率?

如何减少DrawCall?

OpenGL或DirectX

图像编程接口

显卡驱动

Shading Language

几何阶段(输出屏幕空间的顶点信息)

顶点着色器

曲面细分着色器

几何着色器

裁剪

屏幕映射

光栅化阶段(最终画面)

三角形设置(画线算法)

三角形遍历(扫描线相关)

(Early-Z)

片元着色器

逐个片元操作(测试+混合)


【Unity Shader入门精要】渲染流水线

 应用阶段(CPU、输出渲染图元)

  • 摄像机

https://docs.unity3d.com/Manual/class-Camera.html

【Unity Shader入门精要】渲染流水线

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渲染

  • 在第一个Pass中,我们不进行任何光照计算,而是仅仅计算哪些片元是可见的,这主要是通过深度缓冲技术来实现,当发现一个片元是可见的,我们就把它的相关信息存储到G缓冲区中。
  • 在第二个Pass中,我们利用G缓冲区的各个片元信息,例如表面法线、视角方向、漫反射系数等,进行真正的光照计算。
Target Texture

将相机的画面输出到一张贴图中,通常作为后处理使用。

设置了相关RT后,该相机无法再将结果输出到屏幕上。

       
  • 模型

【Unity Shader入门精要】渲染流水线

  • 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

【Unity Shader入门精要】渲染流水线

  • 光源

https://docs.unity3d.com/Manual/class-Light.html

【Unity Shader入门精要】渲染流水线

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通信)

【Unity Shader入门精要】渲染流水线

  • 1、导入Mesh信息

将几何阶段的数据从硬盘(HDD)加载到内存(RAM),再到显存(VRAM)。

因为显卡对显存的访问速度更快,而且大多数情况下显卡没有访问内存(RAM)的权限

  • 2、设置渲染状态(Mesh Renderer、光源、相机)

  • 3、调用Draw Call(通常是一个Pass)

  • CommandBuffer(CPU和GPU并行工作)

【Unity Shader入门精要】渲染流水线

如果没有流水线化,那么CPU需要等到GPU完成上一个渲染任务才能再次发送渲染命令。但这种方法显然会造成效率低下。使用一个命令缓冲区(Command Buffer)可以让CPU和GPU并行工作。

命令缓冲区包含了一个命令队列,由CPU向其中添加命令,而由GPU从中读取命令,添加和读取的过程是互相独立的。命令缓冲区使得CPU和GPU可以相互独立工作。当CPU需要渲染一些对象时,它可以向命令缓冲区中添加命令,而当GPU完成了上一次的渲染任务后,它就可以从命令队列中再取出一个命令并执行它。

命令缓冲区中的命令有很多种类,而Draw Call是其中一种,其他命令还有改变渲染状态等(例如改变使用的着色器,使用不同的纹理等)。

  • DrawCall多了影响速率?

【Unity Shader入门精要】渲染流水线

在每次调用Draw Call之前,CPU需要向GPU发送很多内容,包括数据、状态和命令等。在这一阶段,CPU需要完成很多工作,例如检查渲染状态等。而一旦CPU完成了这些准备工作,GPU就可以开始本次的渲染。GPU的渲染能力是很强的,渲染200个还是2000个三角网格通常没有什么区别,因此渲染速度往往快于CPU提交命令的速度。如果Draw Call的数量太多,CPU就会把大量时间花费在提交Draw Call上,造成CPU的过载。

  • 如何减少DrawCall?

【Unity Shader入门精要】渲染流水线

尽管减少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(斑点、碎片),来生成一个三角形网格。

【Unity Shader入门精要】渲染流水线

【Unity Shader入门精要】渲染流水线

  • 几何着色器

几何着色器以一个或多个表示为一个单独基本图形(primitive)即图元的顶点作为输入,比如可以是一个点或者三角形。几何着色器在将这些顶点发送到下一个着色阶段之前,可以将这些顶点转变为它认为合适的内容。

  • 裁剪

视椎体裁剪 在NDC中剔除不在范围内的图元。【Unity Shader入门精要】渲染流水线
前/后向剔除
  • Cull Front
  • Cull Back
  • Cull Off
  • 屏幕映射

【Unity Shader入门精要】渲染流水线

光栅化阶段(最终画面)

  • 三角形设置(画线算法)

【Unity Shader入门精要】渲染流水线

上阶段得到的是三角形的顶点位置信息,为了得到其覆盖的像素信息,需要首先计算三角形边界的表达式。

  • 三角形遍历(扫描线相关)

【Unity Shader入门精要】渲染流水线

检查像素是否被三角形覆盖,并对深度信息、法线、纹理坐标等进行差值处理。

  • (Early-Z)

目的:提前进行深度测试,知道哪些片元会被舍弃,从而不在片元着色器中对其进行计算,以提高GPU性能。

问题:如前方物体进行了透明度测试丢弃了部分片元,而透过这些片元部分的后方物体因为没有通过深度测试而无法显示。

解决:现代的GPU会判断片元着色器中的操作是否和提前测试发生冲突,如果有冲突,就会禁用提前测试。但是,这样也会造成性能上的下降,因为有更多片元需要被处理了。这也是透明度测试会导致性能下降的原因。

  • 片元着色器

  • 逐个片元操作(测试+混合)

【Unity Shader入门精要】渲染流水线

 DoubleBuffer