1. 程式人生 > 實用技巧 >在maptalks中載入三維模型obj,fbx,glb

在maptalks中載入三維模型obj,fbx,glb

  • maptalks 是一個基於WebGL的三維地圖js庫,基於maptalks可以快速建立web三維地圖,載入各種地圖資料,生成對應的圖層,載入三維模型等。

  • 地圖載入。maptalks支援標準的地圖資料型別,如GeoJson,TileServer等。

    GeoJson格式是一種地理資訊物件描述檔案,可以是本地的,也可以儲存在遠端伺服器上,其基本格式如下:

    

{
"type": "FeatureCollection",
"name": "line0706",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84
" } }, "features": [ { "type": "Feature", "properties": { "id": 26, "OID": 26, "PID": "1JS2036-1JS2037", "Shape_Length": 215.78135367381537, "linegj": 400.0, "linecz": "球墨鑄鐵" }, "geometry": { "type": "LineString", "coordinates": [ [ 117.139587450494673, 31.980963606892612 ], [ 117.141572764515146, 31.980002698897021
] ] } }, { "type": "Feature", "properties": { "id": 27, "OID": 27, "PID": "1JS2037-1JS2038", "Shape_Length": 174.33523286131555, "linegj": 400.0, "linecz": "球墨鑄鐵" }, "geometry": { "type": "LineString", "coordinates": [ [ 117.141572764515146, 31.980002698897021 ], [ 117.143128167657636, 31.979157606398935 ] ] } }, {
"type": "Feature", "properties": { "id": 28, "OID": 28, "PID": "1JS2038-AJS2720", "Shape_Length": 74.157593232456321, "linegj": 400.0, "linecz": "球墨鑄鐵" }, "geometry": { "type": "LineString", "coordinates": [ [ 117.143128167657636, 31.979157606398935 ], [ 117.143830270406454, 31.978859076104776 ] ] } }, { "type": "Feature", "properties": { "id": 29, "OID": 29, "PID": "1JS2039-DJS504", "Shape_Length": 116.76683212772929, "linegj": 400.0, "linecz": "球墨鑄鐵" }, "geometry": { "type": "LineString", "coordinates": [ [ 117.146490176708213, 31.977233403153686 ], [ 117.147518501281553, 31.9766498062083 ] ] } }, { "type": "Feature", "properties": { "id": 30, "OID": 30, "PID": "1JS2040-1JS2041", "Shape_Length": 346.5596390599348, "linegj": 400.0, "linecz": "球墨鑄鐵" }, "geometry": { "type": "LineString", "coordinates": [ [ 117.148207019802612, 31.976299944711535 ], [ 117.151189187226777, 31.974481644858205 ] ] } } ] }

  

    可以看出,geoJson格式是在Json資料格式的基礎上增加了關於GIS的一些屬性描述資訊,

    如type:Feature表示是一個地理物件,properties表示物件的屬性值,geometry表示要描述的地理物件,

    裡面type:LineString表示型別是一個線段, coordinates 表示座標,後面接著的陣列表示一組座標值,來表示起始點座標位置。

  

  ArcGISTileLayer

     let ArcGIS_Layer = new maptalks.ArcGISTileLayer('line', {

          visible: true,

          urlTemplate: 'http://127.0.0.1:6080/server/rest/services/line/MapServer',

     });

    urlTemplate對應的值就是已經部署在ArcGIS伺服器上的地圖服務地址。

  • 三維模型。基於maptalks 有一個使用threejs開發 maptalks.threeLayer 庫,可以在maptalks中直接引用,在maptalks中載入三維模型,其實就是使用threejs載入三維模型。

   常用的三維模型有以下幾種,obj,fbx,glb。使用同一個模型匯出的三種檔案格式大小如下圖:

  

  從檔案大小看,fbx和glb更有優勢。

  使用Windows自帶的3D模型開啟Wie

  obj模型一般情況下包含2-3個檔案,obj,mtl,jpg(png), obj 為模型檔案,mtl 和 jpg檔案為材質檔案,obj檔案可以通過文字編輯器開啟,可以看到裡面指定了該obj檔案對應的材質檔案的名稱。其餘的是在三維空間中繪製的點位置。

    

  需要使用材質的位置則指定使用mtl檔案中的材質名稱。

    

  在mtl檔案中,則可以看到該材質的名稱對應的引數

    

  fbx檔案和glb為二進位制編碼後的檔案,使用文字編輯器開啟為亂碼。

  從可讀性看,obj檔案更佔優勢。

  • 使用maptalks.threelayer 載入三維模型

    threejs針對obj、fbx、glb三種格式的三維模型有專門的示例,針對threejs載入,可以在其官網 https://threejs.org/examples 檢視,這裡專門講解maptalks 地圖中的載入。

    首先,threejs 提供了用於專門解析模型的 js 檔案,  

    

    需要注意的是:在使用的過程中,需要確認 maptalks.js, maptalks.threelayer.js, 與 OBJLoader.js,MTLLoader.js,FBXLoader.js,GLTFLoader.js所引用three.js 版本一致,否則會出現函式不相容的情況,無法呼叫成功。

    載入obj的方法為:    

 new THREE.MTLLoader(manager)
                .setPath('/js/symap/lib/maptalks/models/obj/ws/')
                .load('gs.mtl', function (materials) {

                    materials.preload();

                    new THREE.OBJLoader(manager)
                        .setMaterials(materials)
                        .setPath('/js/symap/lib/maptalks/models/obj/ws/')
                        .load('gs.obj', function (object) {

                            object.traverse(function (child) {
                                if (child instanceof THREE.Mesh) {
                                    child.scale.set(0.01, 0.01, 0.01);
                                    child.rotation.set(Math.PI * 1 / 2, Math.PI * 1 / 8, 0);
                                }
                            });

                            _this.getObject3d().add(object);

                            const z = layer.distanceToVector3(50, 50).x;
                            const position = layer.coordinateToVector3(
                                coordinate,
                                z
                            );

                            _this.getObject3d().position.copy(position);

                        });
                });

    載入 fbx 的方法為:

