着色器应用贴图纹理并修改顶点实现“平地起丘陵”

来张效果图先:

    var plane;
    function addpanel(){
        var axesHelper = new THREE.AxesHelper( 400 );
        scene.add( axesHelper );
        var geometry = new THREE.PlaneBufferGeometry( 200,200,400, 400);1
        var material = createMaterial("vertex-shader-2", "fragment-shader-2");
        material.uniforms.texture_grass={
            type:'t', 
            value:new THREE.TextureLoader().load("../texture/tile_aqua.png"),
        }
        material.uniforms.texture_grass.value.wrapS = THREE.RepeatWrapping;2
        material.uniforms.texture_grass.value.wrapT = THREE.RepeatWrapping;
        var displacement = new Float32Array(200000);3
        for(var i = 0; i < 200000; i++){
            displacement[i]=0;
        }
        //丘陵
        displacement = addmoutain(displacement,50,170,30,30);
        displacement = addmoutain(displacement,100,30,30,30);
        displacement = addmoutain(displacement,150,270,30,30);
        displacement = addmoutain(displacement,200,30,30,30);
        displacement = addmoutain(displacement,250,170,30,30);
        displacement = addmoutain(displacement,300,30,30,30);
        
        geometry.addAttribute('displacement', new THREE.BufferAttribute(displacement, 1));4
        plane = new THREE.Mesh( geometry, material );
        plane.rotation.x = -1;
        scene.add( plane );
    }

1.利用PlaneBufferGeometry起了一块尺寸200X200的面板,后面的400,400是x轴y轴细分的三角形个数

2.为着色器材质提供贴图纹理,并设置repeat(贴图尺寸必须为2的n次幂)

3.因为plane被细分了(400 x 400,实际可能不是160000个顶点,就像两个三角形4个顶点,4个三角形却只有6个顶点,因为共用的问题)产生n个顶点,所以我们要提供相对应数量的数据供顶点使用;

其实这些数据是后面要传给着色器,替换position.z的,addmoutain函数的作用是以第m个点为中心,周围radius半径内的点按一定规则升降(距离中心点越近,那么高度越高,即值越大)

该函数如下:

    function addmoutain(arr,row,col,radius,height){
        var colstart = (col-radius+1)>0?(col-radius+1):1;
        var colend = (col+radius)>400?400:(col+radius);
        var rowstart = (row -radius + 1)>0?(row -radius + 1):1;
        var rowend = (row+radius)>400?400:(row+radius);
        for(var i = rowstart;i<=rowend;i++){
            for(var j = colstart;j<=colend;j++){
                var distance = Math.sqrt(Math.pow((row-i),2) + Math.pow((col-j),2));
                if(distance < radius){
                    var percent = (radius - distance)/radius;
                    var h = Math.sin(percent * Math.PI/2) * height/2;
                    arr[(i-1)*400+j-1] = h;
                }
            }
        }
        return arr;
    }

4.将写好的顶点z轴数据值传给geometry。

下面来看两个着色器:

vs:

<script id="vertex-shader-2" type="x-shader/x-vertex">
    attribute float displacement;
    varying vec2 vUv;
    varying float pt;
    void main(){
        vUv = uv * 4.0; 
        pt = displacement;
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position.xy,displacement, 1);
    }
</script>

通过uv * 4.0 实现贴图repeat,position.z值用displacement代替,pt(varying类型)用于后续fs计算透明度的依据

fs:

<script id="fragment-shader-2" type="x-shader/x-fragment">
    uniform sampler2D texture_grass;
    varying vec2 vUv;
    varying float pt;

    void main(){
        vec4 color = texture2D(texture_grass, vUv);
        float percent = 1.0;
        if(pt > 0.5){
            percent = pt / 15.0;
        }
        gl_FragColor = color * percent;
    }
</script>

因为最高的高度为15.0;所以pt/15.0实现的山丘的颜色透明过渡。

这样,就写完了,后续考虑利用该种方法画一个围棋盘,敬请期待!

原文地址:https://www.cnblogs.com/eco-just/p/11349480.html