赞
踩
Three.js 优化方向
接下来使用合并 mesh
的方式做一个例子:世界人口可视化:World Population。
最终效果:
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列
// 行范围: 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轴朝上:
2)先放一个立方体:
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方向的高度动态调整。
// 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() }
目前效果:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。