Unity3D 的物理渲染和光照模型

oschina發表於2015-07-18

為什麼地球在兩極嚴寒,而在赤道火熱?這個問題,彷彿與著色器毫不相干,但卻是理解光照模型怎樣工作的基礎。正如這個教程前面部分所解釋的,表面著色器使用數學模型來預測光照在三角形上怎樣反射。總的來說,Unity 引擎支援兩種著色技術,一個是啞光著色器,一個是鏡面材料著色器。前一種對於不透明表面的支援很完美,而後一種則用來模擬反射物件。這些光照模型背後的數學可能非常複雜,但是如果你想創造屬於你自己的光照效果,你就得理解它們是如何工作的。直到 Unity 版本 4.x,預設的漫射光照模型都基於朗伯反射(Lambertian reflectance)的。

漫反射面:郎伯模型

回到最開始的問題,兩極冷的原因就在於它受照射的陽光比赤道區域少。這是由於它們受到太陽的斜射。下圖顯示了八角形兩極區域受到的光線明顯少於正面區域。

Unity3D 的物理渲染和光照模型

藍線代表了正交法向量單位長度。橙線表示了光線的方向。光通量的衰減取決於光線方向與法向向量的夾角。在郎伯模型( Lambertian model )中,它的值等於垂直的入射光線。

Unity3D 的物理渲染和光照模型

其可以表述為:

/[I= /left /| L /right /| /, cos /alpha = cos /alpha/]

式中, /left /| L /right /| 為 L(這個量是之前定義過的)的長度 ,然後 /alpha 為 N 和 L 的夾角。這個運算元在向量代數中被叫做點積 ,在前邊的文章中也已經簡單介紹了。正式的寫法應該是這個樣子的:

/[A /cdot B = /left /| A /right /| /, /left /| B /right /| /, cos /alpha/]

在 Cg/HLSL 中都可以使用點操作符。其將返回一個從 -1 到 1 的數,當兩向量正交時將返回 0,並且當它們平行時 /pm 為 1。我們將使用其作為一個乘法系數,代表了從一個光源接收到多少三角光(三角函式光,即正弦餘弦的意思,意會!)。

朗伯著色器(Lambertian shader)

我們現在已經有必要來理解一個朗伯模型在著色器中是如何實現的。Cg/HLSL 允許用一個自定義的函式替換標準的朗伯模型。在第8行,在指令 #pragma surface 中使用 SimpleLambert,強制著色器搜尋叫做 LightingSimpleLambert 的函式:

Shader "Example/SimpleLambert" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
}
SubShader {
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf SimpleLambert

struct Input {
float2 uv_MainTex;
};

sampler2D _MainTex;
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
}

half4 LightingSimpleLambert (SurfaceOutput s, half3 lightDir, half atten) {
half NdotL = dot (s.Normal, lightDir);
half4 c;
c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten * 2);
c.a = s.Alpha;
return c;
}

