Phong光照模型
之前說過兼搏,游戲場景中計算光照時,需要考慮光源超棺、模型材質和觀察方向三方面的因素向族。很多物理學家呵燕、數(shù)學家根據(jù)這些因素棠绘,再結合真實物理世界光照效果的經(jīng)驗,總結出幾種非常經(jīng)典的光照模型再扭,比如說蘭伯特(Lambert)氧苍、環(huán)境光(Ambient)、Phong泛范、Blinn-Phong让虐、PBR等等,其中最經(jīng)典的要數(shù)Phong光照模型罢荡。
Phong光照模型是由著名學者裴祥風在1975年提出的第一個有巨大影響力的光照計算模型赡突。這個模型只考慮物體對直接光照的反射作用,認為環(huán)境光是常量区赵,沒有考慮物體之間相互的反射光惭缰,物體間的反射光只用環(huán)境光表示。用一個簡單的公式來表示笼才,就是:
光照 = 漫反射(Diffuse) + 高光反射(Specular) + 環(huán)境光(Ambient)
漫反射
漫反射用來描述漱受,當光線從光源照射到模型表面時,該表面會向每個方向散射多少輻射量骡送。漫反射光線的強度與物體表面的法線和光源方向之間夾角的余弦值成正比昂羡。這部分可以通過光源照射方向和法線方向的點積來實現(xiàn)。漫反射光的計算公式可以寫成:
其中摔踱,normalDir表示法線方向虐先,lightDir表示光源方向,為了防止點積為負數(shù)派敷,因此使用max函數(shù)將其截取到0蛹批,這樣可以防止物體被后面來的光源照亮。C-light表示光源顏色,m-light表示材質的漫反射顏色般眉。
法線方向可以通過下面的公式計算得出:
// v表示頂點
normalDir = normalize(mul(float4(v.normal,0.0),unity_WorldToObject).xyz);
光源方向通過使用Unity內置的變量_WorldSpaceLightPos0計算獲取了赵,不過這里涉及到光源類型問題,如果是平行光源甸赃,直接將_WorldSpaceLightPos0變量歸一化柿汛,就能得到光源方向;但如果是點光源埠对,還需要再減去頂點的世界空間坐標位置络断,然后再歸一化獲取光源方向。
//i.pos_world = mul(unity_ObjectToWorld,v.vertex).xyz;
// v表示頂點
// 獲取平行光项玛、點光源方向
#if defined (DIRECTIONAL)
half3 light_dir = normalize(_WorldSpaceLightPos0.xyz);
#elif defined (POINT)
half3 light_dir = normalize(_WorldSpaceLightPos0.xyz - i.pos_world);
#endif
除了使用max()函數(shù)截取點積結果貌笨,使用saturate()函數(shù)也可以達到同樣的效果。
接下來是C-light光源顏色襟沮,光源顏色使用內置變量_LightColor0獲取锥惋,不過在此之前嘲玫,還需要引入內置文件“AutoLight.cginc”
最后是M-light材質顏色蟹腾,直接可以通過材質紋理獲取。
知道了上述參數(shù)的計算或獲取方法屯阀,就能計算出漫反射顏色了固灵,不過還有一個問題沒有考慮捅伤,那就是光的衰減。
平行光通常是用來模擬自然界的太陽光巫玻,因為太陽光有很高的強度丛忆,光的衰減幾乎可以忽略不計。而對于點光來說仍秤,就必須通過計算物體到光源之間的距離來獲得熄诡。具體計算方法如下:
// 計算衰減
#if defined (DIRECTIONAL)
half attuenation = 1.0; // 衰減系數(shù)
#elif defined (POINT)
// 計算點光源到物體之間的具體
// i.pos_world = mul(unity_ObjectToWorld,v.vertex).xyz;
// v表示頂點
half distance = length(_WorldSpaceLightPos0.xyz - i.pos_world);
// 衰減范圍值
half range = 1.0 / unity_WorldToLight[0][0];
float attuenation = normalize((range - distance)/range); // 點光源的衰減值
#endif
漫反射最終的計算公式為:
half4 base_color = tex2D(_MainTex, i.uv);
half3 NdotL = dot(normal_dir,light_dir);
// 計算漫反射顏色
half3 diffuse_color = max(0.0, NdotL) * _LightColor0.xyz * base_color.xyz * attuenation;
高光反射
這里的高光反射是一種經(jīng)驗模型,并不完全符合真實物理世界中的高光反射現(xiàn)象徒扶。當一束光線照射到物體上時粮彤,光線會沿著物體表面法線防線朝著和入射方向相反的方向反射,這種反射的光線姜骡,可以讓物體看起來更加有光澤导坟。
另外,高光反射還和攝像機的觀察方向有關圈澈,從不同的角度觀察物體惫周,物體的高光反射也是不一樣的,高光反射的計算公式是:
reflectDir表示反射方向康栈,viewDir表示攝像機的觀察方向递递,C-light表示光源顏色喷橙。
反射方向使用reflect()函數(shù),傳入光源負方向和法線方向登舞,具體代碼如下:
half3 reflect_dir = reflect(-light_dir, normal_dir);
觀察方向通過攝像機世界空間坐標位置減去頂點世界空間位置計算得出贰逾,具體代碼如下:
half3 view_dir = normalize(_WorldSpaceCameraPos.xyz - i.pos_world);
高光反射的計算代碼如下:
half3 view_dir = normalize(_WorldSpaceCameraPos.xyz - i.pos_world);
half3 reflect_dir = reflect(-light_dir, normal_dir);
half RdotV = dot(reflect_dir,view_dir);
half3 spec_color = max(0.0,RdotV) * _LightColor0.xyz;
這個高光計算的代碼還是屬于簡單版的,大家可以引入其他變量菠秒,比如反光度(shininess)疙剑、高光強度(specIntensity)、高光遮罩(specMask)等等践叠,靈活控制高光反射的效果言缤。
half3 spec_color = pow(max(0.0,RdotV),_Shininess) * _LightColor0.xyz * _SpecIntensity * spec_mask.rgb;
環(huán)境光
最后一步的添加環(huán)境光,環(huán)境光相對來說就非常簡單了禁灼,可以使用純色管挟,或者使用一張紋理貼圖來制作。
Phong光照模型最后的計算結果是
half3 phong_color = diffuse_color + spec_color + _AmbientColor.xyz;
總結
上述漫反射弄捕、高光反射和環(huán)境光的計算屬于簡易版本僻孝,實際在編寫shader中,還可以加入更多參數(shù)實現(xiàn)更復雜精確的控制效果察藐,比如說混入顏色貼圖皮璧、粗糙圖貼圖舟扎、法線貼圖分飞。
另外,需要提醒的是:任何光照模型的計算睹限,都需要根據(jù)渲染路徑的思路來進行計算譬猫,具體思路請參考Unity中的渲染路徑RenderingPath,下面給出一個光照模型shader Pass的模板:
Shader "lit/Phong"
{
SubShader
{
Tags {"RenderType"="Opaque"}
Pass
{
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma multi_compile_fwdbase
#include "AutoLight.cginc"
}
Pass
{
Tags { "LightMode"="ForwardAdd" }
Blend One One
CGPROGRAM
#pragma multi_compile_fwdadd
#include "AutoLight.cginc"
ENDCG
}
}
}
以前向渲染路徑為例羡疗,它使用到了兩個Pass染服,一個是ForwardBase,另外一個是ForwardAdd叨恨,因此在Tags 中要將這兩個Pass名稱傳入到LightMode柳刮。
初次之外,還需要分別添加multi_compile_fwdbase 和 multi_compile_fwdadd痒钝。
另外秉颗,還需要再引入AutoLight.cginc內置文件,這個文件包含了許多光源計算的內置變量送矩。