Chango的數學Shader世界(七)水波模擬-透明水面,菲涅爾(Fresnel)效應

目的:

解析,改進,批評一個國外免費透明水面Shader,進一步瞭解Shader背後的物理原理。

參考:

菲涅爾反射

分析:

我將原水面Shader一再簡化,從中抽取最主要的部分,忽略細枝末節,並改掉缺陷,指出不足。

簡化版:

1.造型

其中,頂點偏移,也就是波的造型,是用貼圖製造的,一目瞭然:

就是一張高度圖經過4方向擾動,乘以波高度。然後它再乘上頂點法線在世界座標系上的Z軸高度,這個莫名奇妙的操作會使得Normal朝上的頂點的波高更加高,而偏的更加低。我認爲編者腦子裏也不清楚,所以我修改了,直接乘以(0,0,1)。如果你的高度圖只有b通道,這個(0,0,1)都不用乘。

2.Fresnel_Function

再觀原材質圖,你一定會注意到我們的主角,一個叫Fresnel_Function的節點。

UE4這樣起名字,也真是大言不慚,一會我們會說到。

這個節點,涉及到一個現象,菲涅爾效應。國外一般不稱爲效應,搜索Fresnel equations

現象倒是很簡單:

對着玻璃看,越斜着看,它反射越多,越不透明,越像鏡子;

正着看,反射越少,就越透明。

我們在生活中常常見到過這種現象:

(我懷疑夏天遠方道路反光,也是這個原因)

總之,這是個在各個材質上都很普遍發生的現象,包括水面。

如果說我們之前的不透明水面本質就是鏡子,那我們這次的透明水面本質就是玻璃。

真正的Fresnel方程比較複雜,可脫胎於麥克斯韋電磁學方程

對於透明物體:

然而UE4的Fresnel_Function節點的方程並不根據實際。它利用視線與平面越垂直,反射越少這一點,進行了粗略模擬。這個方程也是圖形學中常見的:

F=(1-(V\cdot N))^{power}

其中V向量是像素點到相機位置的向量,也就是光照模型中常見的View向量,與法線N起點一致。power用來控制效果,顯然由於1-V·N在[0,1]間,power越大,那麼越垂直於N的V的F才越大。在下圖中,也就是當power越來越大,黑色會擴大,而白環會減少,慢慢收縮到最邊緣那些法線十分垂直於視線的頂點上(白色爲1,黑色爲0):

顯然這個函數可以用來做邊緣光等其他特效。

那麼,我們回過頭來看透明水波材質。它的Fresnel節點的power賦予了5,十分接近邊緣:

注意:

以下簡稱Fresnel_Fuction(...)爲Fresnel(power)。

以下稱V垂直於N的情況爲“垂視”

3.金屬度(Metallic)

原材質給金屬度設置的是0。可以理解,因爲金屬材質不發生菲涅爾效應,而且金屬度越高,透明金屬材質越丟失BaseColor。但金屬度太低,水的透明和反光也就低了一個層次。看下金屬度爲1的水面,按需求和風格調整即可:

(油膩的反光又出現了)

4.反光度(Specular)

反光直接給1,沒什麼好說的。給0就失去了波光粼粼的效果。

5.粗糙度(Roughness)

回看透明水波材質。它的粗糙度被設置爲:

Lerp(0.1,0,Fresnel(5))

也就是越垂視的地方粗糙度越爲0,完全光滑;其他地方爲0.1,也很光滑,但較邊緣差了點。

對於對面,從直覺上來說是這樣,而且Roughness影響反射光計算,越光滑反射越多,符合菲涅爾效應。

實際效果上對比不明顯。

6.不透明度(Opacity)

Lerp(0.5,1,Fresnel(5))

越垂視,越不透明。也符合菲涅爾效應。

7.折射率(Refraction)

Lerp(1.1,1.3,Fresnel(5))

在介質密度不同的表明,光發生折射。從低密度到高密度,折射角小於入射角。

