URP | PBR材質(zhì)(三)-自定義PBR
內(nèi)容偏多

注意 因為太復雜,本人才疏學淺有很多錯誤的地方,
目的,以Unity默認URP Shader效果為參考效果。
基礎(chǔ)公式
PBR基礎(chǔ)公式

光照的公式我們可以總結(jié)為:
最終結(jié)果 = 直接光的漫反射 + 直接光的高光反射 ?+ 間接光的高光反射 + 間接光的漫反射 + 其他光照效果(自發(fā)光什么什么等)
D ? 法線分布函數(shù):描述微觀法線N和半角向量H的趨同性比重。粗糙度越低,物體光滑度越高,
G ?高光
F ?菲尼爾
PBR 流程
Metallic粗糙度工作流
貼圖 (紋理,法線,環(huán)境遮蔽,金屬度,粗糙度,自發(fā)光)
Specular工作流
紋理,法線,環(huán)境遮蔽,高光,自發(fā)光
我們這次以及金屬粗糙度光照流程。
準備基礎(chǔ)Shader
準備Shader ,默認Shader 有基礎(chǔ)屬性。
我們前輸入需要準備的貼圖 變量
這里定義 顏色貼圖,法線貼圖,Mask貼圖,
Mask 貼圖
金屬度貼圖儲存在 = R通道
粗糙度貼圖儲存到 = ?G通道
AO貼圖儲存到 = ?B通道
自發(fā)光貼圖儲存到 = Alpha
目前我們需要數(shù)值,后面替換成貼圖。
聲明定義變量,貼圖
片元著色器階段輸出
輸出貼圖顏色
輸出顏色貼圖

計算法線
因為我們使用的是法線貼圖,我們在頂點著色器階段求出的是模型的法線。
我們需要把這兩融合起來。
在頂點著色器簡單求出切線,副切線等。
對應(yīng)的片元結(jié)構(gòu)體中輸出
在片元著色器階段計算出法線
效果

這樣N就計算完成

測試 NormalScale 控制法線強度是否起作用。

擴展 UnpackNormalScale 函數(shù)
UnpackNormalScale 法線紋理采樣
首先呢,我們都知道法線是一個三維向量,其每個分量的范圍都是(-1,1),但在我們存儲的法線圖中每個通道的范圍是(0,1),這其中存在一個轉(zhuǎn)換關(guān)系。以R通道分量為例:

所以逆過程就是先乘以2再加上1
再者,Unity對法線貼圖的壓縮算法采用的格式被稱為DXT5nm,這種格式的突出特點是利用A通道存儲x分量,利用G通道存儲y分量,因為這兩個通道的bit位數(shù)最多(RGBA分別為5,6,5,8)。所以需要我們反映射,至于Z分量,由于法線向量是單位向量,所以我們可以利用幾何關(guān)系求得Z分量。

環(huán)境準備
準備10個球體,10個材質(zhì)球

指定基礎(chǔ)貼圖,顏色貼圖,法線貼圖,粗糙度,金屬度使用數(shù)值調(diào)整。


直接光高光反射部分
鏡面反射部分

鏡面反射部分包含三個函數(shù) ? D ?F ?G ,分母部分還有一個標準化因子. $4(w_o ? n)(w_i ? n)$

高光反射 | D
法線分布函數(shù)GGX(Normal Distribution Function)

它是描述微觀法線N和半角向量H的趨同性比重。

實現(xiàn)粗糙度高光過程
在Shader里定義一個函數(shù),計算D
我們這里使用上面的12shader
這個函數(shù)是在計算D ? ?需要輸入2個變量,一個是NdotH , ?一個是粗糙度變量。
需要的基礎(chǔ)數(shù)據(jù)
我們需要計算出 NdotH
在片元著色器階段獲取燈光信息,歸一化傳過來的的N V,使用V + L計算出H
在定義一個變量擴展粗糙度
目前需要對粗糙度進行映射處理
這里 1 - _Roughness ?反向粗糙度 。
單獨輸出D
輸出看一下結(jié)果,
現(xiàn)在調(diào)整粗糙度就可以看到高光的變化了。

注意: 這里是高光范圍,所以需要限制最小值也是有高光點的。


幾何遮蔽 | G
幾何函數(shù)G,(Geometry function)
即核心方程的G項,它是描述入射射線(即光照方向)和出射射線(視線方向)被自己的微觀幾何形狀遮擋的比重。

? ??? ? ?
使用粗糙度作為幾何函數(shù)的輸入?yún)?shù)
這里的K是做a基于幾何函數(shù)是針對直接光照還是針對IBL光照的重映射
擴展 這里幾何函數(shù)是因為需要考慮觀察方向(V)和光線入射方向(L)的遮蔽情況

幾何函數(shù)

幾何函數(shù)具有兩種主要形式:G1和G2
我們前求出K的值,在計算G1 G2相乘。
這里 K的值 使用擬合曲線:float k = pow(1 + roughness, 2) * 0.5;
這里我們需要哪些屬性,,
a 粗糙度
NdotL
NdotV
再去定義真正的G項,把NL和NV代入子項,相乘即可
注意 :K系數(shù),在直射光和間接光的計算方式略有不同,代碼里是直接光的,而間接光的K=pow(roughness,2)/2,這里要注意。
在片元著色器階段計算 NdotL ? NdotV
效果
粗糙度 0.1 - 1.0


