原文地址:Jordan Stevens - Technical Artist

我的小站地址:【翻譯】Write PBR Algorithms In Unity ③

組裝你的PBR Shader Part1:微表面分佈函式(高光函式)

什麼是微表面分佈函式(Normal Distribution Function)?

微表面分佈函式是構成BRDF函式的是哪個核心要素之一。NDF統計學上描述了微表面發現的分佈情況。在我們的使用當中,這個函式作為權重函式來縮放高光以及反射。將NDF看做表面的基礎幾何屬性是非常重要的。讓我們開始在我們的Shader中加一些演算法,這樣我們就可以看到NDF是怎麼對我們的效果產生影響的了。

首先我們要做的就是編寫一個演算法,為了讓這個演算法可見,我們將我們返回的浮點數重寫。

float3

SpecularDistribution

=

specColor

//the algorithm implementations will go here

return

float4

float3

1

1

1

*

SpecularDistribution

rgb

1

);

之後的一些計算環節也會按照這樣的形式出現,當你在計算環節中寫完了演算法,你會在當前位置寫上演算法。如果你要寫一個新的演算法的時候簡單將其註釋掉就完事兒了,結果只會基於沒有註釋的演算法。別擔心,我們會將這種做法改進,以在Unity中更輕鬆地切換不同的演算法,再也不用進行註釋惹。

讓我們從最簡單的blinn-phong演算法開始:

Blinn-Phong NDF

【翻譯】透過Unity學習PBR演算法 ③

Blin近似算出了Phong高光,並將其作為Phong高光模型的最佳化版。

Blin透過半形向量與法向量點乘顯然比計算光的反射方向要快。這個演算法算出來的結果要比Phong更加柔和。

float

BlinnPhongNormalDistribution

float

NdotH

float

specularpower

float

speculargloss

){

float

Distribution

=

pow

NdotH

speculargloss

*

specularpower

Distribution

*=

2

+

specularpower

/

2

*

3。1415926535

);

return

Distribution

}

Blin-Phong演算法並不符合物理規律,但是依舊可以產生可信的高光,用於一些特殊的美術用途。將上面的演算法放入演算法階段,並且將下面的程式碼放入到片元階段。

SpecularDistribution

*=

BlinnPhongNormalDistribution

NdotH

_Glossiness

max

1

_Glossiness

*

40

));

如果你往你的shader中輸入一個光滑度你會看到物體會有一個高光來表現其法線分佈,而剩下的地方都是黑色。這樣我們可以更輕鬆地除錯我們的shader。上方程式碼中的40只是我能夠讓函式提供更大的範圍,但是對每個人來說並不是一個確定的值。

Phong NDF

【翻譯】透過Unity學習PBR演算法 ③

float

PhongNormalDistribution

float

RdotV

float

specularpower

float

speculargloss

){

float

Distribution

=

pow

RdotV

speculargloss

*

specularpower

Distribution

*=

2

+

specularpower

/

2

*

3。1415926535

);

return

Distribution

}

Phong演算法是另外一種非物理演算法,但是它產生比Blin近似出更好的效果。下面是實現的例子:

SpecularDistribution *= PhongNormalDistribution(RdotV, _Glossiness, max(1,_Glossiness * 40));

為了與Blin-Phong進行對比,不要過於在意40這個值。

Beckman NDF

【翻譯】透過Unity學習PBR演算法 ③

Beckman微表面分佈函式是一個更加高階的函式,並且將我們的粗糙度帶入計算當中。粗糙度和法線與半形向量的點乘共同對物體表面的法線分佈進行近似。

float

BeckmannNormalDistribution

float

roughness

float

NdotH

{

float

roughnessSqr

=

roughness

*

roughness

float

NdotHSqr

=

NdotH

*

NdotH

return

max

0。000001

,(

1。0

/

3。1415926535

*

roughnessSqr

*

NdotHSqr

*

NdotHSqr

))

*

exp

((

NdotHSqr

-

1

/

roughnessSqr

*

NdotHSqr

)));

}

該函式的實現非常簡單。

SpecularDistribution *= BeckmannNormalDistribution(roughness, NdotH);

在Beckman光照模型處理物體表面的時候需要注意的一點是。你可以從上面的影象中看出,Beckman光照模型隨著光滑度的變化在慢慢變化,直到一個高光點聚攏在了一個確定的點上。當表面的光滑度增加的時候反射高光聚攏在了一起,產生了非常不錯的從粗糙到光滑的藝術效果。這種表現效果在早期的粗糙材質中非常受歡迎,對於塑膠中光滑度的渲染也非常不錯。

Gaussian NDF

【翻譯】透過Unity學習PBR演算法 ③

Gaussian NDF不像其他的一些計算模型那樣受歡迎,因為它傾向於渲染出更柔和的反射高光,而不是渲染光滑度更高的材質。對於藝術效果來說是可行的,但是對於它是否更符合自然物理還存在著爭論。

float

GaussianNormalDistribution

float

roughness

float

NdotH

{

float

roughnessSqr

=

roughness

*

roughness

float

thetaH

=

acos

NdotH

);

