实用Shader样例详解3——水波效果

发表于2016-06-30
评论2 1.78w浏览

 水波效果在游戏中的应用非常的多,对于大型的水体系统来说,会把大型网格的几何波动与动态法线贴图的生成结合起来,在实时游戏中进行应用。不过因为性能上的消耗,在移动端尚未见到较为复杂的水体系统模型,通常都是一些水模拟简单模型(例如小鳄鱼爱洗澡,捕鱼达人等)。这里会跟大家简单解释下水波模拟的原理,给大家提供一定的思路,同时也实现一个简单的水波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);
}

效果如下:

 

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引