菲涅爾函數(shù) | F
float hl = max(saturate(dot(halfDir, lightDir)), 0.0001);
菲涅爾反射(Fresnel Reflectance)。光線以不同角度入射會有不同的反射率。相同的入射角度,不同的物質(zhì)也會有不同的反射率。萬物皆有菲涅爾反射。F0是即 0 度角入射的菲涅爾反射值。大多數(shù)非金屬的F0范圍是 0.02 - 0.04,大多數(shù)金屬的F0范圍是 0.7 - 1.0。

Schlick 的模型的公式

但是由于我們需要的法線方向并不是模型本身的宏觀法線n,而是經(jīng)過D項篩選通過的微觀法線H,故需把N改成H。
UNITY 對這個計算進行了優(yōu)化,視線方向V換成了L,如下所示,這是我們所使用的函數(shù)。

其中的5次方計算量比較大,把它變成自然對數(shù)函數(shù)進行計算可以節(jié)省計算量,后續(xù)文章里所有的5次方計算都可以換算成對數(shù)計算。

F 函數(shù)
這里輸入 HdotL在輸入一個F0 計算
那在片元著色器階段計算HdotL和F0
效果
無貼圖 金屬度 0 -1

有顏色貼圖

注意:菲涅爾效果是由金屬度影響的。
這里金屬度給到0的時候是沒有顏色貼圖,


合并輸出
直接光高光部分我們都計算完成,把D,G,F代入公式,計算出高光部分。

這里計算出的高光點數(shù)值是大于1的,如果沒有打開后處理的泛光效果的話,也看不出來,但是如果打開之后,就會發(fā)現(xiàn)效果不對。我這里強行把它限制到了0到1的區(qū)間了。
效果
公式結(jié)果 金屬度 0-1 粗糙度 0-1

注意:這里的黃色是貼圖的顏色
增加光照顏色
注意:這里經(jīng)過半球積分后會乘PI,不要丟了;注意這里并未再次乘KS,因為KS=F,已經(jīng)計算過了一次不需要重復計算。
金屬度=1 粗糙度 0-1

金屬度 = 0 粗糙度 0-1

金屬度 0-1 粗糙度 0-1


直接光漫反射部分
上面準備好數(shù)據(jù),計算一下直接光漫反射部分。
漫反射部分
獲取計算漫反射需要的數(shù)據(jù)
漫反射計算

k_d? = ?漫反射部分占比率
k_s? = ?反射部分占比率
注意: 由于分母帶了PI,而半球積分后會乘PI,兩者就約掉了這就沒有寫。
入射光線和出射光線最大是180度,計算的時候是在一個半球空間計算。

這里的C = Albedo
擴展 這里為什么不叫Diffuse了,反而叫Albedo? 這是因為,Albedo 都是顏色信息,原來比如需要做一些陰影到貼圖上,PBR完全不需要處理陰影,只需要顏色,所以起名叫Albedo
而除以?π 的原因是為歸一化BRDF,從而使得BRDF符合能量守恒的條件,不會導致反射出去的光線比入射的光線還要多的情況。
所以上面這個公式可以這樣表示
這里我們現(xiàn)在沒有計算出kd ,前計算 NdaoL * 光照顏色
效果

這里處理一下漫反射,我們上面只是簡單的制作的漫反射效果,我們還需要和金屬度顏色關(guān)聯(lián)到一起。
效果
金屬度 = 0-1 ? ?粗糙度 1


合并直接光漫反射和直接光高光反射
效果
金屬度 0- 1 ? 粗糙度 0-1


金屬度 ?1 ?粗糙度 0- 1


粗糙度 1 金屬度 0-1


代碼

間接光漫反射部分
球體型光照
在場景中創(chuàng)建球型光照,使用函數(shù)獲取球型光照的顏色。
輸出結(jié)果看一下效果
效果

IndirF_Function
定義一個函數(shù) 計算間接光的F
分別計算出間接光的 ks kd效果
輸出間接光漫反射
金屬 0 粗糙度 0-1

注意:金屬度是1 都是黑色。

間接光鏡面反射部分
間接光照的高光反射本質(zhì)是對于反射探針(360全景相機)拍的一張圖進行采樣,把采樣到的顏色當成光照去進行計算,這種光照稱為基于圖像的光照IBL(Image-Based Lighting),前面的漫反射也是IBL,關(guān)于IBL有非常復雜的理論,
URP | Reflection Probes 反射探針 - 嗶哩嗶哩 (bilibili.com)
前定義一個函數(shù),對Cube采樣,根據(jù)不同的粗糙度進行采樣。
注意:這里也考慮AO的效果
單獨輸出看結(jié)果
效果
粗糙度

還需要間接光高光影響因子,這里就使用Unity默認的方法, unity使用的曲線擬合去得到結(jié)果.
smoothness ? 這里就單獨粗糙度,因為前面的粗糙度我們重新映射過了。

輸出查看高光影響因子
效果

在計算間高光反射接光
效果
金屬度 1 粗糙度 0-1

金屬度 0 ?粗糙度 0-1

合并輸出,
效果
金屬度 1 粗糙度 0-1

金屬度 0 ?粗糙度 0-1

效果對比
這是自定義和 URP 自帶的Lit Shader對比。

全代碼
總結(jié)
PBR其實和其他光照模型一樣,都是根據(jù)入射光和出射光計算,只是使用了更物理的算法BRDF算法。
漫反射部分,是在半球空間計算,C = 顏色貼圖,
Fresnel 需要考慮金屬和非金屬對光的反射區(qū)別,F(xiàn)resnel的公式。
UE4和unity的粗糙度反射是不一樣的,
UE4

Unity

內(nèi)容太多分開,下面會進行代碼整理完善。