渲染路径(Rendering Path)
渲染路径决定了光照如何应用到Shader中,决定了把光源信息和处理后的光照信息放到那些数据中。


四种渲染路径设置 :
- Deferred(延迟渲染)
- Forward(前向渲染)
- Legacy Vertex Lit(遗留顶点照明渲染)
- Legacy Deferred(light prepass)(遗留延迟渲染)
Unity会优先执行Shader中符合当前渲染路径的Pass,例如设置了Deferred(延迟渲染)。
- 优先执行Shader中Pass的LightMode为Deferred(延迟渲染)路径的Pass。
- 如果没有符合条件的Pass,寻找低一级Shader中Pass的LightMode为Forward(前向渲染)路径的Pass。
- 如果没有符合条件的Pass,寻找低一级Shader中Pass的LightMode为Legacy Vertex Lit(遗留顶点照明渲染)路径的Pass。
Shader在同一时间内,只会执行一个渲染路径下的Pass。
Shader指定某个Pass使用什么渲染路径,通过标签LightMode设置。
标签 | 描述 |
Always | 不管使用什么渲染路径,该Pass都会被渲染,但不会计算任何光照 |
ForwardBase | 用于前向渲染,该Pass会计算环境光,最重要的平行光,逐顶点/SH光源和Lightmaps |
ForwardAdd | 用于前向渲染,该Pass会计算额外的逐像素光源,每个Pass对应一个光源 |
Deferred | 用于延迟渲染,该Pass会渲染G缓冲(G-buffer) |
ShadowCaster | 把物体的深度信息渲染到阴影映射纹理或一张深度纹理中 |
PrepassBase | 用于遗留的延迟渲染,该Pass会渲染法线和高光反射的指数部分 |
PrepassFinal | 用于遗留的延迟渲染,该Pass通过合并纹理,光照和自发光来渲染得到最后的颜色 |
Vertex,VertexLMRGBM,VertexLM | 用于遗留的顶点照明渲染 |
当Shader中有"LightMode" = "Always"时:
- 当Shader中只有 "LightMode" = "Always"的Pass时,Shader中所有Pass无论在什么渲染路径下都按先后顺序执行。
- 当Shader处于Deferred(延迟渲染)路径下时候,Shader中的"LightMode" = "Always"的Pass不被执行。
- 当Shader处于Forward(前向渲染),和Legacy Vertex Lit(遗留顶点照明渲染)渲染路径下时,含有"LightMode" = "Always"的Pass的Shader,会根据Shader中其他Pass是否能在当前渲染路径下执行来决定"LightMode" = "Always"的Pass是否能被执行。如果其他Pass有能够执行的,"LightMode" = "Always"的Pass也能被执行。如果其他所有Pass不能被执行, "LightMode" = "Always"的Pass也不能被执行。 所有能执行的Pass会根据前后顺序执行,Shader中"LightMode" = "Always"的Pass之外的所有Pass都不执行,那"LightMode" = "Always"的Pass也不被执行。
Unity5.x后如果设置了前向渲染路径,Pass没有设置LightMode标签,就会被当做Legacy Vertex Lit(遗留顶点照明渲染)等同的Pass。
前向渲染路径
1.前向渲染路径原理
每进行一次完成的前向渲染,需要在一个Pass中渲染图元,并计算两个缓冲区。深度缓冲区决定片元是否可见,可见就更新颜色缓冲区颜色值。
对于每个逐像素光源,都需要一个Pass计算光照。一个物体如果受M个光源影响,就需要执行M个Pass,然后在帧缓冲中把这些光照结果混合得到最终颜色。如果一个场景有N个模型,每个模型受到M个光源影响,渲染这个场景需要 N * M 个Pass。因此渲染引擎会限制每个物体的逐像素光照数目。
2.Unity中的前向渲染
Unity中前向渲染有3中处理光照的方式:
- 逐顶点处理
- 逐像素处理
- 球谐函数处理
光源的类型和渲染模式决定了它被怎么处理。
- 光源类型是指该光源是平行光还是其他类型光源。
- 光源渲染模式是指该光源Render Mode属性
Pixel Light Count数量(Quality Setting)的光源会按逐像素处理,然后最多四个光源按逐顶点处理,剩下的按SH方式处理。
- 场景中最亮的平行光总是按逐像素处理
- Render Mode被设置成Not Important的光源,按逐顶点或SH处理。
- Render Mode被设置成Important的光源,按逐像素处理。
- 根据以上规则,逐像素光源大于Pixel Light Count数量(Quality Setting),光源会被当做逐顶点或SH方式处理。
- 最亮的平行光Render Mode被设置为Not Important一样会被当做逐顶点或SH方式处理。
前向渲染有两种Pass:
- Base Pass(LightMode = "ForwardBase")
- AdditionPass(LightMode = "ForwardAdd")
Unity前向渲染(LightMode = ForwardBase/ForwardAdd)内置光照变量和函数 :
变量名称 | 类型 | 描述 |
_LightColor0 | float4 | 该Pass处理的逐像素光源的颜色 |
_WorldSpaceLightPos0 | float4 | 该Pass处理的逐像素光照位置。如果_WorldSpaceLightPos0.w = 0,表示该光源是平行光,_WorldSpaceLightPos0.xyz是平行光的方向向量,反之表示光源为点光源,_WorldSpaceLightPos0.xyz为光源的世界坐标 |
_LightMatrix0 | float4x4 | 从世界空间到光源空间的变换矩阵,可以用于采样cookie和光强衰减纹理 |
unity_4LightPosX0 unity_4LightPosY0 unity_4LightPosZ0 |
float4 | 仅用于Base Pass,前4个非重要的点光源在世界空间中的位置 |
unity_4LightAtten0 | float4 | 仅用于Base Pass,存储了前4个非重要的点光源的衰减因子 |
unity_LightColor | half[4] | 仅用于Base Pass,存储了前4个非重要的点光源的颜色 |
函数名 | 描述 |
float3 WorldSpaceLightDir(float4 v) | 仅可用于前向渲染,输入一个模型空间的顶点位置,返回世界空间中从该点到光源的光照方向。内部实现使用了UnityWorldSpaceLightDir函数,没有归一化。 |
float3 UnityWorldSpaceLightDir(float4 v) | 仅可用于前向渲染,输入一个世界空间下的顶点位置,返回世界空间中从该点到光源的光照方向,没有被归一化。 |
float3 ObjSpaceLightDir(float4 v) | 仅可用于前向渲染,输入一个模型空间下的顶点位置,返回模型空间中从该点到光源的光源方向,没有被归一化。 |
float3 Shade4PointLights(...) | 仅可用于前向渲染,计算四个点光源的光照。它的参数是已经内置的光照数据。通常就像上表的 unity_4LightPosX0, unity_4LightPosY0 unity_4LightPosZ0,unity_4LightAtten0,unity_LightColor等等。 通常使用这个函数计算逐顶点的光照。 |
顶点照明渲染路径
通常只使用一个Pass(LightMode = Vertex),进行逐顶点光照计算。
顶点照明渲染中,最多可以计算8个逐顶点光源的光照。
顶点照明渲染路径中可用的内置变量和函数
变量名称 | 类型 | 描述 |
unity_LightColor | half4[8] | 光源颜色(如果某个物体所受光照小于8个,剩余的光源颜色为黑色。) |
unity_LightPosition | float4[8] | 如果光源是平行光,-xyz分量是视角(相机)空间中的光源方向,z分量值为0。其他光源类型xyz分量是视角空间中的光源位置,其他光源类型为z分量值为1. |
unity_LightAtten | half4[8] | 光源衰减因子,如果光源是聚光灯,x分量是cos(spotAngle/2),y分量是1/cos(spotAngle/4);如果是其他类型的光源,x分量是-1,y分量是1。z分量是衰减的平方,w分量是光源范围开根号的结果。 |
unity_SpotDirection | float4[8] | 如果光源是聚光灯,值为视角空间的聚光灯的位置。如果是其他类型的光源,值为(0,0,1,0) |
函数名 | 描述 |
float3 ShadeVertexLights(float4 vertex,float3 normal) | 输入模型空间中的顶点位置和法线,计算四个逐顶点光源的光照以及环境光。内部实现实际上调用了ShadeVertexLightsFull函数 |
float3 ShadeVertexLightsFull(float4 vertex,float3 normal,int lightCount,bool spotLight) | 输入模型空间中的顶点位置和法线,计算lightCount个光源的光照以及环境光。如果spotLight值为true。那么这些光源会被当做聚光灯处理。虽然结果更精确,但是计算更加耗时。否则按点光源处理。 |
延迟照明渲染路径(移动设备不支持)
当场景中包含大量实时光源时,前向渲染性能急速下降,这时候就可以使用延迟渲染路径。除了前向渲染使用的颜色缓冲区和深度缓冲区外,延迟渲染还会利用额外的G缓冲区。G缓冲区存储了表面法线,位置等用于光照计算的材质属性等。
1.延迟渲染原理
- Pass1(不进行光照计算,通过深度缓冲,计算片元可见性)
把物体的漫反射颜色,高光反射颜色,平滑度,法线,自发光和深度等信息渲染到屏幕空间的G缓冲区中。对于每个物体,这个Pass只会执行一次。
- Pass2(将Pass1存储到G缓冲区中可见片元,取出计算光照)
取出Pass1存到G缓冲区中的片元信息,计算光照,得到最终光照颜色。再存到帧缓冲区。
延迟渲染效率跟场景中光源数量没关系。而和我们的屏幕空间大小有关。我们需要的信息都存储在G缓冲区中,而缓冲区可以理解成一张张2D图片。屏幕越大,计算越耗时。
2.Unity中延迟渲染
Unity5.x之前使用遗留的的延迟渲染路径。Unity5.x之后使用延迟渲染路径。
延迟渲染缺点:
- 不支持真正的抗锯齿功能
- 不能处理半透明物体
- 对显卡有要求
3.G缓冲区的渲染纹理
- RT0: 格式ARGB32,RGB存储漫反射颜色,A通道没使用。
- RT1: 格式ARGB32,RGB存储高光反射颜色,A通道存储高光反射的指数部分。
- RT2: 格式ARGB2101010,RGB存储法线,A通道没使用。
- RT3: 格式ARGB32(非HDR)或 ARGBHalf(HDR),存储自发光+lightmap+反射探针。
- 深度缓冲和模板缓冲
4.延迟渲染路径可访问内置变量和函数
名称 | 类型 | 描述 |
_LightColor | float4 | 光源颜色 |
_LightMatrix0 | float4x4 | 从世界空间到光源空间的变换矩阵,可以用于采样cookie和光前衰减纹理 |
4个渲染路径对比
特性 | 延迟渲染路径 | 正向渲染路径 | 传统延迟渲染 | 传统顶点光照 |
---|---|---|---|---|
逐像素照明(法线贴图、light cookie) | √ | √ | √ | x |
实时阴影 | √ | 有注意事项 | √ | x |
反射探头(Reflection Probe) | √ | √ | x | x |
深度和法线缓冲区 | √ | 额外的渲染通道pass | √ | x |
Soft Particles | √ | x | √ | x |
半透明物体 | x | √ | x | √ |
抗锯齿 | x | √ | x | √ |
Light Culling Masks | Limited | √ | Limited | √ |
光照保真度 | 所有 per-pixel | 一些 per-pixel | 所有 per-pixel | 所有 per-vertex |
实践:1.前向渲染路径
Shader "Chan/Chapter9_ForwardRendering" {
Properties
{
_Diffuse("Diffuse",Color) = (1,1,1,1)
_Specular("Specular",Color) = (1,1,1,1)
_Gloss("Gloss",Range(8.0,256)) = 20
}
SubShader
{
//Base Pass
Tags{"RenderType" = "Opaque"}
Pass
{
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
//保证光照衰减等光照变量可以被正确赋值
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct a2v
{
float4 vertex:POSITION;
float3 normal:NORMAL;
&