zoukankan      html  css  js  c++  java
  • 用shader实现流动的水面(webgl)

    这段时间一直在看如何用shader绘制一个流动的水面,直接用贴图(高度图、法向贴图)实现的方法,这里就不讨论了。

    搜了一大波博客资料,感觉存在如下一些问题

    1⃣️大多数资料都是基于opengl实现(或者是unity里的shader),过多关注点在渲染上面而不是水波的mesh实现上,让人没有看下去的欲望

    2⃣️有的就直接是照搬别人的博客,公式大段大段地搬,却没有自己的一丝见解,太过敷衍

    3⃣️代码不加注释,对前来学习者不太友好

    4⃣️针对webgl的实现,网上的资料太少(虽然已经有了opengl的实现)

    所以在查阅了资料之后,决定写一个webgl版本的实现(three.js + shader)

    nvidia官方提供的水波实现方程(其实网上大多数博客里的方程式应该都是源于此处):传送门

    对应的,知乎有一篇文章,基本上就是上面网站的中文版,但是作者加入了一点自己思考后的想法,个人觉得很好,推荐一下:GPU Gems 基于物理模型的水面模拟

    -------------------------------------------------------------------华丽的分割线-------------------------------------------------------------------------

    一、PlaneGeometry + ShaderMaterial + 正弦波方程式

        //1.PlaneGeometry
        this.seaMaterial = new THREE.ShaderMaterial({
          uniforms: { 
            time:{type:'f',value:0},
          },
          vertexShader: seashader.vs,
          fragmentShader: seashader.fs,
          side:THREE.DoubleSide,
          wireframe: true
        });
        this.geometry = new THREE.PlaneGeometry( 500,500,100,20);
        var plane = new THREE.Mesh( this.geometry, this.seaMaterial );
        plane.rotation.x= -Math.PI/2;
        this.scene.add( plane );
    const seashader = {
        vs:`
            uniform float time;
    
            void main(){
                float x = position.x;
                float y = position.y;
                float PI = 3.141592653589;
    
                float sz = 0.0;
                float ti = 0.06;
                float index = 1.0;
                vec2 dir;//波的方向
                //四条正弦波相加
                for(int i = 0;i<4;i++){
                    ti = ti + 0.0005;
                    index = index + 0.1;
                    if(mod(index,2.0)==0.0){
                        dir = vec2(1.0,ti);
                    }else{
                        dir = vec2(-1.0,ti);
                    }
                    float l1 = 2.0 * PI / (0.5);//波长
                    float s1 = 10.0 * 2.0 / l1;//速度
                    float z1 = 1.0 * sin(dot(normalize(dir),vec2(x,y)) * l1 + time * s1);//正弦波方程式
                    sz +=z1;
                }
                gl_Position = projectionMatrix * modelViewMatrix * vec4(x,y,sin(sz) * 10.0,1.0);
            }
        `,
        fs:`
            void main(){
                gl_FragColor = vec4(90./255.,160./255.,248./255.,1.0);      
            }
        `,
    }
    //animation
        if(this.seaMaterial){
          this.seaMaterial.uniforms.time.value += 0.01;
        }

    参考的水波方程式:

    效果如下:

    温馨提示:

    调参很重要,水波方向、波长、波的叠加数量,如果取值不当,生成的波将会很诡异(和plane尺寸以及分割的分数有关,因为这些参数将会直接影响vs接收的顶点坐标)。

    可以看到上述代码vs中顶点z:sin(sz) * 10.0,在测试中,我发现对sz进行sin处理,得到的水波细节会更多一点,就是凹凸的感觉会多一点。

    正弦波具有圆润的外观-这可能正是我们想要一个平静的田园池塘所需要的。

    二、Gerstner波方程式

    接下来只贴shader部分

    const gerstnershader = {
        vs:`
            uniform float time;
    
            void main(){
                float x = position.x;
                float y = position.y;
                float PI = 3.141592653589;
    
                float sx = 0.0;
                float sy = 0.0;
                float sz = 0.0;
    
                float ti = 0.0;
                float index = 1.0;
                vec2 dir;//水波方向
                for(int i = 0;i<3;i++){
                    ti = ti + 0.0005;
                    index +=1.0;
                    if(mod(index,2.0)==0.0){
                        dir = vec2(1.0,ti);
                    }else{
                        dir = vec2(-1.0,ti);
                    }
                    float l1 = 2.0 * PI / (0.5 + ti);//波长
                    float s1 = 20.0 * 2.0 / l1;//速度
                    float x1 = 1.0 * dir.x * sin(dot(normalize(dir),vec2(x,y)) * l1 + time * s1);
                    float y1 = 1.0 * dir.y * sin(dot(normalize(dir),vec2(x,y)) * l1 + time * s1);
                    float z1 = 1.0 * sin(dot(normalize(dir),vec2(x,y)) * l1 + time * s1);
                    sx +=x1;
                    sy +=y1;
                    sz +=z1;
                }
                sx = x + sx;
                sy = y + sy;
                gl_Position = projectionMatrix * modelViewMatrix * vec4(sx,sy,sin(sz) * 10.0,1.0);
            }
        `,
        fs:`
            void main(){
                gl_FragColor = vec4(90./255.,160./255.,248./255.,1.0);      
            }
        `,
    }

    参考的水波方程式:

    gerstner波相较正弦波的特点是:波峰陡峭、波谷平坦。

    效果如下:

  • 相关阅读:
    windows下 删除指定文件夹里面一周前的所有文件和文件夹的bat
    freeswitch 把SIP注册信息数据库从SQLITE 改为MYSQL的方法
    memcached+Mysql(主从)
    Linux下的MySQL主主复制
    RabbitMQ php 使用
    如果你写PHP, 请多注意自己是否有良好的习惯
    MySQL主从配置的一些总结
    Mongodb与mysql语法比较
    Yii框架tips
    Windows下Python添加MySQLdb扩展模块
  • 原文地址:https://www.cnblogs.com/eco-just/p/11747777.html
Copyright © 2011-2022 走看看