光照模型的原理和實現(xiàn)思路——Phong

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)。漫反射光的計算公式可以寫成:

a1.png

其中摔踱,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內置文件,這個文件包含了許多光源計算的內置變量送矩。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末蚕甥,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子栋荸,更是在濱河造成了極大的恐慌菇怀,老刑警劉巖凭舶,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異爱沟,居然都是意外死亡帅霜,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進店門呼伸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來义屏,“玉大人,你說我怎么就攤上這事蜂大∶鲱恚” “怎么了?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵奶浦,是天一觀的道長兄墅。 經(jīng)常有香客問我,道長澳叉,這世上最難降的妖魔是什么隙咸? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮成洗,結果婚禮上五督,老公的妹妹穿的比我還像新娘。我一直安慰自己瓶殃,他們只是感情好充包,可當我...
    茶點故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著遥椿,像睡著了一般基矮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上冠场,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天家浇,我揣著相機與錄音,去河邊找鬼碴裙。 笑死钢悲,一個胖子當著我的面吹牛,可吹牛的內容都是我干的舔株。 我是一名探鬼主播莺琳,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼督笆!你這毒婦竟也來了芦昔?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤娃肿,失蹤者是張志新(化名)和其女友劉穎咕缎,沒想到半個月后珠十,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡凭豪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年焙蹭,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嫂伞。...
    茶點故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡孔厉,死狀恐怖,靈堂內的尸體忽然破棺而出帖努,到底是詐尸還是另有隱情撰豺,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布拼余,位于F島的核電站污桦,受9級特大地震影響,放射性物質發(fā)生泄漏匙监。R本人自食惡果不足惜凡橱,卻給世界環(huán)境...
    茶點故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望亭姥。 院中可真熱鬧稼钩,春花似錦、人聲如沸达罗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽氮块。三九已至绍载,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間滔蝉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工塔沃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蝠引,地道東北人。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓蛀柴,卻偏偏與公主長得像螃概,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鸽疾,可洞房花燭夜當晚...
    茶點故事閱讀 43,494評論 2 348

推薦閱讀更多精彩內容