Three.js 优化之合并 Mesh(1)

参考:
Three.js Optimize Lots of Objects

Three.js 优化之合并 Mesh(1)

Three.js 优化方向

  • 模型不应该太大,添加到场景的时间太长
  • 模型loader可以使用woker优化
  • 合并 mesh

接下来使用合并 mesh的方式做一个例子:世界人口可视化:World Population。

最终效果:
Three.js 优化之合并 Mesh(1)_第1张图片

1、数据源

世界人口公开数据出处

数据格式:

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轴
2、动态高度的长方体

1) 这里,坐标系的参考是y轴朝上:
Three.js 优化之合并 Mesh(1)_第2张图片

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);

Three.js 优化之合并 Mesh(1)_第3张图片

3)在x,y上让其固定缩放到小正方形,在z方向的高度动态调整。

// amount 该区域的人口数量
+ box.scale.set(0.005, 0.005, THREE.MathUtils.lerp(0.01, 0.5, amount));

Three.js 优化之合并 Mesh(1)_第4张图片

如果我们以x轴或者y轴为旋转轴进行旋转,效果:
Three.js 优化之合并 Mesh(1)_第5张图片

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);
  }

Three.js 优化之合并 Mesh(1)_第6张图片

5)让长方体沿着球面移动
长方体可以沿着经纬度移动。
Three.js 优化之合并 Mesh(1)_第7张图片

// 球的半径
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)

这样构建对象的层级关系:
Three.js 优化之合并 Mesh(1)_第8张图片

原理就是:让经纬度的旋转作用在一个球上,然后将这种效果作用到小立方体上。

6)自身高度的偏移

还有一个小问题,这个长方体的一半还在球体的里面。
Three.js 优化之合并 Mesh(1)_第9张图片

// 偏移自身高度的一半
+ geometry.applyMatrix4(new THREE.Matrix4().makeTranslation(0, 0, boxDepth/2));

Three.js 优化之合并 Mesh(1)_第10张图片

7) 多物体的构建
因为要创建很多小长方体,而每创建一个长方体, 就会多创建3个对象:positionHelper,lonHelper,latHelper
因此优化一下即可:

positionHelper.updateWorldMatrix(true, false);
box.applyMatrix4(positionHelper.matrixWorld);
3、小结

完整代码:

// 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()
}

目前效果:
Three.js 优化之合并 Mesh(1)_第11张图片

你可能感兴趣的:(Threejs-Shader,webgl,three.js)