1. 程式人生 > >unity3d 製造自己的水體water effect(二)

unity3d 製造自己的水體water effect(二)

PBR:

講求基本演算法

plus篇 之前一直在用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個波

關鍵程式碼如下:

                               <span style="font-size:14px;"> 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波

 

關鍵程式碼如下:

<span style="font-size:14px;">    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  http://blog.csdn.net/wolf96