1. 程式人生 > 程式設計 >基於Three.js製作一個3D中國地圖

基於Three.js製作一個3D中國地圖

目錄
  • 1.使用geoon繪製3d地圖
    • 1.1 建立場景相關
    • 1.2 根據json繪製地圖
  • 2.增加光照
    • 3.增加陰影模糊
      • 4.增加滑鼠事件
        • 5.渲染
          • 6.動畫效果

            不想看繁瑣步驟的,可以直接去下載專案,如果可以順便來個star哈哈

            本專案使用-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.r
            enderer.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);

            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,} );
            
                            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.adhttp://www.cppcns.com
            d(mesh); }) }) _this.map.add(province); }) }

            geoJson的座標需要進行墨卡託投影轉換才能轉換成平面座標,這裡需要用到d3

            // 墨卡託投影轉換
            const projection = d3.geoMercator().center([104.0,37.5]).scale(80).translate([0,0]);

            2.增加光照

            我們把各種光都打上,環境光,半球光,點光,平行光。以平行光為例,增加投影,調整投影解析度,避免投影出現馬賽克

            const 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空間中,滑鼠事件主要通過射線來獲取滑鼠所在位置,可以想象成滑鼠放出一道射線,照射到的第一個物體就是滑鼠所在位置。此時用的threejsRaycaster,通過Raycaster給對應的省份增加滑鼠移入高亮效果和省份民懸浮展示效果

            this.raycaster = new THREE.Raycaster();
            // 傳入需要檢測的物件 group,group下的所有物件都會被檢測到,如果被射線照到,則intersects有值,表示滑鼠當前在這些物體上   
            const intersects = this.raycaster.intersectObject( this.group,true );
            // 程式碼太多就不貼了,見 GitHub原始碼

            5.渲染

            threejs的渲染一般呼叫原生的requestAnimationFrame,主要做的事就是呼叫rendererrender方法,當然因為我們做了陰影模糊處理,所以還有別的需要做的:

            this.camera.updateMatrixWorld();
            this.csm.update();
            this.renderer.render(this.scene,this.camera);

            6.動畫效果

            地圖上如果有一些動畫效果,可以使用TWEEN.js,github地址,比如地圖標註的出現動畫:

            基於Three.js製作一個3D中國地圖

            最後再奉上專案地址

            到此這篇關於基於Three.js製作一個3D中國地圖的文章就介紹到這了,更多相關Three.js 3D中國地圖內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!