var loader = new THREE.FBXLoader();
            loader.load('/js/symap/lib/maptalks/models/fbx/beng.fbx', function (object) {

                object.scale.set(0.002, 0.002, 0.002);
                object.rotation.set(-Math.PI * 1 / 2, -Math.PI * 1 / 2, 0);

                _this._mixer = new THREE.AnimationMixer(object);

                const action = _this._mixer.clipAction(object.animations[0]);

                if (title.indexOf("測試") > -1) { //速度不為0,顯示運轉
                    action.stop();
                } else {
                    action.play();
                }

                object.traverse(function (child) {

                    if (child.isMesh) {

                        if (child.name == "bengke") {

                            child.material.visible = false;

                        }

                        child.castShadow = true;
                        child.receiveShadow = true;

                    }

                });

                _this.getObject3d().add(object);

                const z = layer.distanceToVector3(150, 150).x;
                const position = layer.coordinateToVector3(
                    coordinate,
                    z
                );
                _this.getObject3d().position.copy(position);

            });

    載入 glb 檔案的方法為:

var gltfLoader = new THREE.GLTFLoader(manager);

            const dracoLoader = new THREE.DRACOLoader(manager);

            gltfLoader.setDRACOLoader(dracoLoader);

            gltfLoader.setPath('/js/symap/lib/maptalks/models/gltf/');
            gltfLoader.load('gssb.glb', function (gltf) {

                var object = gltf.scene;
                object.scale.set(0.3, 0.3, 0.3);
                _this.getObject3d().add(object);

                object.traverse(function (child) {
                    if (child instanceof THREE.Mesh) {
                        //if (child.name == "bengke") {
                        //    child.material.visible = false;
                        //}
                        child.rotation.set(Math.PI * 1 / 2, 0, 0);
                        child.scale.set(0.1, 0.1, 0.1);
                    }
                });

                const z = layer.distanceToVector3(150, 150).x;
                const position = layer.coordinateToVector3(
                    coordinate,
                    z
                );

                _this.getObject3d().position.copy(position);

                _this._mixer = new THREE.AnimationMixer(object);
                var clip = gltf.animations[0];
                _this._mixer.clipAction(clip.optimize()).play();

            });

    在glb的載入中,另外引用了一個 DRACOLoader.js 這是一個js庫可以在模型載入的過程中對模型進行壓縮。

    踩坑記錄:

    在3D軟體設計模型時,將模型放在(0,0,0)的位置,否則模型載入後不能出現在Scene中,或者不在程式碼設定的位置上。

    另外對比同一開發環境下單個模型的載入方式對比:支援不支援 待測

格式

大小

多檔案

可讀性

載入速度

支援動畫

執行壓縮

預壓縮

備註

Obj

17.36M

30ms

fbx

6.3M

21ms

glb

8.3

25ms

    在此次測試中,從載入時間來看,fbx>glb>obj ,綜合網路搜尋結果,glb模型對其它三維GIS軟體(Cesium,CityEngine)的相容性更好,所以在本專案中使用glb格式的三維模型檔案。

    下圖為在maptalks中載入的三維模型:

    

    針對模型較大的問題,網上還有其它解決方法,比如DRACOLoader將模型壓縮為drc檔案進行載入;大模型拆分小模型然後分別壓縮drc檔案載入;精細模型減少內部結構,保留表面減少模型實際大小等,在開發中再進行測試。