前篇:unity3d 制造自己的水體water effect(一)
曲面細分:Unity3d 使用DX11的曲面細分
PBR:
講求基本算法
Unity3d 基於物理渲染Physically-Based Rendering之specular BRDF
plus篇
Unity3d 基於物理渲染Physically-Based Rendering之實現
最終篇
Unity3d 基於物理渲染Physically-Based Rendering之最終篇
之前一直在用unity4.6寫shader,終於下定決心換unity5,然后發現,unity5的渲染比4強太多,
這次完成之前2月份自制拖着沒解決的normal問題,算出normal真的很簡單。
本次水體分別使用兩種波動算法,一個是ShaderX6 中Szecsi 和 Arman的算法,另一個是Gpu gems1里的Gerstner波算法。然后再用PBR渲染。然后還是曲面細分。
這次的效果:
Szecsi & Arman波動算法
首先來看ShaderX6 中的波動算法:
求波動速度,λ為波長,波長就是波峰到波峰之間的距離,速度v為每秒鍾波峰移動的距離。
然后是相函數,k為波動方向,運動方向,垂直於波陣面的水平方向,p為position,t為時間(_Time.y)
求得一個波的位移S,a為振幅,振幅就是從水平面到波峰的高度。
最終我們的水是多個波組合在一起的,所以最終結果為:
博主整共合了4個波
關鍵代碼如下:
vv = sqrt(_G * _Lambda / (2 * _PIE)); psi = 2 * _PIE / _Lambda *(dot(v.vertex.xyz, _K.xyz) + vv*_Time.y); s = lerp(-cos(psi), sin(psi), _A)*0.05; p.y += s; vv = sqrt(_G * _Lambda2 / (2 * _PIE)); psi = 2 * _PIE / _Lambda2 *(dot(v.vertex.xyz, _K2.xyz) + vv*_Time.y); s = lerp(-cos(psi), sin(psi), _A2)*0.05; p.y += s; vv = sqrt(_G * _Lambda3 / (2 * _PIE)); psi = 2 * _PIE / _Lambda3 *(dot(v.vertex.xyz, _K3.xyz) + vv*_Time.y); s = lerp(-cos(psi), sin(psi), _A3)*0.1; p.y += s; vv = sqrt(_G * _Lambda4 / (2 * _PIE)); psi = 2 * _PIE / _Lambda4 *(dot(v.vertex.xyz, _K4.xyz) + vv*_Time.y); s = lerp(-cos(psi), sin(psi), _A4)*0.1; p.y += s; v.vertex.xyz = p.xyz;</span>
產生了波動效果:
加上之前的pbr,可以模擬各種液體
牛奶
血,等等
但是由於曲面細分,離近了看水面上還是有小面的細節,這點有待解決。
Gerstner波
然后Gerstner波
關鍵代碼如下:
float wave(float x, float z, float timer) { float y = 0; float oct = _OCT; float fac = _FAC; float d = sqrt(x * x + z * z);// length(float2(x, z)); for (oct; oct>0; oct--) { y -= fac * cos(timer * _SP + (1 / fac) * x * z * _WS); fac /= 2; } return 2 * _VS * d * y; } </span>
曲面細分帶來的小面不是很明顯,而且面不用分得很細就能看到平滑的效果
normal的生成
最后我們講講normal的生成,
Normal的生成有復雜的和簡單的,但本質上都是求偏導。
先看復雜的,來自GPU Gems1的42章
首先求出Jacobian
舉個例子(直接截圖了^ _ ^):
是不是很麻煩。。
法線本質上的求法就是這樣的,但是,我們有函數:ddx,ddy這兩個神器
我們只需要求出世界坐標點,worldpos
就可以簡單地求出法線:
N = cross(ddx(worldpos),ddy(worldpos))
方法很簡單,不過別忘記,本質上還是上面的一大串。。。
切記:ddx,ddy只能在fragment shader中使用。
只有漫反射的水體如下:
-------- by wolf96 、