Three.js中載入和渲染3D Tiles

当时明月在曾照彩云归發表於2024-03-20

1. 引言

3D Tiles 是 3D GIS 中常見的三維資料格式,能否用Three.js來載入渲染呢?肯定是可以,Three.js只是一個WebGL框架,渲染資料肯定可以,但是載入、解析資料得手動解決

有沒有一個第三方庫解決這個問題呢?有,比如這個:NASA-AMMOS/3DTilesRendererJS: Renderer for 3D Tiles in Javascript using three.js (github.com)

這裡就簡要記錄如何在Three.js中載入 3D Tiles

2. 載入3D Tiles

首先,搭建一個簡單的三維場景

<!DOCTYPE html>
<html lang="en">
 
<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
	<style>
		html,
		body,
		canvas {
			height: 100%;
			width: 100%;
			margin: 0;
		}
	</style>
	
</head>
 
<body>
	<canvas id="canvas"></canvas>
 
	<script type="importmap">
		{
			"imports": {
				"three": "https://unpkg.com/three/build/three.module.js",
				"three/addons/": "https://unpkg.com/three/examples/jsm/"
			}
		}
	</script>
	
	<script type="module">
		import * as THREE from 'three';
		import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 
		const scene = new THREE.Scene();
 
		const canvas = document.querySelector('#canvas');
		const camera = new THREE.PerspectiveCamera(75,  canvas.clientWidth / canvas.clientHeight , 0.1, 1000);
		camera.position.z = 5;
 
		const renderer = new THREE.WebGLRenderer({
			canvas: document.querySelector('#canvas')
		});
		renderer.setSize(window.innerWidth, window.innerHeight, false)
		
		const geometry = new THREE.BoxGeometry(1, 1, 1);
		const material = new THREE.MeshPhongMaterial({
			color: 0x00ff00
		});
		const cube = new THREE.Mesh(geometry, material);
		scene.add(cube);
 
		const light = new THREE.DirectionalLight(0xffffff, 1);
		light.position.set(0, 0, 5);
		scene.add(light);
 
		const controls = new OrbitControls( camera, renderer.domElement );
		
		function animate() {
			requestAnimationFrame(animate);
			cube.rotation.x += 0.01;
			cube.rotation.y += 0.01;
			renderer.render(scene, camera);
		}
		animate();
	</script>
</body>
 
</html>

然後是引入3DTilesRendererJS,這裡引入的是GoogleTilesRenderer來載入Google的線上地圖,另外還需要使用GLTFLoaderDRACOLoader以進行資料解析

<script type="importmap">
		{
			"imports": {
				"three": "https://unpkg.com/three/build/three.module.js",
				"three/addons/": "https://unpkg.com/three/examples/jsm/",
                "3DTilesRendererJS": "https://cdn.jsdelivr.net/npm/3d-tiles-renderer@0.3.30/+esm"
			}
		}
</script>

<script type="module">
    import * as THREE from 'three';
    import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
    import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
    import { TilesRenderer, GoogleTilesRenderer } from '3DTilesRendererJS'
    import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
    // ...
</script>

下一步是載入谷歌線上3D Tiles並加入場景中

const tilesRenderer = new GoogleTilesRenderer( 'AIzaSyBQ7Wj99aTxRqET-22qYWGFcDCWgVDt89A' ); // 傳入的是谷歌傾斜攝影的API key
tilesRenderer.setLatLonToYUp( 36.266494 * THREE.MathUtils.DEG2RAD, 120.460205 * THREE.MathUtils.DEG2RAD ); // Tokyo Tower

tilesRenderer.setCamera( camera );
tilesRenderer.setResolutionFromRenderer( camera, renderer );

const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath( 'https://unpkg.com/three@0.153.0/examples/jsm/libs/draco/gltf/' );

const loader = new GLTFLoader( tilesRenderer.manager );
loader.setDRACOLoader( dracoLoader );

tilesRenderer.manager.addHandler( /\.gltf$/, loader );

scene.add( tilesRenderer.group );

最後在每一幀中更新3D Tiles渲染器

function animate() {
    requestAnimationFrame(animate);
    tilesRenderer.update();
    // ...
    renderer.render(scene, camera);
}

載入的結果如下:

image-20240320163435241

完整程式碼如下:

<!DOCTYPE html>
<html lang="en">
 
<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
	<style>
		html,
		body,
		canvas {
			height: 100%;
			width: 100%;
			margin: 0;
		}
	</style>
	
</head>
 
<body>
	<canvas id="canvas"></canvas>
 
	<script type="importmap">
		{
			"imports": {
				"three": "https://unpkg.com/three/build/three.module.js",
				"three/addons/": "https://unpkg.com/three/examples/jsm/"
			}
		}
	</script>
	
	<script type="module">
		import * as THREE from 'three';
		import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
		import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
        import { TilesRenderer, GoogleTilesRenderer } from 'https://cdn.jsdelivr.net/npm/3d-tiles-renderer@0.3.30/+esm'
        import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
 
		const scene = new THREE.Scene();
 
		const canvas = document.querySelector('#canvas');
		const camera = new THREE.PerspectiveCamera(75,  canvas.clientWidth / canvas.clientHeight , 0.1, 100000);
		camera.position.y = 500;
		camera.position.z = 500;
 
		const renderer = new THREE.WebGLRenderer({
			canvas: document.querySelector('#canvas')
		});
		renderer.setSize(window.innerWidth, window.innerHeight, false)
 
		const controls = new OrbitControls( camera, renderer.domElement );

        const tilesRenderer = new GoogleTilesRenderer( 'AIzaSyBQ7Wj99aTxRqET-22qYWGFcDCWgVDt89A' );
        tilesRenderer.setLatLonToYUp( 35.6586 * THREE.MathUtils.DEG2RAD, 139.7454 * THREE.MathUtils.DEG2RAD ); // Tokyo Tower

        tilesRenderer.setCamera( camera );
        tilesRenderer.setResolutionFromRenderer( camera, renderer );

        const dracoLoader = new DRACOLoader();
        dracoLoader.setDecoderPath( 'https://unpkg.com/three@0.153.0/examples/jsm/libs/draco/gltf/' );

        const loader = new GLTFLoader( tilesRenderer.manager );
        loader.setDRACOLoader( dracoLoader );

        tilesRenderer.manager.addHandler( /\.gltf$/, loader );

        scene.add( tilesRenderer.group );
		
		function animate() {
			requestAnimationFrame(animate);
            tilesRenderer.update();
			renderer.render(scene, camera);
		}
		animate();
	</script>
</body>
 
</html>

上面載入的是日本東京的傾斜攝影資料,那在沒有傾斜攝影資料的地方呢,比如中國的某些城市,答案是會載入類似於高程與遙感影像的合成資料,如下圖:

image-20240320163934943

此時只是把經緯度做以下修改:

tilesRenderer.setLatLonToYUp( 36.266494 * THREE.MathUtils.DEG2RAD, 120.460205 * THREE.MathUtils.DEG2RAD ); // 山東青島

總的來說,有時可以在Three.js中載入這樣的3D Tiles作為底圖來使用,更多的使用方法可參考官方文件:NASA-AMMOS/3DTilesRendererJS: Renderer for 3D Tiles in Javascript using three.js (github.com)

3. 參考資料

[1] NASA-AMMOS/3DTilesRendererJS: Renderer for 3D Tiles in Javascript using three.js (github.com)

[2] 3D Tiles Renderer Options Example (nasa-ammos.github.io)

[3] threejs載入3dtiles(傾斜攝影)資料_threejs 3dtiles-CSDN部落格

相關文章