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的線上地圖,另外還需要使用GLTFLoader
和DRACOLoader
以進行資料解析
<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);
}
載入的結果如下:
完整程式碼如下:
<!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>
上面載入的是日本東京的傾斜攝影資料,那在沒有傾斜攝影資料的地方呢,比如中國的某些城市,答案是會載入類似於高程與遙感影像的合成資料,如下圖:
此時只是把經緯度做以下修改:
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部落格