mapbox threebox 下雨效果 天气特效

使用mapbox为地图容器,结合threebox实现下雨效果,天气特效

效果图

主要代码

  • 创建地图容器
mapboxgl.accessToken = '你的token';
    var map = new mapboxgl.Map({
        container: 'map',
        style: 'mapbox://styles/mapbox/dark-v9',
        pitch: 80,
        center: [116.201756, 39.326785],//坐标 
        zoom: 16, //层级
        antialias: true
    });
  • 添加custom图层以及创建mesh
map.addLayer({
                id: 'custom_layer',
                type: 'custom',
                renderingMode: '3d',
                onAdd: function (map, mbxContext) {
                    window.tb = new Threebox(
                        map,
                        mbxContext,
                        { defaultLights: true }
                    );
                    camera = map.getFreeCameraOptions();

                    var mesh = addRainMesh();
                    mesh = window.tb.Object3D({ obj: mesh, units: 'meters', bbox: false, anchor: 'center' })
                        .setCoords([116.201756, 39.326785, 0]);
                    mesh.setRotation({ x: 90, y: 0, z: 0 });
                    tb.add(mesh)
                },
                render: function (gl, matrix) {
                    tb.update();
                    map.triggerRepaint();
                }
            });
  • 生成雨滴
function addRainMesh() {
        // 雨的盒子
        const box = new THREE.Box3(
            new THREE.Vector3(-5000, 0, -5000),
            // 下雨的范围
            new THREE.Vector3(5000, 5000, 5000)
        );

        //创建雨(雨的材质)
        material = new THREE.MeshBasicMaterial({
            transparent: true,
            opacity: 0.5,
            map: new THREE.TextureLoader().load("./image/drop.png"),
            depthWrite: false,
        });

        material.onBeforeCompile = function (shader, renderer) {
            const getFoot = `
            uniform float top;
            uniform float bottom;
            uniform float time;
            #include <common>
            float angle(float x, float y){
              return atan(y, x);
            }
            vec2 getFoot(vec2 camera,vec2 normal,vec2 pos){
                vec2 position;

                float distanceLen = distance(pos, normal);
                float a = angle(camera.x - normal.x, camera.y - normal.y);
                pos.x > normal.x ? a -= 0.785 : a += 0.785; 
                position.x = cos(a) * distanceLen;
                position.y = sin(a) * distanceLen;
                return position + normal;
            }
            `;
            const begin_vertex = `
            vec2 foot = getFoot(vec2(cameraPosition.x, cameraPosition.z),  vec2(normal.x, normal.z), vec2(position.x, position.z));
            float height = top - bottom;
            float y = normal.y - bottom - height * time;
            y = y + (y < 0.0 ? height : 0.0);
            float ratio = (1.0 - y / height) * (1.0 - y / height);
            y = height * (1.0 - ratio);
            y += bottom;
            y += position.y - normal.y;
            vec3 transformed = vec3( foot.x, y, foot.y );
            `;
            shader.vertexShader = shader.vertexShader.replace(
                "#include <common>",
                getFoot
            );
            shader.vertexShader = shader.vertexShader.replace(
                "#include <begin_vertex>",
                begin_vertex
            );

            shader.uniforms.cameraPosition = {
                value: new THREE.Vector3(0, 200, 0),
            };
            shader.uniforms.top = {
                value: 5000,
            };
            shader.uniforms.bottom = {
                value: 0,
            };
            shader.uniforms.time = {
                value: 0,
            };
            material.uniforms = shader.uniforms;
        };

        var geometry = new THREE.BufferGeometry();

        const vertices = [];
        const normals = [];
        const uvs = [];
        const indices = [];
        // 雨量
        for (let i = 0; i < 5000; i++) {
            const pos = new THREE.Vector3();
            pos.x = Math.random() * (box.max.x - box.min.x) + box.min.x;
            pos.y = Math.random() * (box.max.y - box.min.y) + box.min.y;
            pos.z = Math.random() * (box.max.z - box.min.z) + box.min.z;

            const height = (box.max.y - box.min.y) / 15;
            const width = height / 50;

            vertices.push(
                pos.x + width,
                pos.y + height / 2,
                pos.z,
                pos.x - width,
                pos.y + height / 2,
                pos.z,
                pos.x - width,
                pos.y - height / 2,
                pos.z,
                pos.x + width,
                pos.y - height / 2,
                pos.z
            );

            normals.push(
                pos.x,
                pos.y,
                pos.z,
                pos.x,
                pos.y,
                pos.z,
                pos.x,
                pos.y,
                pos.z,
                pos.x,
                pos.y,
                pos.z
            );

            uvs.push(1, 1, 0, 1, 0, 0, 1, 0);

            indices.push(
                i * 4 + 0,
                i * 4 + 1,
                i * 4 + 2,
                i * 4 + 0,
                i * 4 + 2,
                i * 4 + 3
            );
        }

        geometry.addAttribute(
            "position",
            new THREE.BufferAttribute(new Float32Array(vertices), 3)
        );
        geometry.addAttribute(
            "normal",
            new THREE.BufferAttribute(new Float32Array(normals), 3)
        );
        geometry.addAttribute(
            "uv",
            new THREE.BufferAttribute(new Float32Array(uvs), 2)
        );
        geometry.setIndex(new THREE.BufferAttribute(new Uint32Array(indices), 1));

        var mesh = new THREE.Mesh(geometry, material);
        return mesh;
    }
  • 动画
function render() {
        // 下雨速率
        time = (time + clock.getDelta() * 0.3) % 1;

        if (!camera) {
            return
        }

        material.cameraPosition = camera.position;
        if (material.uniforms) {
            material.uniforms.time.value = time;
        }
    }

代码下载地址:https://download.csdn.net/download/AllBluefm/88781246?spm=1001.2014.3001.5503

  • 13
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值