ENDCG
} 
Fallback "Diffuse"
}
Shader "Example/SimpleLambert" {Properties {_MainTex ("Texture", 2D) = "white" {}}SubShader {Tags { "RenderType" = "Opaque" }CGPROGRAM#pragma surface surf SimpleLambertstruct Input {float2 uv_MainTex;};sampler2D _MainTex;void surf (Input IN, inout SurfaceOutput o) {o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;}half4 LightingSimpleLambert (SurfaceOutput s, half3 lightDir, half atten) {half NdotL = dot (s.Normal, lightDir);half4 c;c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten * 2);c.a = s.Alpha;return c;}ENDCG}Fallback "Diffuse"}

從19到25行 展示了朗伯模型如何簡單地在一個表面著色器中再實現。NdotL 為光顏色的強度係數(相乘)。引數衰減器用於調製光強。其需要被兩個量乘是最初 Unity3D 用於模擬某些特效的技巧。這在 Aras Pranckevičius 中有解釋,在Unity4中留存了下來為了向後相容。最終在 Unity5 進行了修復,所以如果你在 Unity5 上再實現一個朗伯模型的時候,僅僅只要乘上一個量就行啦。

理解標準發光模型的原理是改變其不可或缺的步驟。許多可選的著色技術事實上仍然使用朗伯模型作為其第一步。

Toon shading

最近在遊戲中常用的風格之一即是Toon shading(又稱 cel shading).這是一種非逼真渲染風格,通過改變了光在一個模型上反射實際情況來給人以手繪的感覺。為了達到這樣的效果,我們需要用一個自定義模型來替換至今使用的標準光照模型。最常見用於達到這種效果的方法就是使用加性紋理,在下面的著色器中叫做_RampTex。

Shader "Example/Toon Shading" {
Properties {
_MainTex ("Texture", 2D) = "white" {}
_RampTex ("Ramp", 2D) = "white" {}
}
SubShader {
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Toon

struct Input {
float2 uv_MainTex;
};
sampler2D _MainTex;
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
}

sampler2D _RampTex;
fixed4 LightingToon (SurfaceOutput s, fixed3 lightDir, fixed atten)
{
half NdotL = dot(s.Normal, lightDir); 
NdotL = tex2D(_RampTex, fixed2(NdotL, 0.5));

fixed4 c;
c.rgb = s.Albedo * _LightColor0.rgb * NdotL * atten * 2;
c.a = s.Alpha;

return c;
}

ENDCG
} 
Fallback "Diffuse"
}
Shader "Example/Toon Shading" {Properties {_MainTex ("Texture", 2D) = "white" {}_RampTex ("Ramp", 2D) = "white" {}}SubShader {Tags { "RenderType" = "Opaque" }CGPROGRAM#pragma surface surf Toonstruct Input {float2 uv_MainTex;};sampler2D _MainTex;void surf (Input IN, inout SurfaceOutput o) {o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;}sampler2D _RampTex;fixed4 LightingToon (SurfaceOutput s, fixed3 lightDir, fixed atten){half NdotL = dot(s.Normal, lightDir);NdotL = tex2D(_RampTex, fixed2(NdotL, 0.5));fixed4 c;c.rgb = s.Albedo * _LightColor0.rgb * NdotL * atten * 2;c.a = s.Alpha;return c;}ENDCG}Fallback "Diffuse"}

Unity3D 的物理渲染和光照模型

LightingToon模型計算了光強的朗伯係數NdotL並使用ramp紋理以將其離散化。該例子中僅對映了四級光強階。不同的ramp紋理將細化地獲得不同的toon shading變形。

Unity3D 的物理渲染和光照模型

鏡面: Blinn-Phong模型

朗伯模型並不能模擬鏡面反射材料。這種情況下就需要另一種技術; Unity4.x 採用了 Blinn-Phong 模型. 不需要計演算法向量和光向量的點積, 而是通過和視角的角平分向量來計算 :

Unity3D 的物理渲染和光照模型

數量通過和設定進一步計算。如果你想深入瞭解Unity中光照模型的計算, 可以下載其內建著色器的原始碼. 朗伯和Blinn-Phong表面函式都是在Lighting.cginc檔案中計算的。而在 Unity5 中需要在遺產著色器(Legacy shaders)中尋找。

在Unity5中物理渲染

就像本文在前面提出的那樣, Uniy4.x使用朗伯光照模型作為其預設著色器。Unity5中發生了改變, 其引入了 物理渲染 (PBR). 這個名字聽起來相當有趣啊, 但是與其它光照模型沒有什麼不同。相比於朗伯反射, PBR提供了一個更加逼真的光線物體作用模型。術語physically 來源於,PBR考慮了材料的物理屬性, 比如能量守恆以及光的散射。Unity5為藝術家和開發者提供了兩種不同的方法,用於建立他們的PBR 材料: 金屬工作流 和 鏡面工作流。在前者中,一個材料對光的反射取決於其是什麼樣的金屬(或者說,含有多少金屬的量)。

簡單來說就是,光是電磁波啊,其行為因接觸到的是導體還是絕緣體而不同(電磁波唱由電場和磁場組成,但電場屬性實際上比磁場屬性更重要)。在鏡面工作流中, 所提供的是鏡面對映。儘管被當做兩個不同的東西呈現出來, 金屬材料和鏡面材料實際上以不同的方式初始化同一個著色器;Marmoset 上有一個很好的例程展現了同樣的材料如何通過金屬和鏡面工作流分別建立。這也是為什麼當第一次接觸 Unity5 著色器時會因為原始碼中出現了同一事物卻有兩個工作流現象時會產生誤解。 Joe Wilson 建立了一個相當清晰的例程來知道我們的藝術家:如果你想學習怎麼通過 PBR 建立非常逼真的材質,這將是非常好的開始喲。如果需要更詳細的技術資訊,在 Unity5 部落格裡關於 PBR 的地方猛戳a very well done primer 。

Unity3D 的物理渲染和光照模型

Unity5 中新光照模型名字既簡單又標準。取這個名字是因為現在 PBR 是每一個 Unity3D 中新建物件的初始材料。而且,每一個新建的著色器檔案會被自動配置為一個 PBR 表面著色器:

Shader "Custom/NewShader" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200

CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows

// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0

sampler2D _MainTex;

struct Input {
float2 uv_MainTex;
};

half _Glossiness;
half _Metallic;
fixed4 _Color;

void surf (Input IN, inout SurfaceOutputStandard o) {
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
ENDCG
} 
FallBack "Diffuse"
}
Shader "Custom/NewShader" {Properties {_Color ("Color", Color) = (1,1,1,1)_MainTex ("Albedo (RGB)", 2D) = "white" {}_Glossiness ("Smoothness", Range(0,1)) = 0.5_Metallic ("Metallic", Range(0,1)) = 0.0}SubShader {Tags { "RenderType"="Opaque" }LOD 200CGPROGRAM// Physically based Standard lighting model, and enable shadows on all light types#pragma surface surf Standard fullforwardshadows // Use shader model 3.0 target, to get nicer looking lighting#pragma target 3.0sampler2D _MainTex;struct Input {float2 uv_MainTex;};half _Glossiness;half _Metallic;fixed4 _Color;void surf (Input IN, inout SurfaceOutputStandard o) {// Albedo comes from a texture tinted by colorfixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;o.Albedo = c.rgb;// Metallic and smoothness come from slider variableso.Metallic = _Metallic;o.Smoothness = _Glossiness;o.Alpha = c.a;}ENDCG}FallBack "Diffuse"}

Unity3D 的物理渲染和光照模型

14行將告知 Unity3D 該表面著色器將使用 PBR 光照模型。17行 意味著該著色器將使用高階特性,因而其將不同在落後的硬體上使用。同樣的, SurfaceOutput 也不能同 PBR 一起使用;而是必須使用  SurfaceOutputStandard

PBR 表面輸出

SurfaceOutputStandard 中不光是 Albedo,Normal,Emission 和 Alpha 這些屬性,還有三個新的:

  • 半金屬性: 物體該具有怎樣的金屬含量。同常為0或1,但對於一些奇怪的材料可以使用中間的值。這個將決定光如何在材料上發生反射;
  • 半平滑性:決定表面平滑程度, 從0到1;
  • 半吸收性:指定 AO 特效大小。

你應當通過 SurfaceOutputStandardSpecular 使用鏡面流,其使用 float3 鏡面替換了半金屬性注意當朗伯反射存在一個半鏡面場,其在 PBR 中的鏡面屬性就是一個 float3。這符合鏡面反射光波的 RGB 顏色值屬性 。

Unity 中使用的著色技術

迄今為止已經介紹了四種不同的著色技術。為了避免混淆,可以參考下表中所示,順序分別為:著色技術,表面著色器名,表面輸出結構體名和內建著色器名稱。

Unity4 及以下 Unity5 及以上
漫射 Lambertian reflectance

Lambert, SurfaceOutput

Bumped Diffuse

Physically Based Rendering (Metallic)

Standard, SurfaceOutputStandard

Standard

反射 Blinn–Phong reflection

BlinnPhong, SurfaceOutput

Bumped Specular

Physically Based Rendering (Specular)

Standard, SurfaceOutputStandardSpecular

Standard (Specular setup)

PBR 背後的方程非常複雜。如果你對背後的數學比較感興趣,維基百科中的渲染方程這篇文章 將是非常好的起點。

如果你匯入 Unity3D 包 (包含了該例程中用到的著色器), 你將注意到內建 “Bumped Diffuse” 著色器是怎麼產生一個與其最初實現“Simple Lambert”非常不同的結果。這是因為 Unity3D 的著色器增加了額外的特性,比如正常的對映。

結論

本文介紹了用於表面著色的自定義光照模型。簡單通過一個關於如何修改獲得不同特性的真例項子解釋了朗伯和 Blinn-Phong 模型。有必要注意到單純的漫射材料實際上在生活中是不存在的:即使是你所想到的最鈍的材料也會有一些鏡面反射。漫射材料在過去中非常普遍,因為計算鏡面反射開銷太大。

本文也介紹了什麼是 PBR,以及在 Unity5 中如何使用。PBR 著色器與表面著色器沒有什麼區別,僅僅是帶有了一個非常高階的光照模型。

 

相關文章