轉自馮樂樂的《Unity Shader入門精要》
通常來講,我們要模擬真實的光照環境來生成一張圖像,需要考慮3種物理現象。
首先,光線從光源中被發射出來。
然后,光線和場景中的一些物體相交:一些光線被物體吸收了,而另一些光線被散射到其他方向。
最后,攝像機吸收了一些光,產生了一張圖像。
?
在光學中,我們使用輻照度來量化光。對于平行光來說,它的輻照度可通過計算在垂直于l的單位面積上單位時間內穿過的能量來得到。在計算光照模型時,我們需要知道一個物體表面的輻照度,而物體表面往往是和l不垂直的,我們可以使用光源方向l和表面法線n之間的夾角的余弦值來得到。需要注意的是,這里默認方向矢量的模都為1。下圖顯示了使用余弦值來計算的原因。
因為輻照度是和照射到物體表面時光線之間的距離d/cosθ 成反比的,因此輻照度就和cosθ 成正比。cosθ 可以使用光源方向l和表面法線n的點積來得到。這就是使用點積來計算輻照度的由來。
光照由光源發射出來后,就會與一些物體相交。通常的結果又兩個:散射和吸收。
散射只 改變光線的方向,但不改變光線的密度和顏色。而吸收只改變光線的密度和顏色,但不改變光線的方向。光線在物體表面經過散射后,有兩種方向:一種將會散射到物體內部,這種現象被稱為折射或投射;另一種將會散射到外部,這種現象被稱為反射。對于不透明物體,折射進入物體內部的光線還會繼續與內部的顆粒進行相交,其中一些光線最后會重新發射出物體表面,而另一些則被物體吸收。那么從物體表面重新發射出的光線將具有和入射光線不同的方向分布和顏色。下圖給出了這樣的例子:
為了區分這兩種不同的散射方向,我們再光照模型中使用了不同的部分來計算它們:高光反射部分表示物體表面是如何發射光線的,而漫反射部分則表示有多少光線會被折射、吸收和散射出表面。根據入射光線的數量和方向,我們可以計算出射光線的數量和反向,我們通常使用出射度來描述它。輻射度和出射度之間是滿足線性關系的,而它們之間的比值就是材質的漫反射和高光反射屬性。
在本章中,我們假設漫反射部分是沒有方向性的,也就是說,光線在所有方向上是平均分布的,同時,我們也只考慮某一個特性方向上的高光反射。
?
著色指的是,根據材質屬性(如漫反射屬性等)、光源信息(如光源方向、輻照度等),使用一個等式去計算沿某個觀察方向的出射度的過程。我們也把這個等式稱為光照模型。不同的光照模型有不同的目的。例如,一些用于描述粗糙的物體表面,一些用于描述金屬表面等。
?
我們已經了解了光線在和物體表面相交時會發生哪些現象。當已知光源位置和方向、視角方向時,我們就需要知道一個表面是和光照進行交互的。例如,當光線從某個方向照射到一個表面時,有多少光線被反射?反射的方向有哪些?而BRDF 就是用來回答這些問題的。當給定模型表面上的一個點時,BRDF 包含了對該點外觀的完整的描述。在圖形學中,BRDF 大多使用一個數學公式來表示,并且提供了一些參數來調整材質屬性。通俗來講,當給定入射光線的方向和輻照度后,BRDF可以給出在某個出射方向上的光照能量分布。后面說的BRDF都是對真實場景進行理想化和簡化后的模型,也就是說,它們并不能真實地反應物體和光線之間的交互,這些光照模型被稱為經驗模型。盡管如此,這些經驗模型仍然在實時渲染領域被應用了多年。
?
標準光照模型只關心直接光照,也就是那些直接從光源發射出來照射到物體表面后,經過物體表面的一次反射直接進入攝像機的光線。
它的基本方法是,把進入到攝像機內的光線分為4個部分,每個部分使用一種方法來計算它的貢獻度。這4個部分是:
1)自發光部分。這個部分用于描述當給定一個方向時,一個表面本身會向該方向發射多少輻射量。需要注意的是,如果沒有使用全局光照技術,這些自發光的表面并不會真的照亮周圍的物體,而是它本身看來更亮了而已。
2)高光反射部分。這個部分用于描述當光線從光源照射到模型表面時,該表面會在完全鏡面反射方向散射多少輻射量。
3)漫反射部分。這個部分用于描述,當光線從光源照射到模型表面時,該表面會向每個方向散射多少輻射量。
4)環境光部分。它用于描述其他所有的間接光照。
?
雖然標準光照模型的重點在于描述直接光照,但在真實的世界中,物體也可以被間接光照所照亮。間接光照指的是,光線通常會在多個物體之間進行反射,最后進入攝像機,也就是說,在光線進入攝像機之間,經過了不止一次的物體反射。
在標準光照模型中,我們使用了一種被稱為環境光的部分來近似模擬間接光照。環境光的計算非常簡單,它通常是一個全局變量,即場景中的所有物體都使用這個環境光。下面的等式給出了計算環境光的部分:
?
光線也可以直接由光源發射進入攝像機,而不需要經過任何物體的反射。標準光照模型使用自發光來計算這個部分的共享度。它的計算也很簡單,就是直接使用了該材質的自發光顏色:
通常在實時渲染中,自發光的表面往往并不會照亮周圍的表面,也就是說,這個物體并不會被當成一個光源。Unity 5 引入的全新全局光照系統則可以模擬這類自發光物體對周圍物體的影響。
?
漫反射光照是用于對那些被物體表面隨機散射到各個方向的輻射度進行建模的。在漫反射中,視角的位置是不重要的,因為反射是完全隨機的,因此可以認為在任何反射反向上的分布都是一樣的。但是,入射光線的角度很重要。
漫反射光照符合蘭伯特定律:反射光線的強度與表面法線和光源方向之間的夾角的余弦值成正比。因此,漫反射部分的計算如下:
其中,n是表面法線,I是指向光源的單位矢量,m(diffuse)是材質的漫反射顏色,c(light)是光源顏色。需要注意的是,我們需要防止返現和光源方向點乘的結果為負值,為此,我們使用取最大值的函數來將其截取到0,這可以防止物體被從后面來的光源照亮。
?
這里的高光反射是一種經驗模型,也就是說,它并不完全符合真實世界中的高光反射現象。它可以用于計算那些沿著完全鏡面反射反向被反射的光線,這可以讓物體看起來是由光澤的,例如金屬材質。
計算高光反射需要知道的信息比較多,如表面法線、視角方向、光源方向、反射方向等。我們假設這些矢量都是單位矢量,下圖給出了這些方向矢量。
在這四個矢量中,我們實際上只需要知道其中3個矢量即可,而第4個矢量——反射方向可以通過其他信息計算得到:
這樣,我們就可以利用Phong模型來計算高光反射的部分:
其中m(gloss)是材質的光澤度,也被反稱為反光度。它用于控制高光區域的“亮點”有多寬,m(gloss)越大,亮點就越小。m(spscular)是材質的高光反射顏色,它用于控制該材質對于高光反射的強度和顏色。c(light)則是光源的顏色和強度。
和上述的Phong模型相比,Blinn提出了一個簡單的修改方法來得到類似的效果。它的基本思想是,避免計算反射方向。為此,Blinn模型引入了一個新的矢量,如下:
然后,使用n和h之間的夾角進行計算,而非v和r之間的夾角,如下圖所示:
總結一下,Blinn模型的公式如下:
在硬件實現時,如果攝像機和光源距離模型足夠遠的話,Blinn模型會快于Phong模型,這是因為,此時可以認為V和I都是定值,因此h將是一個常量。但是,當V或者I不是定值時,Phong模型可能反而更快一些。需要注意的是,這兩種光照模型都是經驗模型,也就是說,我們不應該認為Blinn模型是對“正確的”Phong的近似。實際上,在一些情況下,Blinn模型更符合實驗結果。
?
上面,我們給出了基本光照模型使用的數學公式,那么我們再哪里計算這些光照模型呢?通常來講,我們有兩種選擇:在片元著色器中計算,也被稱為逐像素光照;在頂點著色器中計算,也被稱為逐頂點光照。
在逐像素光照中,我們會以每個像素為基礎,得到它的法線,然后進行光照模型的計算。這種在面片之間對頂點法線進行插值的技術被稱為Phong著色,也被稱為Phong插值或者法線插值著色技術。這不同于我們之前講到的Phong光照模型。
與之相對的是逐頂點光照,也被稱為高洛德著色。在逐頂點光照中,我們在每個頂點上計算光照,然后會在渲染圖元內部進行線性插值,最后輸出成像素顏色。由于頂點數目往往小于像素數目,因此逐頂點光照的計算量往往要小于逐像素光照。但是,由于逐頂點光照依賴于線性插值來得到像素光照,因此,當光照模型中有非線性的計算(例如計算高光反射時)時,逐頂點光照就會出問題。而且,由于逐頂點光照會在渲染圖元內部對頂點顏色進行插值,這會導致渲染圖元內部的顏色總是暗于頂點處的最高顏色值,這在某些情況下會產生明顯的菱角現象。
?
在標準光照模型中,環境光和自發光的計算是最簡單的。
在Unity 5中,場景中的環境光可以在Window -> Lighting -> Ambient Source / Ambient Color / Ambient Intensity 中控制,如下圖所示。在Shader 中個,我們只需要通過Unity 內置變量 UNITY_LIGHTMODEL_AMBIENT 就可以得到環境光的顏色和強度信息。而大多數物體是沒有自發光特性的,因此在本書絕大部分的Shader 中都沒有計算自發光部分。如果要計算自發光也非常簡單,我們只需要再片元著色器輸出最后的顏色之前,把材質的自發光顏色添加到輸出顏色上即可。
?
在漫反射公式中可以看出,要計算漫反射需要知道4個參數:入射光線的顏色和強度,材質的漫反射系數,表面法線以及光源方向。
為了防止點積結果為負值,我們需要使用max操作,而CG提供了這樣的函數。在本例中,使用CG的另一個函數可以達到同樣的目的,即saturate函數。
函數:saturate(x)
參數:x 為用于操作的標量或矢量
描述:把 x 截取在[0, 1]范圍內,如果 x 是一個矢量,那么會對它的每一個分量進行這樣的操作。
?
實踐:逐頂點光照
效果如下圖:
1)在Unity 中新建一個場景。在Unity 5.2 中,默認情況下場景將包含一個攝像機和一個平行光,并且使用了內置的天空盒子。在Window -> Lighting -> Skybox 中去掉場景中的天空盒子。
2)新建一個材質
3)新建一個Unity Shader。把新的Shader賦給第2步中創建的材質
4)在場景中創建一個膠囊體,并把第2步中的材質賦給該膠囊體
5)保存場景
接下來我們編寫自己的Shader來實現一個逐頂點的漫反射效果
?
Shader?"Unity?Shader?Book/Chapter?6/Diffuse?Vertex-Level"??{????Properties????{??????//用來控制材質的漫反射顏色??????_Diffuse?("Diffuse",?Color)?=?(1,1,1,1)????}????????SubShader????{??????Pass??????{????????//LightMode?標簽是Pass標簽的一種,它用于定義該Pass在Unity的光照流水線中的角色。????????Tags?{"LightMode"?=?"ForwardBase"}????????????????CGPROGRAM????????#pragma?vertex?vert????????#pragma?fragmentfrag????????????????//為了使用Unity內置的一些變量而包含內置文件????????#include?"Lighting,cgnic"????????????????//為了在Shader中使用Properties語義塊中聲明的屬性,我們需要定義一個和該屬性類型相匹配的變量????????//通過這樣的方式,我們就可以得到漫反射公式中需要的參數之一——材質的漫反射屬性。????????//由于顏色屬性的范圍在0到1之間,因此我們可以使用fixed精度的變量來存儲它。????????fixed4?_Diffuse;????????????????//頂點著色器的輸入結構體????????//為了訪問頂點的法線,我們需要再a2v中定義一個normal變量,并通過使用NORMAL語義來告訴Unity????????//要把模型頂點的法線信息存儲到normal變量中。????????struct?a2v????????{??????????float4?vertex?:?POSITION;??????????float3?normal?:?NORMAL;????????};????????????????//頂點著色器的輸出結構體(同時也是片元著色器的輸入結構體)????????//為了把在頂點著色器中計算得到的光照顏色傳遞給片元,我們需要再v2f中定義一個color變量,且并不是必須使用COLOR語義????????struct?v2f????????{??????????float4?pos?:?SV_POSITION;??????????fixed3?color?:?COLOR;????????};????????????????v2f?vert(a2v?v)????????{??????????//定義返回值o??????????v2f?o;??????????//頂點著色器最基本任務就是把頂點位置從模型空間轉換到裁剪空間中,因此需要用矩陣來進行變換??????????o.pos?=?mul(UNITY_MATRIX_MVP,v.vertex);??????????//我們通過Unity的內置變量UNITY_LIGHTMODEL_AMBIENT?得到了環境光部分??????????fixed3?ambient?=?UNITY_LIGHTMODEL_AMBIENT.xyz;????????????????????//然后開始真正計算漫反射光照部分,首先我們已經知道了材質的漫反射顏色_Diffuse以及頂點法線v.normal。??????????//我們還需要知道光源的顏色和強度信息以及光源方向。Unity提供了我么一個內置變量_LightColor0來訪問該Pass處理的光源的顏色和??????????//強度信息(注意,想要得到正確的值需要定義合適的LightMode標簽),??????????//而光源方向可以由_WorldSpaceLightPos0?來得到。需要注意的是,這里對光源方向的計算并不具有通用性????????????????????//在計算法線和光源方向之間的點積時,我們需要選擇它們所在的坐標系,只有兩者處于同一坐標空間下,它們的點積才有意義。??????????//在這里,我們選擇了世界坐標空間。而由a2v得到的頂點法線是處于模型空間下的,因此我們首先需要把法線轉換到世界空間中。??????????//在第4章中,我們已經知道可以使用頂點變換矩陣的逆轉置對法線進行相同的變換,因此我們首先得到模型空間到世界空間的??????????//變換矩陣的逆矩陣_World2Object,然后通過調換它在mul函數中的位置,得到和轉置矩陣相同的矩陣乘法。??????????//由于法線是一個三維矢量,因此我們只需要截取_World2Object的前三行前三列即可。??????????fixed3?worldNormal?=?normalize(mul(v.normal,?(float3×3)_World2Object));????????????????????fixed3?worldLight?=?normalize(_WorldSpaceLightPos0.xyz);????????????????????//在得到了世界空間中的法線和光源方向后,我們需要對它們進行歸一化操作。在得到它們點擊的結果后,我們使用saturate函數??????????//把參數截取到[0,?1]范圍內。最后,再與光源顏色和強度以及材質的漫反射顏色相乘可得到最終的漫反射光照部分??????????fixed3?diffuse?=?_LightColor0.rgb?*?_Diffuse.rgb?*?saturate(dot(worldNormal,worldLight));????????????????????//最后我們對環境光和漫反射部分相加,得到最終的光照結果??????????o.color?=?ambient?+?diffuse;????????????????????return?o;????????}????????????????//由于所有的計算在頂點著色器中都已完成了,因此片元著色器的代碼很簡單,我們只需要直接把頂點顏色輸出即可????????fixed4?frag(v2f?i)?:?SV_Target????????{??????????return?fixed4(i.color,1.0);????????}????????????????ENDCG??????}????}????????Fallback?"Diffuse"??}?? ?
?
我們只需要對shader進行一些更改就可以實現逐像素的漫反射效果,如下圖
為此,我們進行如下準備工作
1)使用逐頂點中的場景
2)新建一個材質
3)新建一個Unity Shader。把新建的Shader 賦給第2步中創建的材質。
4)把第2步中創建的材質賦給膠囊體。
Shader代碼如下:
?
Shader?"Unity?Shader?Book/Chapter?6/Diffuse?PixelLevel"??{??????Properties??????{??????????_Diffuse?("Diffuse",?Color)?=?(1,1,1,1)??????}????????????SubShader??????{??????????Pass??????????{??????????????Tags?{"LightMode"?=?"ForwardBase"}????????????????????????????CGPROGRAM??????????????#pragma?vertex?vert??????????????#pragma?fragmentfrag????????????????????????????#include?"Lighting,cgnic"????????????????????????????fixed4?_Diffuse;????????????????????????????struct?a2v??????????????{??????????????????float4?vertex?:?POSITION;??????????????????float3?normal?:?NORMAL;??????????????};????????????????????????????//頂點著色器的輸出結構體有作修改??????????????struct?v2f??????????????{??????????????????float4?pos?:?SV_POSITION;??????????????????fixed3?worldNormal?:?TEXCOORD0;??????????????};????????????????????????????//頂點著色器不需要計算光照模型,只需要把世界空間下的法線傳遞給片元著色器即可??????????????v2f?vert(a2v?v)??????????????{??????????????????v2f?o;??????????????????o.pos?=?mul(UNITY_MATRIX_MVP,v.vertex);??????????????????o.worldNormal?=?mul(v.normal,?(float3×3)_World2Object));??????????????????return?o;??????????????}????????????????????????????//片元著色器需要計算漫反射光照模型??????????????fixed4?frag(v2f?i)?:?SV_Target??????????????{??????????????????fixed3?ambient?=?UNITY_LIGHTMODEL_AMBIENT.xyz;??????????????????fixed3?worldNormal?=?normalize(i.worldNormal);??????????????????fixed3?worldLightDir?=?normalize(_WorldSpaceLightPos0.xyz);??????????????????fixed3?diffuse?=?_LightColor0.rgb?*?_Diffuse.rgb?*?saturate(dot(worldNormal,worldLightDir));??????????????????fixed3?color?=?ambient?+?diffuse;??????????????????return?fixed4(color,1.0);??????????????}????????????????????????????ENDCG??????????}??????}????????????Fallback?"Diffuse"??}??
逐像素光照可以得到更加平滑的光照效果。但是,幾遍使用了逐像素漫反射光照,有一個問題仍然存在,在光照無法達到的區域,模型的外觀通常是全黑的,沒有任何明暗變化,這會使模型的背光區域看起來就像一個平面,失去了模型細節表現。實際上我們可以通過添加環境光來得到非全黑的效果,但即便這樣仍然無法解決背光面明暗一樣的缺點。為此,有一種改善技術被提出來,這就是半蘭伯特光照模型。
?
?
廣義的半蘭伯特光照模型的公式如下:
可以看出,與原蘭伯特模型相比,半蘭伯特光照模型沒有使用max操作來防止n和I的點積為負值,而是對其結果進行了一個α 倍的縮放再加上一個β 大小的偏移。絕大多數情況下,α 和β的值均為0.5,即公式為:
通過這樣的方式,我們可以把(n·I)的結果范圍從[-1,1]映射到[0,1]范圍內,也就是說,對于模型的背光面,在原蘭伯特光照模型中點積結果將映射到同一個值,即0值處;而在半蘭伯特模型中,背光面也可以由明暗變化,不同的點積結果會映射到不同的值上。
需要注意的是,半蘭伯特是沒有任何物理依據的,它僅僅是一個視覺加強技術。
對逐像素光照的代碼做一些修改就可以實現半蘭伯特漫反射光照效果。
?
fixed4?frag(v2f?i)?:?SV_Target??{??????fixed3?ambient?=?UNITY_LIGHTMODEL_AMBIENT.xyz;??????fixed3?worldNormal?=?normalize(i.worldNormal);??????fixed3?worldLightDir?=?normalize(_WorldSpaceLightPos0.xyz);??????fixed3?halfLambert?=?dot(worldNormal,?worldLightDir)?*?0.5?+?0.5;??????fixed3?diffuse?=?_LightColor0.rgb?*?_Diffuse.rgb?*?halfLambert;??????fixed3?color?=?ambient?+?diffuse;??????return?fixed4(color,1.0);??}??
下圖給出了逐頂點漫反射光照、逐像素漫反射光照和半蘭伯特光照的對比效果。
?
?
高光反射
之前我們給出了基本光照模型中高光發射部分的計算公式:
函數reflect(i,n)可以計算反射方向:當給定入射方向i和法線方向n時,reflect函數可以返回反射方向。
?
實踐:逐頂點光照的高光反射效果
代碼如下:
?
Shader?"Unity?Shaders?Book/Chapter6/Specular?Vertex-Level"??{??????Properties??????{??????????_Diffuse("Diffuse",?Color)?=?(1,1,1,1)??????????//用于控制材質的高光反射顏色??????????_Specular("Specular",Color)?=?(1,1,1,1)??????????//用于控制高光區域的大小??????????_Gloss("Gloss",?Range(8.0,256))?=?20??????}????????????SubShader??????{??????????Pass??????????{??????????????//設置光照模式??????????????Tags?{?"LightMode"?=?"Forward"}????????????????????????????CGPROGRAM????????????????????????????#pragma?vertex?vert??????????????#pragma?fragment?frag????????????????????????????#include?"Lighting.cgnic"????????????????????????????//存儲屬性中的變量??????????????fixed4?_Diffuse;??????????????fixed4?_Specular;??????????????float?_Gloss;????????????????????????????//輸入結構體??????????????struct?a2v??????????????{??????????????????float4?vertex?:?????POSITION;??????????????????float3?normal?:?NORMAL;??????????????}????????????????????????????//輸出結構體??????????????struct?v2f??????????????{??????????????????float4?pos?:?SV_POSITION;??????????????????fixed3?color?:?COLOR;??????????????}????????????????????????????//頂點著色器中,計算包含高光反射的光照模型??????????????v2f?vert(a2v?v)??????????????{??????????????????v2f?o;??????????????????o.pos?=?mul(UNITY_MATRIX_MVP,v.vertex);??????????????????fixed3?ambient?=?UNITY_LIGHTMODEL_AMBIENT.xyz;????????????????????????????????????fixed3?worldNormal?=?normalize(mul(v.normal,?(float3×3)_World2Object));??????????????????fixed3?worldLightDir?=?normalize(_WorldSpaceLightPos0.xyz);????????????????????????????????????fixed3?diffuse?=?_LightColor0.rgb?*?_Diffuse.rgb?*?saturate(dot(worldNormal,worldLightDir));??????????????????//入射光線關于表面法線的反射方向。由于CG的reflect函數的入射方向要求??????????????????//是由光源指向交點處的,因此需要對worldLightDir取反后再傳給reflect函數。??????????????????fixed3?reflectDir?=?normalize(reflect(-worldLightDir),worldNormal);??????????????????//_WorldSpaceCameraPos得到世界空間中的攝像機的位置??????????????????//再把頂點位置從模型空間變換到世界空間下,再通過和_WorldSpaceCameraPos相減??????????????????//即可得到世界空間下的視角方向??????????????????fixed3?viewDir?=?normalize(_WorldSpaceCameraPos.xyz?-?mul(_Object2World,?v.vertex).xyz);??????????????????//代入公式得到高光反射的光照部分??????????????????fixed3?specular?=?_LightColor0.rgb?*?_Specular.rgb?*?pow(saturate(dot(reflectDir,?viewDir)),_Gloss);????????????????????????????????????o.color?=?ambient?+?diffuse?+?specular;??????????????}????????????????????????????fixed4?frag(v2f?i)?:?SV_Target??????????????{??????????????????return?fixed4(i.color,?1.0);??????????????}??????????????ENDCG??????????}??????}??????Fallback?"Specular"??}??
得到的效果圖如下:
?
?
實踐:高光逐像素光照
更改逐頂點光照部分Shader代碼
?
Shader?"Unity?Shaders?Book/Chapter6/Specular?Pixel-Level"??{??????Properties??????{??????????_Diffuse("Diffuse",?Color)?=?(1,1,1,1)??????????_Specular("Specular",Color)?=?(1,1,1,1)??????????_Gloss("Gloss",?Range(8.0,256))?=?20??????}????????????SubShader??????{??????????Pass??????????{??????????????Tags?{?"LightMode"?=?"Forward"}????????????????????????????CGPROGRAM????????????????????????????#pragma?vertex?vert??????????????#pragma?fragment?frag????????????????????????????#include?"Lighting.cgnic"????????????????????????????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?=?mul(UNITY_MATRIX_MVP,v.vertex);????????????????????????????????????o.worldNormal?=?mul(v.normal,?(float3×3)_World2Object);??????????????????o.worldPos?=?mul(_Object2World,?v.vertex).xyz;??????????????????o.color?=?ambient?+?diffuse?+?specular;??????????????}????????????????????????????//片元著色器需要計算關鍵的光照模型:??????????????fixed4?frag(v2f?i)?:?SV_Target??????????????{??????????????????fixed3?ambient?=?UNITY_LIGHTMODEL_AMBIENT.xyz;??????????????????fixed3?worldNormal?=?normalize(i.worldNormal);??????????????????fixed3?worldLightDir?=?normalize(_WorldSpaceLightPos0.xyz);????????????????????????????????????fixed3?diffuse?=?_LightColor0.rgb?*?_Diffuse.rgb?*?saturate(dot(worldNormal,worldLightDir));????????????????????????????????????fixed3?reflectDir?=?normalize(reflect(-worldLightDir),worldNormal);??????????????????fixed3?viewDir?=?normalize(_WorldSpaceCameraPos.xyz?-?i.worldPos.xyz);??????????????????fixed3?specular?=?_LightColor0.rgb?*?_Specular.rgb?*?pow(saturate(dot(reflectDir,?viewDir)),_Gloss);??????????????????return?fixed4(ambient?+?diffuse?+?specular,?1.0);??????????????}??????????????ENDCG??????????}??????}??????Fallback?"Specular"??}?? 結果如下:
?
Blinn-Phong 光照模型
公式如下
修改逐像素光照中的部分代碼
?
fixed4?frag(v2f?i)?:?SV_Target??{??????fixed3?ambient?=?UNITY_LIGHTMODEL_AMBIENT.xyz;??????fixed3?worldNormal?=?normalize(i.worldNormal);??????fixed3?worldLightDir?=?normalize(_WorldSpaceLightPos0.xyz);????????????fixed3?diffuse?=?_LightColor0.rgb?*?_Diffuse.rgb?*?saturate(dot(worldNormal,worldLightDir));????????????fixed3?reflectDir?=?normalize(reflect(-worldLightDir),worldNormal);????????????fixed3?viewDir?=?normalize(_WorldSpaceCameraPos.xyz?-?i.worldPos.xyz);????????????fixed3?halfDir?=?normalize(worldLightDir?+?viewDir);????????????fixed3?specular?=?_LightColor0.rgb?*?_Specular.rgb?*?pow(max(0,dot(reflectDir,?halfDir)),_Gloss);??????return?fixed4(ambient?+?diffuse?+?specular,?1.0);??}?? 下圖給出了三種高光反射光照的對比結果
?
可以看出,Blinn-Phong 光照模型的高光反射部分看起來更大、更亮一些。在實際渲染中,絕大多數情況我們都會選擇Blinn-Phong光照模型。需要再次提醒的是,這兩種模型都是經驗模型,也就是說,我們不應該認為Blinn-Phong模型是對“正確的”Phong模型的近似。實際上,在一些情況下,Blinn-Phong模型更符合實驗結果。
?
下表給出了UnityCG.cgnic 中一些常用的幫助函數
注意,類似 UnityXXX 的幾個函數是Unity 5 中新添加的內置函數。
轉載于:https://www.cnblogs.com/kanekiken/p/7616700.html
總結
以上是生活随笔為你收集整理的Unity Shader入门精要学习笔记 - 第6章 开始 Unity 中的基础光照的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。