Skip to main content

Draw Mesh with mapbox-gl

结合 mapbox-gl 的自定义图层渲染 Mesh,注意 @sakitam-gis/mapbox-ve 插件内部默认启用了高精度。

示例

Result
Loading...
Live Editor
function render(props) {
  const refDom = useRef(null);

  const store = leva.useCreateStore();

  class MeshLayer {
    constructor (id) {
      this.id = id;
      this.type = 'custom';
      this.renderingMode = '2d';
    }

    get camera () {
      return this.sync.camera;
    }

    updateCamera() {
      this.sync.update();
    }

    projectToWorld (coords) {
      let i = 0;
      const len = coords.length;
      const position = new Float32Array(len * 3);
      for (; i < len; i++) {
        const coord = coords[i];
        const mc = mapboxgl.MercatorCoordinate.fromLngLat({
          lng: coord[0],
          lat: coord[1],
        }, coord[2]);
        position.set([mc.x, mc.y, mc.z], i * 3);
      }

      return position;
    }

    onAdd (map, gl) {
      this.renderer = new Renderer(gl, {
        autoClear: false,
      });
      this.scene = new Scene();
      this.sync = new mbve.CameraSync(map, 'perspective', this.scene);
      this.updateCamera = this.updateCamera.bind(this);
      map.on('move', this.updateCamera);
      map.on('resize', this.updateCamera);

      const geometry = new Geometry(this.renderer, {
        position: {
          size: 3,
          data: this.projectToWorld([
            [70.26608, 38.7213],
            [102.51084435117338, 24.846755709924764],
            [114.46396935117377, 39.232415634606724]
          ]),
        },
        // position: {
        //   size: 2,
        //   data: new Float32Array([0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1])
        // },
        // uv: {
        //   size: 2,
        //   data: new Float32Array([0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1])
        // }
      });

      this.program = new Program(this.renderer, {
        vertexShader: `
      attribute vec2 uv;
      attribute vec3 position;
      uniform vec3 cameraPosition;
      uniform mat4 viewMatrix;
      uniform mat4 modelMatrix;
      uniform mat4 modelViewMatrix;
      uniform mat4 projectionMatrix;

      varying vec2 vUv;

      void main() {
          vUv = uv;

          gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
      }
      `,
        fragmentShader: `
      precision highp float;

      uniform float uTime;
      varying vec2 vUv;

      void main() {
          gl_FragColor.rgb = 0.5 + 0.3 * sin(vUv.yxx + uTime) + vec3(0.2, 0.0, 0.1);
          gl_FragColor.a = 1.0;

      }
      `,
        uniforms: {
          uTime: { value: 0.5 },
        },
        depthTest: false,
      });

      this.mesh = new Mesh(this.renderer, {
        geometry,
        program: this.program,
      });

      this.scene.add(this.mesh);

      this.updateCamera();
    }

    onRemove () {
      this.mesh.destroy();
      this.program.destroy();
    }

    prerender () {
    }

    render () {
      this.scene.worldMatrixNeedsUpdate = true;
      this.renderer.render({
        scene: this.scene,
        camera: this.camera,
      });
      this.renderer.resetState();
    }
  }

  const init = () => {
    mapboxgl.accessToken = 'pk.eyJ1IjoiZXhhbXBsZXMiLCJhIjoiY2p0MG01MXRqMW45cjQzb2R6b2ptc3J4MSJ9.zA2W0IkI0c6KaAhJfk9bWg';
    const map = new mapboxgl.Map({
      container: refDom.current,
      zoom: 3,
      center: [112.26608, 32.7213],
      pitch: 0,
      bearing: 0,
      // style: 'mapbox://styles/mapbox/satellite-streets-v12',
      style: {
        version: 8,
        glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf',
        sources: {
          tile: {
            type: 'raster',
            tiles: [
              'https://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetPurplishBlue/MapServer/tile/{z}/{y}/{x}',
            ],
            tileSize: 256,
          },
        },
        layers: [
          {
            id: 'tile',
            type: 'raster',
            source: 'tile',
            minzoom: 0,
            maxzoom: 22,
            paint: {
              'raster-resampling': 'nearest',
              'raster-fade-duration': 200,
            },
          },
          {
            id: 'background',
            type: 'background',
            layout: {
              visibility: 'none',
            },
          },
        ],
      },
      // center: [-74.0066, 40.7135],
      // zoom: 15.5,
      // pitch: 45,
      // bearing: -17.6,
      antialias: true,
      projection: 'mercator'
    });
    map.on('load', () => {
      const layer = new MeshLayer('mesh');
      map.addLayer(layer);
      window.layer = layer;
    });

    window.map = map;

    return map;
  }

  useEffect(() => {
    const map = init();

    return () => {
      console.log(map);
    };
  }, []);

  return (
    <div className="live-wrap">
      <div className="leva-wrap">
        <Leva
          collapsed
          fill
        ></Leva>
        <LevaPanel collapsed store={store} fill></LevaPanel>
      </div>
      <div className="scene-canvas" ref={refDom}></div>
    </div>
  );
}