回憶初高中物理,折射率定律。國外更一般叫成斯涅爾定律(Snell's Law)

\frac{n2}{n1}=\frac{sin(\theta _{1})}{sin(\theta _{2})}

由於真空密度最低,或者說真空光速最快,所以所有的材質折射率都大於1。

從真空射入某材質:

\frac{n_{out}}{n_{vacuum}=1}=\frac{sin(\theta _{in})}{sin(\theta _{out})}=c/v

透明水波Shader認爲越垂視,折射率越高,這是對的。首先直覺上,越彎曲的玻璃,折射越厲害。

其次,你可以認爲空氣與水折射率不變,當入射角變大,那麼折射率一定要變大。

或者你可以認爲折射率=c/v,在垂視的地方,光路更長,相當於v更小,所以折射率更大了。

8.風格化法線

你可能會發現我們這個水波像是三角透明玻璃組成的,十分風格化。放大之後是這樣的:

原因是這個透明水波Shader的法線。

圖中用了一個函數稱爲Shine_func,打開後發現實現十分簡單,效果也和迪廳反光球一樣:

關鍵是DDX和DDY是什麼?

這個蛋疼的起名讓我一開始以爲是二階導數,但明顯不對。經過搜索我發現它就是屏幕空間x方向偏導數和y方向偏導數。

問題在於爲什麼結果它是這樣的,而不是我們之前那麼順滑的連續函數。

以DDX爲例,就是檢查當前像素點(Pixel)在屏幕水平方向上離得最近的左右兩個頂點(Vertex),計算從左頂點到右頂點的空間向量,再歸一化。

我將這個結果賦予BaseColor,這樣看的準一點。

當屏幕向右是世界系y方向時,其值偏向爲(0,0,1)。因爲屏幕上的頂點從左到右,頂點向y軸方向變化(注意左下角世界座標系的方向):

當我大致面朝(1,1,0)方向:

注意到方塊紅面由於在往屏幕右方向走時,頂點世界位置x增大(導數大於0),y不變(導數爲0),而z變小(導數爲負,負數clamp後爲0),其ddx爲(1,0,0)

方塊上面由於在往屏幕右方向走時,頂點的x,y方向都有增加且一樣,ddx爲(1,1,0)

當我的屏幕右方向更接近x軸時,方塊黃面開始變紅:

儘管我們的相機位置一直在變,同一個頂點的DDX和DDY也在變,但其都保持在切平面上,叉乘出來也一定是法線方向。

這裏可以看出UE4材質系統的2個缺點

1.明明與真正的WP無關,但卻必須引用這個節點,給讀者以誤導

2.程序執行順序對用戶不清楚。這裏的WP明顯是已經經過WPOffset以後的WP了。WPOffset中的WP節點和這裏的WP節點明明一樣,出來的值卻不一樣,若不是我們知曉渲染管線流程,就會被搞得懷疑人生。

9.問題一:法線

上面看出來了,叉乘出來的法線應該屬於世界座標系,但我們的材質默認是切線空間法線。原材質連這個都沒注意到,所以我說編者腦子不清楚

你可能記得,材質有個選項可以修改。但因爲一個蛋疼的原因,我們不得不手動計算,之後會講到。我們需要手動將世界座標系的法線轉到切線座標系。

(上圖從左到右分別是原法線,編者原材質水波法線,和我修改後的法線。我猜由於透明水波既沒什麼陰影,水波的折射是否正確也很難有人看出來,所以這個問題沒人注意。)

所幸UE4有節點輕鬆解決座標系轉換:

10.問題二:折射

 

看到沒有,球下方邊緣有很奇怪的折射。

本來是水面折射,怎麼會和球有關?既然有“屏幕空間反射”,那麼這個折射是不是也是基於屏幕空間算的,然後出的問題呢?

在材質detail中搜索Refraction,我們發現引擎提供2種算法,默認算法是這樣的:

簡單地來說它警告你不要用於像水面一樣的大面積折射面。關於爲什麼會有這個bug它也沒說細說,只說這個是物理模擬。

我選擇第二種:

 

它說這個折射是個假模擬,值給1就是沒折射,2就是offset爲1的折射。這個offset是啥意思它也沒說清楚。還告訴我們必須用切線空間法線,這就是爲什麼我們之前要手動轉換。

蛋疼嗎?蛋疼。UE4辣雞嗎?辣雞。

我更改"折射"爲:

Lerp(1.1,1.9,Fresnel(5))

因爲這個假折射率要比較大,折射效果才明顯。沒辦法,誰叫真折射有Bug呢?咱目前這種情況,就是我寫本系列教程的緣由,瞭解原理,運用原理,避免這種情況。因爲弱勢,所以被牽着鼻子走,不知道在幹什麼。很不幸,我因爲辣雞,又碰上了這種情況,成爲了一個"能用就行"的乞討者。

以後有機會嘗試改寫引擎代碼,改寫這個折射。

結語:

本篇解析,改進,批評一個國外免費透明水面Shader,對簡單的物理和材質系統進行初步瞭解。

由於能力的不足,在折射問題上最終又成爲了一個被攔在原理和實現之外的人。之後提升自己的能力,改變這一狀況。

水波模擬系列到此告一段落。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章