國內(nèi)知名域名注冊網(wǎng)站網(wǎng)站推廣排名教程
原生的 three.js 目前不支持 3d tiles 數(shù)據(jù)的加載,不過開源社區(qū)已經(jīng)給出了一些解決方案,其中最活躍的要屬 3DTilesRendererJS。它為 three.js 提供了加載和調(diào)度 3d tiles 數(shù)據(jù)的基本能力,雖說和 Cesium.js 對 3d tiles 的支持相比還有很大的差距,但也比沒有的好。畢竟 3d tiles 數(shù)據(jù)的加載和調(diào)度還是比較復(fù)雜的,要自己寫也沒那么容易,這一點在以前研究 Cesium.js 相關(guān)源碼的時候就深有體會。
3DTilesRendererJS 最核心的類是 TilesRenderer,用來渲染和調(diào)度一份 3d tiles 數(shù)據(jù),相當(dāng)于Cesium.js 里的 Cesium3DTileset。使用起來非常簡單,構(gòu)造的時候傳入 JSON 文件的 url 即可。
const tileset = new TilesRenderer("http://localhost:8080/XXX/tileset.json");
構(gòu)造?TilesRenderer 實例
接著,需要把?TilesRenderer?實例和?three.js 的 camera 以及 renderer 關(guān)聯(lián)起來,根據(jù) three.js 的相機和渲染器參數(shù)來設(shè)置切片顯示的分辨率。
tileset.setCamera(camera);
tileset.setResolutionFromRenderer(camera, renderer);
關(guān)聯(lián) three.js 的相機和渲染器參數(shù)?
很多 3d tiles 數(shù)據(jù)都是做了頂點壓縮或紋理壓縮的,比如頂點的 DRACO 壓縮、KTX2 和 DDS 等紋理壓縮格式,對于這類數(shù)據(jù)需要在GLTF解析器(GLTFLoader)中添加解壓縮的能力。下面以解壓縮 DRACO 為例用代碼加以說明。
// 配置GLTF數(shù)據(jù)的解析器
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('./jsm/libs/draco/gltf/');const loader = new GLTFLoader(tileset.manager);
loader.setDRACOLoader(dracoLoader);
tileset.manager.addHandler(/\.gltf$/, loader);
為GLTF解析器配置解壓縮 DRACO 的能力?
3d tiles數(shù)據(jù)以往基本上都是在 WGS84橢球上呈現(xiàn)的?,F(xiàn)在要在 three.js 的局部場景下展示,則需要把它放在局部場景相機的視野范圍內(nèi),并保證數(shù)據(jù)的上方向正確。這里封裝了一個 adjustTilesPositionAndDirection 方法,將數(shù)據(jù)放置在局部場景的中心,并將Y軸正方向(Y+)作為數(shù)據(jù)的上方向。這樣數(shù)據(jù)在 three.js 三維場景中就能正常擺放了。
function rotationBetweenDirections(dir1, dir2) {const rotation = new THREE.Quaternion();const a = new THREE.Vector3().crossVectors(dir1, dir2);rotation.x = a.x;rotation.y = a.y;rotation.z = a.z;rotation.w = 1 + dir1.clone().dot(dir2);rotation.normalize();return rotation;
}function adjustTilesPositionAndDirection(tiles) {if (!tiles) {return;}const sphere = new THREE.Sphere();tiles.getBoundingSphere(sphere);const position = sphere.center.clone();const distanceToEllipsoidCenter = position.length();const surfaceDirection = position.normalize();const up = new THREE.Vector3(0, 1, 0);const rotationToNorthPole = rotationBetweenDirections(surfaceDirection, up);tiles.group.quaternion.x = rotationToNorthPole.x;tiles.group.quaternion.y = rotationToNorthPole.y;tiles.group.quaternion.z = rotationToNorthPole.z;tiles.group.quaternion.w = rotationToNorthPole.w;tiles.group.position.y = - distanceToEllipsoidCenter;
}
調(diào)整數(shù)據(jù)在 three.js 場景中的位置和上方向?
最后在每一幀渲染時都去執(zhí)行一次 TilesRenderer 的更新。至此,一份3d tiles數(shù)據(jù)的基本加載就完成了。
function renderLoop() {// 更新 TilesRenderer 之前需要更新 three.js 的相機參數(shù)camera.updateMatrixWorld();tileset.update(); // 更新 TilesRendererrenderer.render(scene, camera);
}
每一幀都更新 TilesRenderer?
以上是使用 3DTilesRendererJS 的基本流程。還可以做一些輔助工作。
可以根據(jù)需要為數(shù)據(jù)注冊一些插件,可選的插件在官方文檔中查看。下面的示例以調(diào)試插件為例,簡要說明插件的注冊和使用方式。
// 注冊調(diào)試插件
tileset.registerPlugin(new DebugTilesPlugin());// ...// 獲取調(diào)試插件,并顯示包圍盒的線框
tileset.getPluginByName('DEBUG_TILES_PLUGIN').displayBoxBounds = true;
插件的注冊和使用?
當(dāng)場景中加載了多份 3d tiles 數(shù)據(jù)時,最好共享內(nèi)存和下載隊列,以減少性能開銷。
// 設(shè)置圖層1的緩存大小
tileset.lruCache.minSize = 900;
tileset.lruCache.maxSize = 1300;// 圖層2和圖層1共享內(nèi)存和下載隊列以減少性能開銷
tileset2.lruCache = tileset.lruCache;
tileset2.downloadQueue = tileset.downloadQueue;
tileset2.parseQueue = tileset.parseQueue;
?共享內(nèi)存和下載隊列
個人覺得和 Cesium.js 相比,3DTilesRendererJS 加載和調(diào)度 3d tiles 的能力還是挺弱的。小場景、和數(shù)據(jù)之間交互(操作、修改)要求不那么高的情況下可以嘗試。如果是做大場景下的 GIS 應(yīng)用,也許 Cesium.js 和 Three.js 做深度融合(繪制在同一個 canvas 上,深度值做統(tǒng)一),GIS 功能交給 Cesium.js,Three.js 做一些效果上的補充,可能會是更好的方案。