实用Shader样例详解3——水波效果
水波效果在游戏中的应用非常的多,对于大型的水体系统来说,会把大型网格的几何波动与动态法线贴图的生成结合起来,在实时游戏中进行应用。不过因为性能上的消耗,在移动端尚未见到较为复杂的水体系统模型,通常都是一些水模拟简单模型(例如小鳄鱼爱洗澡,捕鱼达人等)。这里会跟大家简单解释下水波模拟的原理,给大家提供一定的思路,同时也实现一个简单的水波Shader。
最早的水波效果是采用正弦波的和来近似模拟水面,这种水波在模拟一些比较平静的水面的时候还是可以的,但是对于水面波动较大,例如大海的波浪具有比较尖的浪头,这种效果就不合适了。事实上,水纹理波纹的好坏决定了模拟的逼真度,后面大家渐渐使用Gerstner波函数,优点波峰形状真实而且计算量不大,采用叠加Gerstner波的方法来模拟水波效果。
正弦波:
Gerstner波:
Gerstner波函数:
忽略高次项的水的传播关系:
这里的QDA三个参数都是用来控制波的形状,Q控制波峰的波形(要注意Q值不能太大,不然波峰容易成环),D为波长,A为振幅,w为角频率,g为重力常数,L是波峰到波峰的长度。
为了模拟真实的水面,我们需要生成不同方向和不同波长的Gerstner波,然后进行叠加。我们不能改变正在活动的波的波长,即使慢慢改变了它,波峰也会从原始状态扩展或压缩,看起来非常的不自然。因此,我们只能改变当前的平均波长,在波随时间的推移淡出场景以后,它们会基于新的波长而再生,波的运动方向的改变也是这样。不过波的方向跟其他的参数完全独立,我们可以自由地选择波的方向。
Gerstner波就简单介绍到这里,下面就是Shader实现了。这个Shader是以前在做捕鱼项目时一个比较早的版本了,在真机上运行的效果还行,大家可以酌情使用。
片元着色器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | uniform sampler2D tex; uniform float time ; uniform vec2 resolution; const float PI = 3.1415926535897932; //speed const float speed = 0.2; const float speed_x = 0.3; const float speed_y = 0.3; // geometry const float intensity = 3.; const int steps = 8; const float frequency = 4.0; const int angle = 7; // better when a prime // reflection and emboss const float delta = 20.; const float intence = 400.; const float emboss = 0.3; //---------- crystals effect float col(vec2 coord) { float delta_theta = 2.0 * PI / float (angle); float col = 0.0; float theta = 0.0; for ( int i = 0; i < steps; i++) { vec2 adjc = coord; theta = delta_theta* float (i); adjc.x += cos (theta)* time *speed + time * speed_x; adjc.y -= sin (theta)* time *speed - time * speed_y; col = col + cos ( (adjc.x* cos (theta) - adjc.y* sin (theta))*frequency)*intensity; } return cos (col); } void main() { vec2 p = (gl_FragCoord.xy) / resolution.xy, c1 = p, c2 = p; float cc1 = col(c1); c2.x += resolution.x/delta; float dx = emboss*(cc1-col(c2))/delta; c2.x = p.x; c2.y += resolution.y/delta; float dy = emboss*(cc1-col(c2))/delta; c1.x += dx; c1.y = -(c1.y+dy); float alpha = 1.+dot(dx,dy)*intence; gl_FragColor = texture2D(tex,c1)*(alpha); } |
效果如下: