• 回答数

    0

  • 浏览数

    667

  • 收藏数

    0

作者:团子良 发表于 2022-12-6 17:58:19
跳转到指定楼层
不想看繁琐步骤的,可以直接去github下载项目,如果可以顺便来个star哈哈
本项目使用vue-cli创建,但不影响使用,主要绘制都已封装成类
1、使用geoJson绘制3d地图1.1 创建场景相关
  1. // 创建webGL渲染器
  2. this.renderer = new THREE.WebGLRenderer( { antialias: true,alpha: true} );
  3. this.renderer.shadowMap.enabled = true; // 开启阴影
  4. this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
  5. this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
  6. this.renderer.toneMappingExposure = 1.25;   

  7. // 根据自己的需要调整颜色模式
  8. // this.renderer.outputEncoding = THREE.sRGBEncoding;  

  9. this.renderer.outputEncoding = THREE.sHSVEncoding;
  10. this.renderer.setPixelRatio( window.devicePixelRatio );
  11. // 清除背景色,透明背景
  12. this.renderer.setClearColor(0xffffff, 0);
  13. this.renderer.setSize(this.width, this.height);

  14. // 场景
  15. this.scene = new THREE.Scene();
  16. this.scene.background = null
  17. // 相机 透视相机
  18. this.camera = new THREE.PerspectiveCamera(45, this.width / this.height, 0.1, 5000);
  19. this.camera.position.set(0, -40, 70);
  20. this.camera.lookAt(0, 0, 0);
复制代码
1.2 根据json绘制地图
利用THREE.Shape绘制地图的平面边数据,再用THREE.ExtrudeGeometry将一个面拉高成3d模型,3d饼图同理也可以这么制作
  1. let jsonData = require('./json/china.json')
  2. this.initMap(jsonData);

  3. // initMap 方法主要部分
  4. initMap(chinaJson) {
  5.     /* ...省略
  6.         ...
  7.     */
  8.     chinaJson.features.forEach((elem, index) => {
  9.         // 定一个省份3D对象
  10.         const province = new THREE.Object3D();
  11.         // 每个的 坐标 数组
  12.         const { coordinates } = elem.geometry;
  13.         const color = COLOR_ARR[index % COLOR_ARR.length]
  14.         // 循环坐标数组
  15.         coordinates.forEach(multiPolygon => {
  16.             
  17.             multiPolygon.forEach((polygon) => {
  18.                 const shape = new THREE.Shape();
  19.                
  20.                 for (let i = 0; i < polygon.length; i++) {
  21.                     let [x, y] = projection(polygon[i]);
  22.                     
  23.                     if (i === 0) {
  24.                         shape.moveTo(x, -y);
  25.                     }
  26.                     shape.lineTo(x, -y);
  27.                 }
  28.    
  29.                 const extrudeSettings = {
  30.                     depth: 4,
  31.                     bevelEnabled: true,
  32.                     bevelSegments: 1,
  33.                     bevelThickness: 0.2
  34.                 };
  35.    
  36.                 const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
  37.                
  38.                 // 平面部分材质
  39.                 const material = new THREE.MeshStandardMaterial( {
  40.                     metalness: 1,
  41.                     color: color,
  42.                     
  43.                 } );
  44.                 // 拉高部分材质
  45.                 const material1 = new THREE.MeshStandardMaterial( {
  46.                     metalness: 1,
  47.                     roughness: 1,
  48.                     color: color,
  49.                     
  50.                 } );

  51.                 const mesh = new THREE.Mesh(geometry, [
  52.                     material,
  53.                     material1
  54.                 ]);
  55.                 // 设置高度将省区分开来
  56.                 if (index % 2 === 0) {
  57.                     mesh.scale.set(1, 1, 1.2);
  58.                 }
  59.                 // 给mesh开启阴影
  60.                 mesh.castShadow = true
  61.                 mesh.receiveShadow = true
  62.                 mesh._color = color
  63.                 province.add(mesh);

  64.             })
  65.    
  66.         })
  67.    
  68.         _this.map.add(province);
  69.         
  70.     })
  71. }
复制代码
geoJson的坐标需要进行墨卡托投影转换才能转换成平面坐标,这里需要用到d3
  1. // 墨卡托投影转换
  2. const projection = d3.geoMercator().center([104.0, 37.5]).scale(80).translate([0, 0]);
复制代码
2、增加光照我们把各种光都打上,环境光,半球光,点光,平行光。以平行光为例,增加投影,调整投影分辨率,避免投影出现马赛克
  1. onst light = new THREE.DirectionalLight( 0xffffff, 0.5 );
  2. light.position.set( 20, -50, 20 );

  3. light.castShadow = true;
  4. light.shadow.mapSize.width = 1024;
  5. light.shadow.mapSize.height = 1024;

  6. this.scene.add(light);
复制代码
castShadow = true表示开启投影
3、增加阴影模糊
默认的阴影没有模糊效果,看起来像白炽灯照射的样子,没有柔和感。使用官方示例中的csm来增加阴影模糊
  1. import { CSM } from 'three/examples/jsm/csm/CSM.js';

  2. this.csm = new CSM( {
  3.     maxFar: params.far,
  4.     cascades: 4,
  5.     mode: params.mode,
  6.     parent: this.scene,
  7.     shadowMapSize: 1024,
  8.     lightDirection: new THREE.Vector3( params.lightX, params.lightY, params.lightZ ).normalize(),
  9.     camera: this.camera
  10. } );
复制代码
4、增加鼠标事件
在3d空间中,鼠标事件主要通过射线来获取鼠标所在位置,可以想象成鼠标放出一道射线,照射到的第一个物体就是鼠标所在位置。此时用的threejs的Raycaster,通过Raycaster给对应的省份增加鼠标移入高亮效果和省份民悬浮展示效果
  1. this.raycaster = new THREE.Raycaster();
  2. // 传入需要检测的对象 group,group下的所有对象都会被检测到,如果被射线照到,则intersects有值,表示鼠标当前在这些物体上   
  3. const intersects = this.raycaster.intersectObject( this.group, true );
  4. // 代码太多就不贴了,见 GitHub源码
复制代码
5、渲染
threejs的渲染一般调用原生的requestAnimationFrame,主要做的事就是调用renderer的render方法,当然因为我们做了阴影模糊处理,所以还有别的需要做的:
  1. this.camera.updateMatrixWorld();
  2. this.csm.update();
  3. this.renderer.render(this.scene, this.camera);
复制代码
6、动画效果
地图上如果有一些动画效果,可以使用TWEEN.js,github地址[2],比如地图标注的出现动画:



分享:
回复

使用道具

成为第一个回答人

高级模式 评论
您需要登录后才可以回帖 登录 | 立即注册 微信登录