Three.js 優化之合併 Mesh(1)

參考:
Three.js Optimize Lots of Objects
html

Three.js 優化之合併 Mesh(1)

Three.js 優化方向git

  • 模型不該該太大,添加到場景的時間太長
  • 模型loader能夠使用woker優化
  • 合併 mesh

接下來使用合併 mesh的方式作一個例子:世界人口可視化:World Populationchrome

最終效果:
在這裏插入圖片描述
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()
}

目前效果:
在這裏插入圖片描述

相關文章
相關標籤/搜索