歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Unity3D 的物理渲染和光照模型

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

Unity3D--定義攝像機的投影矩陣 http://www.linuxidc.com/Linux/2013-04/82566.htm

Unity3D 幾個默認函數的區分 http://www.linuxidc.com/Linux/2012-07/66012.htm

Unity3D 一個設置方向鍵移動和空格起跳的腳本 http://www.linuxidc.com/Linux/2012-07/66011.htm

Unity3D 給對象(gameObject)添加腳本代碼 http://www.linuxidc.com/Linux/2012-07/66010.htm

Unity3D中實現動態加載Resources目錄外的資源 http://www.linuxidc.com/Linux/2014-06/103320.htm

漫反射面:郎伯模型

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

藍線代表了正交法向量單位長度。橙線表示了光線的方向。光通量的衰減取決於光線方向與法向向量的夾角。在郎伯模型( 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 著色器與表面著色器沒有什麼區別,僅僅是帶有了一個非常高級的光照模型。

Unity3D 的詳細介紹:請點這裡
Unity3D 的下載地址:請點這裡

英文原文:Physically Based Rendering and lighting models in Unity3D

Copyright © Linux教程網 All Rights Reserved