不想看繁琐步骤的,可以直接去github下载项目,如果可以顺便来个star哈哈 本项目使用vue-cli创建,但不影响使用,主要绘制都已封装成类 1、使用geoJson绘制3d地图1.1 创建场景相关- // 创建webGL渲染器
- this.renderer = new THREE.WebGLRenderer( { antialias: true,alpha: true} );
- this.renderer.shadowMap.enabled = true; // 开启阴影
- this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
- this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
- this.renderer.toneMappingExposure = 1.25;
- // 根据自己的需要调整颜色模式
- // this.renderer.outputEncoding = THREE.sRGBEncoding;
- this.renderer.outputEncoding = THREE.sHSVEncoding;
- this.renderer.setPixelRatio( window.devicePixelRatio );
- // 清除背景色,透明背景
- this.renderer.setClearColor(0xffffff, 0);
- this.renderer.setSize(this.width, this.height);
- // 场景
- this.scene = new THREE.Scene();
- this.scene.background = null
- // 相机 透视相机
- this.camera = new THREE.PerspectiveCamera(45, this.width / this.height, 0.1, 5000);
- this.camera.position.set(0, -40, 70);
- this.camera.lookAt(0, 0, 0);
复制代码 1.2 根据json绘制地图利用THREE.Shape绘制地图的平面边数据,再用THREE.ExtrudeGeometry将一个面拉高成3d模型,3d饼图同理也可以这么制作 - let jsonData = require('./json/china.json')
- this.initMap(jsonData);
- // initMap 方法主要部分
- initMap(chinaJson) {
- /* ...省略
- ...
- */
- chinaJson.features.forEach((elem, index) => {
- // 定一个省份3D对象
- const province = new THREE.Object3D();
- // 每个的 坐标 数组
- const { coordinates } = elem.geometry;
- const color = COLOR_ARR[index % COLOR_ARR.length]
- // 循环坐标数组
- coordinates.forEach(multiPolygon => {
-
- multiPolygon.forEach((polygon) => {
- const shape = new THREE.Shape();
-
- for (let i = 0; i < polygon.length; i++) {
- let [x, y] = projection(polygon[i]);
-
- if (i === 0) {
- shape.moveTo(x, -y);
- }
- shape.lineTo(x, -y);
- }
-
- const extrudeSettings = {
- depth: 4,
- bevelEnabled: true,
- bevelSegments: 1,
- bevelThickness: 0.2
- };
-
- const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
-
- // 平面部分材质
- const material = new THREE.MeshStandardMaterial( {
- metalness: 1,
- color: color,
-
- } );
- // 拉高部分材质
- const material1 = new THREE.MeshStandardMaterial( {
- metalness: 1,
- roughness: 1,
- color: color,
-
- } );
- const mesh = new THREE.Mesh(geometry, [
- material,
- material1
- ]);
- // 设置高度将省区分开来
- if (index % 2 === 0) {
- mesh.scale.set(1, 1, 1.2);
- }
- // 给mesh开启阴影
- mesh.castShadow = true
- mesh.receiveShadow = true
- mesh._color = color
- province.add(mesh);
- })
-
- })
-
- _this.map.add(province);
-
- })
- }
复制代码geoJson的坐标需要进行墨卡托投影转换才能转换成平面坐标,这里需要用到d3 - // 墨卡托投影转换
- const projection = d3.geoMercator().center([104.0, 37.5]).scale(80).translate([0, 0]);
复制代码 2、增加光照我们把各种光都打上,环境光,半球光,点光,平行光。以平行光为例,增加投影,调整投影分辨率,避免投影出现马赛克
- onst light = new THREE.DirectionalLight( 0xffffff, 0.5 );
- light.position.set( 20, -50, 20 );
- light.castShadow = true;
- light.shadow.mapSize.width = 1024;
- light.shadow.mapSize.height = 1024;
- this.scene.add(light);
复制代码castShadow = true表示开启投影 3、增加阴影模糊默认的阴影没有模糊效果,看起来像白炽灯照射的样子,没有柔和感。使用官方示例中的csm来增加阴影模糊 - import { CSM } from 'three/examples/jsm/csm/CSM.js';
- this.csm = new CSM( {
- maxFar: params.far,
- cascades: 4,
- mode: params.mode,
- parent: this.scene,
- shadowMapSize: 1024,
- lightDirection: new THREE.Vector3( params.lightX, params.lightY, params.lightZ ).normalize(),
- camera: this.camera
- } );
复制代码 4、增加鼠标事件在3d空间中,鼠标事件主要通过射线来获取鼠标所在位置,可以想象成鼠标放出一道射线,照射到的第一个物体就是鼠标所在位置。此时用的threejs的Raycaster,通过Raycaster给对应的省份增加鼠标移入高亮效果和省份民悬浮展示效果 - this.raycaster = new THREE.Raycaster();
- // 传入需要检测的对象 group,group下的所有对象都会被检测到,如果被射线照到,则intersects有值,表示鼠标当前在这些物体上
- const intersects = this.raycaster.intersectObject( this.group, true );
- // 代码太多就不贴了,见 GitHub源码
复制代码 5、渲染threejs的渲染一般调用原生的requestAnimationFrame,主要做的事就是调用renderer的render方法,当然因为我们做了阴影模糊处理,所以还有别的需要做的: - this.camera.updateMatrixWorld();
- this.csm.update();
- this.renderer.render(this.scene, this.camera);
复制代码 6、动画效果地图上如果有一些动画效果,可以使用TWEEN.js,github地址[2],比如地图标注的出现动画:
|