Three.js 優化之合併 Mesh(1)
Three.js 優化方向git
- 模型不該該太大,添加到場景的時間太長
- 模型loader能夠使用woker優化
- 合併 mesh
接下來使用合併 mesh
的方式作一個例子:世界人口可視化:World Population。chrome
最終效果:
app
一、數據源
世界人口公開數據出處less
數據格式:優化
Parameter | Description | Requirements |
---|---|---|
NCOLS | Number of cell columns. | Integer greater than 0. |
NROWS | Number of cell rows. | Integer greater than 0. |
XLLCENTER or XLLCORNER | X coordinate of the origin (by center or lower left corner of the cell)原點的X座標. | Match with Y coordinate type. |
YLLCENTER or YLLCORNER | Y coordinate of the origin (by center or lower left corner of the cell)原點的Y座標. | Match with X coordinate type. |
CELLSIZE | Cell size. | Greater than 0. |
NODATA_VALUE | The input values to be NoData in the output raster. | Optional. Default is -9999. |
該人口數據有180行, 360列ui
// 行範圍: latNdx: 0~179 // 列範圍: lonNdx: 0~359 // xllcorner: 原點的X座標 -180. // yllcorner: 原點的Y座標 -90. // 經度計算: lonNdx + yllcorner // 緯度計算: latNdx + xllcorner // lon經度範圍: -180~180 // lat維度範圍: -90~90 // (180,90) z軸, // (0, 0) y軸, // (90, -90) x軸
二、動態高度的長方體
1) 這裏,座標系的參考是y軸朝上:
this
2)先放一個立方體:spa
const boxWidth = 1; const boxHeight = 1; const boxDepth = 1; const geometry = new THREE.BoxBufferGeometry(boxWidth, boxHeight, boxDepth); const box = new THREE.Mesh(geometry, material); const material = new THREE.MeshBasicMaterial(); scene.add(box);
3)在x,y上讓其固定縮放到小正方形,在z方向的高度動態調整。code
// amount 該區域的人口數量 + box.scale.set(0.005, 0.005, THREE.MathUtils.lerp(0.01, 0.5, amount));
若是咱們以x軸或者y軸爲旋轉軸進行旋轉,效果:
4)接着,放一個球進去。
function addSphere() { const geometry = new THREE.SphereBufferGeometry(1, 16, 16); const material = new THREE.MeshBasicMaterial({ wireframe: true}); let mesh = new THREE.Mesh(geometry, material) scene.add(mesh); }
5)讓長方體沿着球面移動
長方體能夠沿着經緯度移動。
// 球的半徑 const radius = 1 // 經度助手, -180~180 const lonHelper = new THREE.Object3D(); scene.add(lonHelper); lonHelper.name = "lonHelper" // 緯度助手, -90~90 const latHelper = new THREE.Object3D(); lonHelper.add(latHelper); latHelper.name = "latHelper" // 位置助手, 將對象移動到球體的邊緣 const positionHelper = new THREE.Object3D(); positionHelper.position.z = radius positionHelper.name = "positionHelper" latHelper.add(positionHelper); positionHelper.add(box)
這樣構建對象的層級關係:
原理就是:讓經緯度的旋轉做用在一個球上,而後將這種效果做用到小立方體上。
6)自身高度的偏移
還有一個小問題,這個長方體的一半還在球體的裏面。
// 偏移自身高度的一半 + geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 0, boxDepth/2));
7) 多物體的構建
由於要建立不少小長方體,而每建立一個長方體, 就會多建立3個對象:positionHelper
,lonHelper
,latHelper
。
所以優化一下便可:
positionHelper.updateWorldMatrix(true, false); box.applyMatrix4(positionHelper.matrixWorld);
三、小結
完整代碼:
// import * as THREE from './lib/three.module.js' import Stage from './Stage.js' import { loadFile} from "./utils.js" window.THREE = THREE class App { constructor() { window.lm = this this.stage = new Stage("#app") this.stage.run() this.stage.camera.position.set(0,0,5) this.parseData = this.parseData.bind(this) this.addBoxes = this.addBoxes.bind(this) this.addSphere() this.loadData() } addBoxes(file) { let aaa = 0 const scene = this.stage.scene const { min, max, data} = file; const range = max - min; // make one box geometry const boxWidth = 1; const boxHeight = 1; const boxDepth = 1; const geometry = new THREE.BoxBufferGeometry(boxWidth, boxHeight, boxDepth); geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 0, 0.5)); // 這些助手將使放置盒子變得容易。 咱們能夠將lon輔助程序沿其Y軸旋轉到經度 const lonHelper = new THREE.Object3D(); scene.add(lonHelper); lonHelper.name = "lonHelper" // 咱們將latHelper沿其X軸旋轉到緯度 const latHelper = new THREE.Object3D(); lonHelper.add(latHelper); latHelper.name = "latHelper" // 位置助手將對象移動到球體的邊緣 const positionHelper = new THREE.Object3D(); positionHelper.position.z = 1; positionHelper.name = "positionHelper" latHelper.add(positionHelper); // latNdx: 0~179 // lonNdx: 0~359 // xllcorner: 原點的X座標 -180. // yllcorner: 原點的Y座標 -90. data.forEach((row, latNdx) => { row.forEach((value, lonNdx) => { if (value === undefined) { return; } const amount = (value - min) / range; const material = new THREE.MeshBasicMaterial(); const hue = THREE.MathUtils.lerp(0.7, 0.3, amount); const saturation = 1; const lightness = THREE.MathUtils.lerp(0.4, 1.0, amount); material.color.setHSL(hue, saturation, lightness); const mesh = new THREE.Mesh(geometry, material); mesh.name = "box" scene.add(mesh); // adjust the helpers to point to the latitude and longitude // 調整助手以指向經度和緯度 lonHelper.rotation.y = THREE.MathUtils.degToRad(lonNdx + file.xllcorner); latHelper.rotation.x = THREE.MathUtils.degToRad(latNdx + file.yllcorner); // use the world matrix of the position helper to position this mesh. // 使用位置助手的世界矩陣來定位該網格。 positionHelper.updateWorldMatrix(true, false); mesh.applyMatrix4(positionHelper.matrixWorld); mesh.scale.set(0.005, 0.005, THREE.MathUtils.lerp(0.01, 0.5, amount)); }); }); } parseData(text) { const data = []; const settings = { data}; let max; let min; // split into lines text.split('\n').forEach((line) => { // split the line by whitespace const parts = line.trim().split(/\s+/); if (parts.length === 2) { // only 2 parts, must be a key/value pair settings[parts[0]] = parseFloat(parts[1]); } else if (parts.length > 2) { // more than 2 parts, must be data const values = parts.map((v) => { const value = parseFloat(v); if (value === settings.NODATA_value) { return undefined; } max = Math.max(max === undefined ? value : max, value); min = Math.min(min === undefined ? value : min, value); return value; }); data.push(values); } }); return Object.assign(settings, { min, max}); } addSphere() { const loader = new THREE.TextureLoader(); const texture = loader.load('./texture/world.jpg'); const geometry = new THREE.SphereBufferGeometry(1, 32, 32); const material = new THREE.MeshBasicMaterial({ map: texture, wireframe: false}); let mesh = new THREE.Mesh(geometry, material) mesh.name = "sphere" this.stage.scene.add(mesh); } loadData() { loadFile('./data/gpw_v4_population_count_rev11_2020_1_deg.asc') .then(this.parseData) .then(this.addBoxes); } } window.onload = () => { let app = new App() }
目前效果: