渲染路径
在Unity中,渲染路径决定了光照是如何应用到UnityShader中的。在UnityShader中需要为每一个Pass指定渲染路径,该Shader的光照计算才能正常进行。
Pass{
Tags { "LightModel" = "ForwardBase"}
}
Unity5.0之后主要有四种渲染路径:前向渲染路径(Forward)、延迟渲染路径(Deferred)和旧版顶点照明渲染路径(Legacy Vertex Lit)、旧版延迟渲染路径(Legacy Deferred(light prepass)),可以在Edit->Projecting-Graphics中进行设置。设置渲染路径,Unity会把光照属性按渲染流程准备好,可以通过Unity提供的内置光照变量来访问这些属性。
标 签 名 | 描 述 |
---|---|
Always | 不管使用哪种渲染路径,该Pass总是会被渲染,但不会计算任何光照 |
ForwardBase | 用于前向渲染。该Pass会计算环境光、最重要的平行光、逐顶点/SH光源和Lightmaps |
ForwardAdd | 用于前向渲染。该Pass会计算额外的逐像素光源,每个Pass对应一个光源 |
Deferred | 用于延迟渲染。该Pass会计算额外的逐像素光源,每个Pass对应一个光源 |
ShadowCaster | 把物体的深度信息渲染到阴影映射纹理(shadowmap)或一张深度纹理中 |
PrepassBase | 用于遗留的延迟渲染。该Pass会渲染法线和高光反射的指数部分 |
PrepassFinal | 用于遗留的延迟渲染。该Pass通过合并纹理、光照和自发光来渲染得到最后的颜色 |
Vertex、VertexLMRGBM和VertexLM | 用于遗留的顶点照明选软 |
Unity中的前向渲染
原理: 渲染对象的渲染图元时,计算两个缓冲区的信息(颜色缓冲区和深度缓冲区),利用深度缓冲来决定一个片元是否可见,如果可见就更新颜色缓冲区的颜色值,否则就丢弃。
前向渲染路径处理光照的三种方式: 逐顶点处理、逐像素处理、球谐函数(SH)处理。
决定一个光源使用哪种处理模式取决于它的类型和渲染模式。光源类型指的是平行光还是其他类型的光源,渲染模式指的是Render Mode变量,可以设置成Important或者Not Important。判断规则如下:
- 场景中最亮的平行光按逐像素处理。
- Render Mode 为 Not Important的光源,会逐顶点或者SH处理。
- Render Mode 为 Important的光源,会逐像素处理。
- 如果上面得到的逐像素光源小于Quality Setting中的逐像素光源数量,会有过更多光源以逐像素的方式进行渲染。
前向渲染有两种Pass:
- Base Pass。一般一个Unity Shader定义一个(双面渲染的话两个),执行一次。
- Additional Pass。如果有N个光源影响该物体,就会执行N次。
前置渲染可以使用的内置光照变量:
名 称 | 类 型 | 描述 |
---|---|---|
_LightColor | float4 | 该Pass处理的逐像素光源的颜色 |
_WorldSpaceLightPos0 | float4 | _WorldSpaceLightPos0.xyz是该pass处理的逐像素光源的位置。如果该光源是平行光,那么_WorldSpaceLightPos0.w是0,其他光源类型w值为1 |
_LightMatrix0 | float4x4 | 从世界空间到光源空间的变换矩阵。可以用于采样的cookie和光强衰减纹理 |
unity_4LightPosX0,unity_4LightPosY0,unity_4LightPosZ0 | float4 | 仅用于Base Pass。前4个非重要的电光源在世界空间中的位置 |
unity_4LightAtten0 | float4 | 仅用于Base Pass。存储了前4个非常重要的电光源的衰减因子 |
unity_LightColor | half4[4] | 仅用于Base Pass。存储了前4个非重要的点光源的颜色 |
顶点照明渲染路径(Vertex)
即逐顶点计算光照。硬件配置要求最低,运算性能最高,效果最差的类型,不支持逐像素效果(如:阴影、法线映射、高精度高光反射等)。
顶点照明渲染可以使用的内置变量和函数:
名 称 | 类 型 | 描 述 |
---|---|---|
unity_LightColor | half4[8] | 光源颜色 |
unity_LightPosition | float4[8] | xyz分量是视角空间中的光源位置。如果光源是平行光,那么z分量值为0,其他光源类型z分量值为1 |
unity_LightAtten | half4[8] | 光源衰减因子。如果光源是聚光灯,x分量是cos(spotAngle/2),y分量是1/cos(spotAngle/4);如果是其他类型的官员,x分量是-1,uy分量是1.z分量是衰减的平方w分量是光源范围开根号的结果 |
unity_SpotDirection | float4[8] | 如果光源是聚光灯的话,值为视角空间的聚光灯的位置;如果是其他类型的官员,值为(0,0,1,0) |
延迟渲染路径(Deferred)
使用额外的缓冲区(G缓冲,Geometry-buffer),存储离摄像机最近的表明的其他信息(表面法线、位置、材质属性等)。避免前向渲染在大量实时光照下时的性能急速下降。实现就用两个Pass,与场景光源数目无关,而是和屏幕空间大小有关。
主要包含两个Pass:
- 第一个Pass:只计算哪些片元可见(深度缓冲技术),可见的就存到G缓冲中。
- 第二个Pass:利用G缓冲中的片元信息,计算光照。
缺点:
- 不支持真正的抗锯齿(anti-aliasing)功能。
- 不能处理半透明物体。
- 显卡必须支持MRT(Multiple Render Targets)、Shader Mode3.0及以上、深度渲染纹理以及双面的模版缓冲。
光照衰减
Unity默认使用纹理查找的方式来计算逐像素的点光源和聚光灯的衰减。使用这种方法可以在一定程度上提升性能,而且得到的效果大部分情况下都是良好的,但是存在一些弊端:
- 需要预处理得到采样为例,而且纹理的大小也会影响衰减的精度。
- 不直观,同时也不方便,因此一旦把数据存储到查找表中,我们就无法使用其他的数学公式来计算衰减。
1.光照衰减纹理
UNITY_ATTEN_CHANNEL:可以得到衰减纹理中衰减值所在分量。
光照衰减纹理
需要预处理得到采样纹理,纹理的大小会影响衰减精度。类似渐变纹理,(0, 0)表示与光源重合时的衰减值,(1, 1)为离光源空间最大距离的衰减值。
Unity内部使用_LightTexture0的纹理来计算光源衰减。利用_LightMatrix0变换矩阵来计算点在光源空间的位置。
// 计算衰减纹理坐标值
float3 lightCoord = mul(_LightMatrix0, float4(i.worldPosition, 1)).xyz;
// 根据坐标对纹理采样,用dot点乘就是直接做距离的平方,`.rr`的意思就是取得到rgb值的r作为一个新的二维坐标即(r, r)。
fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
2. 数学公式计算衰减,使用的是线性衰减:
flaot distance = length(_WorldSpaceLightPos0.xyz) - i.worldPosition.xyz);
atten = 1.0 / distance;
阴影
Pass {
Tags {"LightMode"="ShadowCaster"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
struct v2f {
V2F_SHADOW_CASTER;
};
v2f vert (a2v v){
v2f o;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
return o;
}
fixed4 frag(v2f i) : SV_Target {
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}