mapbox-gl结合threejs
文章目录
- 前言
- 为什么使用threebox
- 代码示例
- 总结
前言
最近在研究threejs和mapbox的结合,花了一天多的时间,结合threebox这个mapbox的三维库,给mapbox中创建自定义图层,添加自定义几何体,基于react-hooks实现,代码不多,但是threebox官网的例子给的很少,所以不少东西还是需要自己摸索下,特此记录下来。
参考:threebox.js
为什么使用threebox
mapbox官网有使用threejs的示例,但是由于threejs使用的是右手坐标系,而mapbox作为一个时空数据的渲染库,默认使用EPSG4326坐标系,参考官网mapbox-gl中创建threejs场景代码如下。
// configuration of the custom layer for a 3D model per the CustomLayerInterfaceconst customLayer = {id: '3d-model',type: 'custom',renderingMode: '3d',onAdd: function (map, gl) {this.camera = new THREE.Camera();this.scene = new THREE.Scene();// create two three.js lights to illuminate the modelconst directionalLight = new THREE.DirectionalLight(0xffffff);directionalLight.position.set(0, -70, 100).normalize();this.scene.add(directionalLight);const directionalLight2 = new THREE.DirectionalLight(0xffffff);directionalLight2.position.set(0, 70, 100).normalize();this.scene.add(directionalLight2);// use the three.js GLTF loader to add the 3D model to the three.js sceneconst loader = new THREE.GLTFLoader();loader.load('https://docs.mapbox.com/mapbox-gl-js/assets/34M_17/34M_17.gltf',(gltf) => {this.scene.add(gltf.scene);});this.map = map;// use the Mapbox GL JS map canvas for three.jsthis.renderer = new THREE.WebGLRenderer({canvas: map.getCanvas(),context: gl,antialias: true});this.renderer.autoClear = false;},render: function (gl, matrix) {const rotationX = new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(1, 0, 0),modelTransform.rotateX);const rotationY = new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(0, 1, 0),modelTransform.rotateY);const rotationZ = new THREE.Matrix4().makeRotationAxis(new THREE.Vector3(0, 0, 1),modelTransform.rotateZ);const m = new THREE.Matrix4().fromArray(matrix);const l = new THREE.Matrix4().makeTranslation(modelTransform.translateX,modelTransform.translateY,modelTransform.translateZ).scale(new THREE.Vector3(modelTransform.scale,-modelTransform.scale,modelTransform.scale)).multiply(rotationX).multiply(rotationY).multiply(rotationZ);this.camera.projectionMatrix = m.multiply(l);this.renderer.resetState();this.renderer.render(this.scene, this.camera);this.map.triggerRepaint();}};
mapbox-gl中使用threejs场景需要创建type为custom的layer,onAdd函数只触发一次,用来初始化threejs场景,render在地图缩放、移动、旋转时都会触发,重新更新模型和相机的位置,如果涉及动画,代码量会更多。
threebox提供方便的方法来管理地理坐标,以及同步地图和场景相机
。
代码示例
- 添加threebox封装的球
- 使用threejs添加一个立方体,并在它的上面添加一个上下浮动的圆锥
- 基于着色器添加一个黄色的半球光罩,并由下至上渐透明
效果:
完整代码:
import React, { useRef, useEffect, useState } from 'react';
import mapboxgl from 'mapbox-gl';
import MapboxLanguage from '@mapbox/mapbox-gl-language';
import { Threebox, THREE } from 'threebox-plugin';
import 'antd/dist/antd.css';function App() {const mapContainerRef = useRef();const sphereMesh = useRef();const coneMesh = useRef();const lightRingMesh = useRef();const blueMaterial = new THREE.MeshPhongMaterial({color: '#4791ff',side: THREE.DoubleSide});const redMaterial = new THREE.MeshPhongMaterial({color: '#f73131',side: THREE.DoubleSide});const shaderMaterial = new THREE.ShaderMaterial({vertexShader: `varying vec3 vPosition;void main(){vec4 viewPosition = viewMatrix * modelMatrix *vec4(position,1);gl_Position = projectionMatrix * viewPosition;vPosition = position;}`,fragmentShader: `varying vec3 vPosition;uniform float uHeight;void main(){float gradMix = (vPosition.z+uHeight/2.0)/uHeight;gl_FragColor = vec4(1.0,1.0,0,1.0-gradMix);}`,transparent: true,side: THREE.DoubleSide,})const mapRef = useRef();let step = 200;let cylinderRadius = 0// 初始化基础图层useEffect(() => {mapboxgl.accessToken = 'your token'mapRef.current = new mapboxgl.Map({zoom: 14,center: [116.5, 39.9],pitch: 90,style: 'mapbox://styles/mapbox/streets-v11',container: mapContainerRef.current,antialias: true,},);mapRef.current.addControl(new MapboxLanguage({ defaultLanguage: "zh-Hans" }))mapRef.current.on('load', (e) => {//地图加载完后,才能进行添加图层//console.log('load happened', mapRef.current.style);mapRef.current.addLayer({id: 'custom_layer',type: 'custom',renderingMode: '3d',onAdd: function (map, mbxContext) {window.tb = new Threebox(map,mbxContext,{defaultLights: true,// enableSelectingFeatures: true, //change this to false to disable fill-extrusion features selection// enableSelectingObjects: true, //change this to false to disable 3D objects selection// enableDraggingObjects: true, //change this to false to disable 3D objects drag & move once selected// enableRotatingObjects: true, //change this to false to disable 3D objects rotation once selected// enableTooltips: true});// 示例一,threebox封装的小球sphereMesh.current = window.tb.sphere({ radius: 5, color: 'green', material: 'MeshStandardMaterial', anchor:'center' }).setCoords([116.48, 39.9, 200]);window.tb.add(sphereMesh.current);//示例二 圆锥const coneFeometry = new THREE.ConeGeometry(50, 50, 64);coneMesh.current = new THREE.Mesh(coneFeometry, redMaterial);// coneMesh.current.translateZ(50)coneMesh.current = window.tb.Object3D({ obj: coneMesh.current , units: 'meters', bbox: false, anchor:'center' }).setCoords([116.49, 39.9, 400]);coneMesh.current.rotation.x = -0.5 * Math.PI;window.tb.add(coneMesh.current); //示例三 立方体const geometry = new THREE.BoxGeometry(150, 150, 300);let cube = new THREE.Mesh(geometry, blueMaterial);cube = window.tb.Object3D({ obj: cube, units: 'meters', bbox: false, anchor:'center' }).setCoords([116.49, 39.9, 0]);window.tb.add(cube); // 示例三 半球光罩 let cylinderGeom = new THREE.SphereBufferGeometry(10,32,32, 0,Math.PI)lightRingMesh.current = new THREE.Mesh(cylinderGeom, shaderMaterial);lightRingMesh.current.rotation.x = -0.5 * Math.PI;lightRingMesh.current.geometry.computeBoundingBox();const { min, max } = lightRingMesh.current.geometry.boundingBox;// 设置物体高差let uHeight = max.y - min.y;shaderMaterial.uniforms.uHeight = {value: uHeight,};lightRingMesh.current = window.tb.Object3D({ obj: lightRingMesh.current, bbox: true, anchor:'center' }).setCoords([116.5, 39.9, 0])window.tb.add(lightRingMesh.current);// 执行动画animate()},// 地图更新时触发(拖拽、移动、缩放)render: function (gl, matrix) {window.tb.update();}})});}, []);function animate() {// console.log(lightRingMesh.current)requestAnimationFrame(() => { animate() });step += 0.03const z = Math.abs(50 * Math.cos(step)) + 400coneMesh.current.setCoords([116.49, 39.9, z]);cylinderRadius += 0.01;// 当半径大于1时,重新开始if (cylinderRadius > 1) {cylinderRadius = 0;}// console.log(lightRingMesh.current)}}return (<div style={{ display: 'flex' }}><divid="map-container"ref={mapContainerRef}style={{ height: '100vh', width: '100vw' }}/><div style={{ position: 'fixed', top: '0', right: '0' }}><button onClick={() => { sphereMesh.current.visible = !sphereMesh.current.visible }} style={{ marginRight: '10px' }}>修改显隐</button></div></div>);
}export default App;
总结
- mapbox-gl :custom-layer
- threejs
- threebox
mapbox-gl结合threejs相关推荐
- GIS开发:推荐Mapbox gl解决方案
在二维地图的开发中,实现类似于百度.高德地图那样加载简体的模型,使用mapbox gl是一个比较好的解决方案. https://docs.mapbox.com/mapbox-gl-js/api/ 类似 ...
- Mapbox gl tile瓦片渲染点以及图片Icon
Mapbox gl tile瓦片渲染点以及图片Icon 1. 效果图 2. 源码 参考 1. 效果图 点效果图如下: 以图标渲染效果图如下: 注意图片要不能跨域,需要下载的下来才能正常展示. 2. 源 ...
- 初识mapbox GL
一.概述 最近由于项目的需求,借此机会对mapbox GL做了一个系统的学习,同时也对整个学习过程做一个记录,一方面留作自用,另一方面也希望看到此文的人在学习mapbox GL的时候,能够有所启发.有 ...
- 进阶mapbox GL之paint和filter
概述 通过前面的文章初识mapbox GL我们对mapbox GL有了一个相对比较全面的认识,本节结合一些示例,重点讲述一下mapbox GL里面的filter和paint的用法. 说明 本文中的示例 ...
- Mapbox GL JS 表达式概述
表达式(expressions)是Mapbox GL JS的一个高级功能,它为数据的添加和渲染展示提供了更多的灵活性. 表达式的功能包括: 数据驱动样式:根据一个或多个数据属性指定样式规则. 算术:对 ...
- Mapbox GL JS 地图英文转中文的解决办法
Mapbox GL JS地图框架的使用者已经越来越多了.不仅仅是因为它支持矢量瓦片,它里面内置了一些非常精美的地图,并且支持自定义搭配地图.对于个人开发者而言,一般都不具备自己搭建地图服务的硬件资源, ...
- Mapbox GL JS介绍及使用
Mapbox GL JS介绍及使用:(以web端基本交互实现为例) Mapbox GL JS 是一个 JavaScript 库,它使用 WebGL,以 vector tiles 和 Mapbox st ...
- GIS开发:mapbox gl几种底图的加载
mapbox gl除了加载客户端可控制样式的底图,也能够加载常规的影像底图. 这里指发布的影像或者矢量地图切片,在线经常使用的有天地图,谷歌地图.高德百度等,底图的加载,mapbox gl中,主要是修 ...
- Mapbox GL可视化之热力图
本篇使用Mapbox GL JS实现数据的热力图可视化,以截止到2020-03-01日的新冠疫情作为示例数据. 一 什么是热力图 热力图使用颜色的深浅表示数值的变化,从而表示数据的分布情况,这使得用户 ...
- 喜大普奔——Mapbox GL JS支持多种投影了
1. 写在前面 Mapbox GL JS 在v2.6.0之前不支持投影,默认地图是Mercator投影方式,如果项目中使用其他类型的投影(比如加载经纬度直投4326的地图切片),必须通过修改源码的方式 ...
最新文章
- 施一公:西湖大学评审终身副教授不看文章数,而是看你能不能讲好一个故事...
- ListView 异步更新出现问题的解决(Handler)
- 【转】3D图形引擎(DX9): FX
- 测试点解析:1049 数列的片段和_12行代码AC
- H264视频通过RTMP直播
- python自动化测试脚本可以测php吗_请对比分析一下php的自动化测试与python的自动化测试...
- jaxb java xml序列化_XML编程总结(六)——使用JAXB进行java对象和xml格式之间的相互转换...
- Scrapy框架实现爬虫
- 横向对比5大开源语音识别工具包,CMU Sphinx最佳
- FastReport添加Sqlite数据源
- STM32F412 can测试问题汇总
- 理论力学知识要点归纳(三)
- win10通过开启teredo访问ipv6
- python原生是什么意思_什么是 云原生?
- 查找算法--Java实例/原理
- android线程池!如何试出一个Android开发者真正的水平?内容太过真实
- Dell服务器启动显示Entering System Servvice To cancel.
- 两点经纬度计算方位角,以正北为0度
- 爱奇艺《大魔术师》海内外备受关注
- vue 八大生命周期