Unity3D 的物理渲染和光照模型
為什麼地球在兩極嚴寒,而在赤道火熱?這個問題,彷彿與著色器毫不相干,但卻是理解光照模型怎樣工作的基礎。正如這個教程前面部分所解釋的,表面著色器使用數學模型來預測光照在三角形上怎樣反射。總的來說,Unity 引擎支援兩種著色技術,一個是啞光著色器,一個是鏡面材料著色器。前一種對於不透明表面的支援很完美,而後一種則用來模擬反射物件。這些光照模型背後的數學可能非常複雜,但是如果你想創造屬於你自己的光照效果,你就得理解它們是如何工作的。直到 Unity 版本 4.x,預設的漫射光照模型都基於朗伯反射(Lambertian reflectance)的。
漫反射面:郎伯模型
回到最開始的問題,兩極冷的原因就在於它受照射的陽光比赤道區域少。這是由於它們受到太陽的斜射。下圖顯示了八角形兩極區域受到的光線明顯少於正面區域。
藍線代表了正交法向量單位長度。橙線表示了光線的方向。光通量的衰減取決於光線方向與法向向量的夾角。在郎伯模型( Lambertian model )中,它的值等於垂直的入射光線。
其可以表述為:
/[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"}
LightingToon模型計算了光強的朗伯係數NdotL並使用ramp紋理以將其離散化。該例子中僅對映了四級光強階。不同的ramp紋理將細化地獲得不同的toon shading變形。
鏡面: Blinn-Phong模型
朗伯模型並不能模擬鏡面反射材料。這種情況下就需要另一種技術; Unity4.x 採用了 Blinn-Phong 模型. 不需要計演算法向量和光向量的點積, 而是通過和視角的角平分向量來計算 :
數量通過和設定進一步計算。如果你想深入瞭解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 。
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"}
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 著色器與表面著色器沒有什麼區別,僅僅是帶有了一個非常高階的光照模型。
相關文章
- 渲染中的光照著色方式:PBR(Physically Based Rendering,物理基礎渲染)與 傳統經驗渲染
- 傳統光照模型模型
- WidsMob HDR Mac(光照渲染效果照片處理器)Mac
- WidsMob HDR Mac 光照渲染效果照片處理器Mac
- 【Filament】自定義Blinn Phong光照模型模型
- [computer graphics]簡單光照模型(Phong和Blinn-Phong)和明暗處理模型
- Unity中的光源型別(向前渲染路徑進行光照計算)Unity型別
- OpenGL 3D 模型載入和渲染3D模型
- PBR(基於物理的渲染)學習筆記2筆記
- 一個具有邏輯和物理檢視的ER模型設計工具模型
- OpenAI Sora真的有自己的物理模型嗎?OpenAISora模型
- CDM(Conceptual Data Model,概念資料模型)和 PDM(Physical Data Model,物理資料模型)模型
- 基於物理的渲染(PBR)白皮書(二) PBR核心理論與渲染光學原理總結
- AI生成遊戲中基於物理的渲染(PBR)貼圖探索AI遊戲
- OpenGL高階版本學習日誌2:光照模型&材質模型
- Nature|機器學習和物理模型的「雙向奔赴」,3種AI氣候建模方法機器學習模型AI
- SSR後端渲染和CSR前端渲染的區別後端前端
- 物理的枷鎖,桎梏,和束縛
- Unity3D事件系統和EventSystemUnity3D事件
- 前向渲染和延遲渲染
- 水和演算法有何相似?用物理學來理解大語言模型演算法模型
- GPU精粹與Shader程式設計(一):關於基礎物理渲染GPU程式設計
- 近未來世界+全域性光照渲染,絕地求生新遊《PUBG:NEW STATE》正式公佈
- 實現一個前向渲染的Phong模型(一)模型
- OBJ模型檔案的結構、匯入與渲染OBJ模型
- Autodesk 3DS MAX 2022三維動畫、渲染和模型軟體3D動畫模型
- 堆和棧在物理上的區別
- 前端路由和後端路由,前端渲染和後端渲染前端路由後端
- 服務端渲染和客戶端渲染服務端客戶端
- GPU渲染和GDIGPU
- 兩種解決powerdesigner概念模型轉物理模型報欄位重複錯誤的方法模型
- 雲伺服器和物理機的區別伺服器
- 物理安全和網路安全的交點在哪?
- Unity3d的安裝Unity3D
- react和vue的渲染流程對比ReactVue
- Vue 基礎自查——條件渲染和列表渲染Vue
- 「技美之路 第08篇」圖形 2.4 傳統經驗光照模型詳解模型
- 10 管理物理和快照備庫
- 基座模型、聊天模型和指令模型的區別模型