return

exp

-

thetaH

*

thetaH

/

roughnessSqr

);

}

這個演算法的實現與其他的NDF非常相似,都是依賴於粗糙度以及表面發現於半角向量的點乘。

SpecularDistribution

*=

GaussianNormalDistribution

roughness

NdotH

);

GGX NDF

【翻譯】透過Unity學習PBR演算法 ③

GGX是最受歡迎的光照模型之一,當前大多數的BRDF函式都依賴於它的實現。GGX是由Bruce Walter和Kenneth Torrance發表出來的。在他們[論文]中的許多演算法都是目前大量被使用到的。

float

GGXNormalDistribution

float

roughness

float

NdotH

{

float

roughnessSqr

=

roughness

*

roughness

float

NdotHSqr

=

NdotH

*

NdotH

float

TanNdotHSqr

=

1

-

NdotHSqr

/

NdotHSqr

return

1。0

/

3。1415926535

*

sqr

roughness

/

NdotHSqr

*

roughnessSqr

+

TanNdotHSqr

)));

}

SpecularDistribution

*=

GGXNormalDistribution

roughness

NdotH

);

The specular highlight of the GGX Algorithm is very tight and hot, while still maintaining a smooth distribution across the surface of our ball。 這是一個簡單的例子來解釋為什麼GGX演算法經常將反射率變換為金屬度來表示。

Trowbridge-Reitz NDF

【翻譯】透過Unity學習PBR演算法 ③

Trowbridge-Reitez方法在GGX相同的論文中被髮表出來。主要可見的區別就是物體邊緣的高光比GGX的方式產生的銳利邊緣要更加柔和了。

float

TrowbridgeReitzNormalDistribution

float

NdotH

float

roughness

){

float

roughnessSqr

=

roughness

*

roughness

float

Distribution

=

NdotH

*

NdotH

*

roughnessSqr

-

1。0

+

1。0

return

roughnessSqr

/

3。1415926535

*

Distribution

*

Distribution

);

}

類似地,Trobridge-reitz方法也依賴於粗糙度與半形向量和法線的點乘。

SpecularDistribution

*=

TrowbridgeReitzNormalDistribution

NdotH

roughness

);

Trowbridge-Reitz Anisotropic NDF

【翻譯】透過Unity學習PBR演算法 ③

各向異性的NDF方程產生了各向異性的表面描述。它允許我們做出有各項異性效果的表面。這個函式我們需要新增一個新的屬性來確定各向異性的強度。

我們的屬性

_Anisotropic

“Anisotropic”

Range

-

20

1

))

=

0

我們的變數

float

_Anisotropic

float

TrowbridgeReitzAnisotropicNormalDistribution

float

anisotropic

float

NdotH

float

HdotX

float

HdotY

){

float

aspect

=

sqrt

1。0

h

-

anisotropic

*

0。9

h

);

float

X

=

max

。001

sqr

1。0

-

_Glossiness

/

aspect

*

5

float

Y

=

max

。001

sqr

1。0

-

_Glossiness

*

aspect

*

5

return

1。0

/

3。1415926535

*

X

*

Y

*

sqr

sqr

HdotX

/

X

+

sqr

HdotY

/

Y

+

NdotH

*

NdotH

));

}

各向異性的材質與各向同性的材質的其中一個區別是是否需要使用切線或者副法線來描述表面。上圖表現出來的是各向異性為1時的效果。

SpecularDistribution

*=

TrowbridgeReitzAnisotropicNormalDistribution

_Anisotropic

NdotH

dot

halfDirection

i

tangentDir

),

dot

halfDirection

i

bitangentDir

));

Ward AnisoTropic NDF

【翻譯】透過Unity學習PBR演算法 ③

Ward方式的各向異性BRDF函式產生了與上一個方式不同的效果。其高光變得更加柔和,隨著光滑度的降低高光液消失得更快了。

float

WardAnisotropicNormalDistribution

float

anisotropic

float

NdotL

float

NdotV

float

NdotH

float

HdotX

float

HdotY

){

float

aspect

=

sqrt

1。0

h

-

anisotropic

*

0。9

h

);

float

X

=

max

。001

sqr

1。0

-

_Glossiness

/

aspect

*

5

float

Y

=

max

。001

sqr

1。0

-

_Glossiness

*

aspect

*

5

float

exponent

=

-

sqr

HdotX

/

X

+

sqr

HdotY

/

Y

))

/

sqr

NdotH

);

float

Distribution

=

1。0

/

4。0

*

3。14159265

*

X

*

Y

*

sqrt

NdotL

*

NdotV

));

Distribution

*=

exp

exponent

);

return

Distribution

}

就像Trowbridge-Reitz函式,Ward函式也需要切線與副法線(切線)資料,而且依賴於光與法線的點乘和視覺方向與法線的點乘。

SpecularDistribution

*=

WardAnisotropicNormalDistribution

_Anisotropic

NdotL

NdotV

NdotH

dot

halfDirection

i

tangentDir

),

dot

halfDirection

i

bitangentDir

));