1、Unity 光源型別
Unity —共支持 4 種光源型別:
- 平行光
- 點光源(Point Light)
- 聚光燈(Spot Light)
- 面光源(area light)
面光源僅在烘焙時才可發揮作用, 因此不在本節討論范圍內,
本節中, 我們學習如何處理點光源(pointlight) 和聚光燈(spotlight),
1.1、光源型別
1.1.1、平行光
平行光的幾何屬性只有方向,不會衰減,
1.1.2、點光源
由空間的一個球體定義的光源,
點光源會存在光線衰減,中心點為1,邊緣點為0;中間的衰減可以由一個函式定義,
1.1.3、聚光燈
這三種中最復雜的一種,照亮空間內的一個錐形區域,
中間的衰減同樣可以通過函式定義,只是更加復雜,
2、Unity 光照衰減
Unity 使用一張紋理作為査找表(Lookup Table, LUT),在片元著色器中計算逐像素光照的衰減,
這樣做減輕了計算壓力,但是也有缺點:
- 需要預處理得到采樣紋理, 而且紋理的大小也會影響衰減的精度,
- 不直觀, 同時也不方便, 因此一旦把資料存盤到査找表中, 我們就無法使用其他數學公式來計算衰減,
2.1、用于光照衰減的紋理
Unity 在內部使用一張名為 _LightTexture0 的紋理來計算光源衰減,
如果我們對該光源使用了 cookie,那么衰減査找紋理是 _LightTextureB0
我們通常只關心 _LightTexture0 對角線上的紋理顏色值,
例如:(0,0)點表明了與光源位置重合的點的衰減值, 而(1,1)點表明了在光源空間中所關心的距離最遠的點的衰減值,
2.2、使用紋理采樣
2.2.1、獲得該點在光源空間上的位置
使用 unity_WorldToLight 矩陣可以把頂點從世界空間變換到光源空間,
將其與世界空間下的頂點坐標相乘即可,
float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz;
2.2.2、紋理采樣
使用這個坐標的模的平方對衰減紋理進行采樣, 得到衰減值:
fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
使用了光源空間中頂點距離的平方( 通過 dot 函式來得到)來對紋理采樣, 之所以沒有使用距離值來采樣是因為這種方法可以避免開方操作,
然后, 我們使用宏 UNITY_ATTEN_CHANNEL 來得到衰減紋理中衰減值所在的分量, 以得到最終的衰減值,
2.3、使用公式采樣
例如:
float distance = length(_WorldSpaceLightPosO.xyz - i.worldPosition.xyz);
atten = 1.0 / distance;
Unity 沒有在檔案中給出內置衰減計算的相關說明,也沒有開放相關介面(如聚光燈的朝向、 張開角度等),
當然,我們可以利用腳本將相關資訊傳遞給 Shader,但是效率、靈活性就很差了,
3、實踐
3.1、在前向渲染中處理光源
首先定義第一個 Pass — BasePass ,
使用#pragma multi_compile_fwdbase 指令,保證光照衰減等光照變數可以被正確賦值,
在 BasePass 中計算場景中的環境光,
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
Base Pass 會在計算逐像素平行光時呼叫一次,
因為環境光、自發光只需要計算一次,因此放在 Base Pass 中計算,
如果場景中有多個平行光,Unity 會選擇最亮的平行光傳遞給 Base Pass 進行逐像素處理,
其他平行光會按照逐頂點或在 Additional Pass 中按逐像素的方式處理,
如果場景中沒有任何平行光, 那么 Base Pass 會當成全黑的光源處理,
使用 _WorldSpaceLightPos0 來得到這個平行光的方向,
使用 _LightColor0 來得到它的顏色和強度( _LightColor0 已經是顏色和強度相乘后的結果),
隨后定義 Addition Pass ,
使用#pragma multi_compile_fwdadd指令,
使用 Blend 指令:Blend One One宣告混合模式(因為我們不希望覆寫),
使用判斷來獲得光源方向、處理衰減,
最終代碼如下:
Shader "Unity Shaders Book/Chapter 9/ForwardRendering" {
Properties {
_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
_Specular ("Specular", Color) = (1, 1, 1, 1)
_Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader {
Tags { "RenderType"="Opaque" }
Pass {
// Pass for ambient light & first pixel light (directional light)
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
// Apparently need to add this declaration
#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;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
fixed atten = 1.0;//光照衰減
return fixed4(ambient + (diffuse + specular) * atten, 1.0);
}
ENDCG
}
Pass {
// Pass for other pixel lights
Tags { "LightMode"="ForwardAdd" }
Blend One One
CGPROGRAM
// Apparently need to add this declaration
#pragma multi_compile_fwdadd
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
//判斷是否是平行光,獲得世界坐標下光線方向
#ifdef USING_DIRECTIONAL_LIGHT
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
#else
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
#endif
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
//判斷是否是平行光,處理衰減
#ifdef USING_DIRECTIONAL_LIGHT
fixed atten = 1.0;
#else
#if defined (POINT)
float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz;
fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
#elif defined (SPOT)
float4 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1));
fixed atten = (lightCoord.z > 0) * tex2D(_LightTexture0, lightCoord.xy / lightCoord.w + 0.5).w * tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
#else
fixed atten = 1.0;
#endif
#endif
return fixed4((diffuse + specular) * atten, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
效果如下:能夠實作非平行光源的光照衰減,
3.2、Base Pass 與 Additional Pass 的呼叫
Unity 中,我們可以使用幀除錯器(Frame Debugger)工具來査看場景的繪制程序,
Window -> Analysis -> Frame Debugger
可以看到, Unity 首先清除顏色、深度和模板緩沖,為后面的渲染做準備;
隨后 Unity 利用 Base Pass ,將平行光的光照渲染到幀快取中;
最后呼叫 Additional Pass ,將點光源依次應用到物體上,
Unity 處理這些點光源的順序是按照它們的重要度排序的, 重要度與顏色、強度、距離物體遠近都有關系,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/499636.html
標籤:其他
上一篇:實體解釋NLLLoss損失函式與CrossEntropyLoss損失函式的關系
下一篇:JSON 格式介面測驗流程
