8.3 单光贴图和Forward 渲染路径
8.3.1单光照贴图在VertexLit和Forward下面的不同表现
在单光照贴图的情况下,Camera的RenderingPath为VertexLit时,有一个不理想的地方就是被烘焙过的静态物体,默认的材质不会受到实时光照的影响。当然,可以通过提供自定义的材质改变这一行为,但是很麻烦。在RenderingPath为Forward时,这种麻烦就不会存在了,经过烘焙物体,Unity的默认材质会继续受到实时Pixel光源的影响。
8.3.2 准备可应用于烘培的自发光材质
如图所示。与前两个场景类似,不同之处在于我们会在这里演示Forward模式下如何让材质正确地被烘焙,以及正确地应用光照贴图。
首先是绿色的自发光材质。与VertexLit模式下的自发光类似,这里只是提供了_EmissionLM属性,以及材质名前缀为Self-Illumin的Unity规范。ForwardEmission.shader的代码如下:
Shader "Self-Illumin/Lighting/LightMapping/Lab_3/ForwardEmission" { Properties { _MainTex("MainTexture",2d)="white"{} _Color ("Base Color", Color) =(1,1,1,1) _EmissionLM ("Emission (Lightmapper)", Float) = 1 } SubShader { pass{ Tags{ "LightMode"="ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdbase #include "UnityCG.cginc" #include "Lighting.cginc" uniform float4 _Color; struct vertOut{ float4 pos:SV_POSITION; float4 color:COLOR; float3 litDir:TEXCOORD0; float3 worldN:TEXCOORD1; }; vertOut vert(appdata_base v) { float4 worldV=mul(_Object2World,v.vertex); float3 worldN=mul(_Object2World,float4(SCALED_NORMAL,0)).xyz; float3 c=Shade4PointLights(unity_4LightPosX0,unity_4LightPosY0,unity_4LightPosZ0,unity_LightColor[0],unity_LightColor[1],unity_LightColor[2],unity_LightColor[3],unity_4LightAtten0,worldV.xyz,worldN); vertOut o; o.pos=mul(UNITY_MATRIX_MVP,v.vertex); o.litDir=WorldSpaceLightDir(v.vertex); o.worldN=worldN; o.color=float4(c,1.0)*_Color; return o; } float4 frag(vertOut i):COLOR { float4 c=max(0.0,dot(i.worldN,normalize(i.litDir)))*_LightColor0*_Color; return c+i.color+_Color; } ENDCG }//end pass pass{ Tags{ "LightMode"="ForwardAdd"} Blend One One CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" uniform float4 _Color; struct vertOut{ float4 pos:SV_POSITION; float3 litDir:TEXCOORD0; float3 worldN:TEXCOORD1; float atten:TEXCOORD2; }; vertOut vert(appdata_base v) { float3 worldN=mul(_Object2World,float4(SCALED_NORMAL,0)).xyz; vertOut o; o.pos=mul(UNITY_MATRIX_MVP,v.vertex); o.litDir=WorldSpaceLightDir(v.vertex); float dist=length(o.litDir); o.atten=1/(1+dist*dist*_WorldSpaceLightPos0.w); o.worldN=worldN; return o; } float4 frag(vertOut i):COLOR { float4 c=_LightColor0*_Color*max(0.0,dot(i.worldN,normalize(i.litDir)))*i.atten; return c; } ENDCG }//end pass } }
再就是一个myForward.shader,在这个自定义Shader中,我们计算了实时光,但是并没有计算光照贴图的影响。
8.3.3 在ForwardBase内计算光照贴图
然后就是myForwardLM.shader。在此Shader的ForwardBase Pass内,我们计算了unity_Lightmap的影响,这也是Unity默认的计算光照贴图的地方。之所以在ForwardBase内计算,而不是在ForwardAdd内计算,是因为在有多个Pixel光源的情况下ForwardAdd会分别被执行多次。myForwardLM.shader的代码如下:
Shader "Tut/Lighting/LightMapping/Lab_3/myForwardLM" { Properties { _MainTex("MainTexture",2d)="white"{} _Color ("Base Color", Color) =(1,1,1,1) } SubShader { pass{ Tags{ "LightMode"="ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdbase #include "UnityCG.cginc" #include "Lighting.cginc" uniform float4 _Color; sampler2D _MainTex; float4 _MainTex_ST; //scale & position of _MainTex #ifndef LIGHTMAP_OFF // sampler2D unity_Lightmap;//Beast lightmap // float4 unity_LightmapST; //scale & position of Beast lightmap #endif struct vertOut{ float4 pos:SV_POSITION; float4 color:COLOR; float3 litDir:TEXCOORD0; float3 worldN:TEXCOORD1; float2 uv:TEXCOORD2; #ifndef LIGHTMAP_OFF float2 uvLM:TEXCOORD3; #endif }; vertOut vert(appdata_full v) { float4 worldV=mul(_Object2World,v.vertex); float3 worldN=mul(_Object2World,float4(SCALED_NORMAL,0)).xyz; float3 c=Shade4PointLights(unity_4LightPosX0,unity_4LightPosY0,unity_4LightPosZ0,unity_LightColor[0],unity_LightColor[1],unity_LightColor[2],unity_LightColor[3],unity_4LightAtten0,worldV.xyz,worldN); vertOut o; o.pos=mul(UNITY_MATRIX_MVP,v.vertex); o.litDir=WorldSpaceLightDir(v.vertex); o.worldN=worldN; o.uv=v.texcoord.xy; #ifndef LIGHTMAP_OFF o.uvLM=v.texcoord1.xy; #endif o.color=float4(c,1.0)*_Color; return o; } float4 frag(vertOut i):COLOR { float4 c=max(0.0,dot(i.worldN,normalize(i.litDir)))*_LightColor0*_Color; #ifndef LIGHTMAP_OFF float3 clm=tex2D(_MainTex,i.uv).rgb*DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap,i.uvLM)); c+=float4(clm,1.0); #endif return (c+i.color); } ENDCG }//end pass pass{ Tags{ "LightMode"="ForwardAdd"} Blend One One CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" uniform float4 _Color; struct vertOut{ float4 pos:SV_POSITION; float3 litDir:TEXCOORD0; float3 worldN:TEXCOORD1; float atten:TEXCOORD2; }; vertOut vert(appdata_base v) { float3 worldN=mul(_Object2World,float4(SCALED_NORMAL,0)).xyz; vertOut o; o.pos=mul(UNITY_MATRIX_MVP,v.vertex); o.litDir=WorldSpaceLightDir(v.vertex); float dist=length(o.litDir); o.atten=1/(1+dist*dist*_WorldSpaceLightPos0.w); o.worldN=worldN; return o; } float4 frag(vertOut i):COLOR { float4 c=_LightColor0*_Color*max(0.0,dot(i.worldN,normalize(i.litDir)))*i.atten; return c; } ENDCG }//end pass } }
8.3.4Forward渲染路径下烘焙之后的实时照明
现在可以烘焙一下了,其结果如图下所示。首先我们可以注意到,相比于VertexLit模式下,烘焙过的物体,默认材质不再受到实时光源的影响,我们在Forward模式下的黄色实时Pixel光源仍对烘焙过的物体有着照明作用。但是可以尝试,在Camera为Forward模式时,将实时光源改为Not
Important Vertex光源,是不会对已烘焙过的物体产生照明影响的。其次,被烘焙过的光源不再对烘焙过的物体产生照明,比如场景中的自色光源(光源1)和绿色光源(光源2),我们可以通过GUI移动或者改变颜色,但是对于烘焙过的物体,它不再产生照明。除非我们将光源Lightmapping模式改为RealTime
Only,它才会对烘焙过的物体产生照明。