剛開(kāi)始接觸unity shader的時(shí)候發(fā)現(xiàn)自己寫(xiě)的unlit shader不支持烘焙場(chǎng)景中的光沾凄,無(wú)論烘焙場(chǎng)景什么樣,自己寫(xiě)的shader根本不接受烘焙的影響撒蟀,而unity自帶的standard shader及其他一系列都完美支持,那自己寫(xiě)的shader要如何像unity自帶shader那樣支持烘焙呢手负?
其實(shí)很簡(jiǎn)單姑尺,幾行代碼就能搞定。讓我們先來(lái)看一個(gè)場(chǎng)景
這是我在unity中搭建了一個(gè)臨時(shí)場(chǎng)景并烘焙好(具體過(guò)程可看這個(gè)視頻)统捶,然后這個(gè)猴臉用了standard shader柄粹,現(xiàn)在我把他換成我自己寫(xiě)的一個(gè)最簡(jiǎn)單的diffuse shader,只輸出貼圖顏色驻右,然后就成了這樣
貼圖丑了點(diǎn),大家無(wú)視好了兑凿。茵瘾。。
現(xiàn)在發(fā)現(xiàn)拗秘,咦,為什么不像剛剛那樣在猴臉的左側(cè)有橙黃色的光疊加而右側(cè)有藍(lán)光的疊加呢扮匠?這是因?yàn)槟菐仔猩衿娴拇a沒(méi)有加,我們現(xiàn)在來(lái)看要加什么棒搜。
Shader "Unlit/Monkey"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color ("Main Color",Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float2 uv1 : TEXCOORD1;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
#ifdef LIGHTMAP_ON
float2 uvLM : TEXCOORD1;
#endif
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
#ifdef LIGHTMAP_ON
o.uvLM = v.uv1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
#endif
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
col *= _Color;
#ifdef LIGHTMAP_ON
fixed3 lm = DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap,i.uvLM));
col.rgb *= lm;
#endif
return col;
}
ENDCG
}
}
}
首先第一條要在CGPROGRAM
后添加一行
#pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON
這樣shader就可以判斷l(xiāng)ightmap開(kāi)沒(méi)開(kāi)了力麸。
然后在vertex shader里添加
#ifdef LIGHTMAP_ON
o.uvLM = v.uv1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
#endif
把lightmap的uv準(zhǔn)備好之后使用。
這里這樣寫(xiě)是和unity的源碼TRANSFORM_TEX
一致
// Transforms 2D UV by scale/bias property
#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
不過(guò)不能直接調(diào)用TRANSFORM_TEX
是因?yàn)?code>unity_LightmapST這個(gè)東西在ST
之前沒(méi)有下劃線(xiàn)_
闺鲸,所以沒(méi)法用現(xiàn)成的方法了埃叭。
再來(lái)到fragment shader里就和獲取主貼圖一樣操作了
#ifdef LIGHTMAP_ON
fixed3 lm = DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap,i.uvLM));
col.rgb *= lm;
#endif
這里為什么要用DecodeLightmap
又要用UNITY_SAMPLE_TEX2D
來(lái)解讀ligtmap呢?實(shí)際上unity生成的lightmap的有兩種編碼格式:doubleLDR和RGBM立镶,根據(jù)馮樂(lè)樂(lè)前輩的考證
Unity使用了兩種編碼方式來(lái)存儲(chǔ)lightmap:
1类早、doubleLDR,需要一張rgb24貼圖
2莺奔、RGBM,需要一張rgba32貼圖
在移動(dòng)設(shè)備上使用doubleLDR格式令哟,可以獲得更快的計(jì)算速度。它只用到了lightmap的RGB通道晴竞。
PC上則使用RGBM格式狠半,可以獲得更廣的亮度范圍,而犧牲一點(diǎn)速度神年。它使用了貼圖的RGBA通道,而A通道是用來(lái)做乘法垛耳,所以稱(chēng)為RGBM格式。
這兩種格式的差異就是導(dǎo)致不同平臺(tái)下lightmap表現(xiàn)不同的原因堂鲜,當(dāng)然Unity會(huì)在切換平臺(tái)時(shí)幫我們對(duì)貼圖進(jìn)行轉(zhuǎn)換,而不需要太關(guān)心這個(gè)差異哥纫。
我們這樣解讀lightmap才能得到正確的顏色值而且不必?fù)?dān)心因?yàn)槠脚_(tái)不同造成的差異痴奏。
其實(shí)烘焙分為兩種模式,有向的和無(wú)向的抛虫,如下圖
上面的解析光照貼圖方式是針對(duì)無(wú)向模式简僧,有向模式應(yīng)該這樣解析
float4 lightmapDir = UNITY_SAMPLE_TEX2D_SAMPLER(unity_LightmapInd, unity_Lightmap, i.uvLM);
float3 lm = DecodeDirectionalLightmap(col,lightmapDir,normal);
unity_LightmapInd
代表有向模式下烘焙出來(lái)的包含光照方向的光照貼圖(是的!有向模式會(huì)烘焙出兩張光照貼圖棉姐,一張里儲(chǔ)存顏色啦逆,一張里儲(chǔ)存方向),然后有個(gè)方法叫DecodeDirectionalLightmap
夏志,需要傳入顏色(比如說(shuō)物體貼圖的顏色,或者自定義的顏色湿诊,看最終想要的效果來(lái)決定)瘦材,解析出來(lái)的光照貼圖顏色以及世界坐標(biāo)下的法線(xiàn),這樣出來(lái)的最終結(jié)果就是有向烘焙模式下光照貼圖的信息了食棕。
最終,我們自己寫(xiě)的shader也能支持烘焙了眶拉。
說(shuō)完如何支持lightmap憔儿,我們?cè)賮?lái)看看如何支持light probes。我們?cè)诤姹簣?chǎng)景的時(shí)候,有時(shí)候場(chǎng)景中的某些東西不是static吴侦,也就是不是固定不動(dòng)的坞古,這樣烘焙好的結(jié)果無(wú)法應(yīng)用到這種在場(chǎng)景中動(dòng)來(lái)動(dòng)去的物件,而我們希望這樣的物件依然受到烘焙的影響痪枫,那么這個(gè)時(shí)候就需要light probes了。
由于弄light probes挺麻煩的易阳,所以我直接使用了這篇博客中的場(chǎng)景吃粒,然后加上了幾行代碼來(lái)支持light probes。我們先來(lái)看結(jié)果(這次我會(huì)做動(dòng)圖了P觳)
這個(gè)方塊在動(dòng)到黃色區(qū)域的時(shí)候呈現(xiàn)偏黃的顏色,而到了藍(lán)色區(qū)域就會(huì)偏藍(lán)肖爵。這其實(shí)就是在利用unity shader中自帶的一個(gè)方法
ShadeSH9
來(lái)實(shí)現(xiàn)的臀脏,具體代碼如下
Shader "Unlit/LightProbes"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color ("Main Color",Color) = (1,1,1,1)
}
SubShader
{
Tags { "RenderType"="Opaque" "LightMode" = "ForwardBase" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
fixed3 SHLighting : COLOR;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
float3 worldNormal = UnityObjectToWorldNormal(v.normal);
o.SHLighting = ShadeSH9(float4(worldNormal,1));
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
col *= _Color;
col.rgb *= i.SHLighting;
return col;
}
ENDCG
}
}
}
這里有一點(diǎn)要記住揉稚,自定義的shader一定要寫(xiě)上Tags{"LightMode" = "ForwardBase"}
,即是說(shuō)要指定好LightMode窃植,不指定的話(huà)ShadeSH9
會(huì)返回黑色(全黑的那種),你完全不會(huì)想看到的顏色巷怜。
參考
UnityShader實(shí)例07:在shader調(diào)用unity內(nèi)置lightmap和light Probes