Skip to content

29. 包围盒与模型大小计算

Published:

不同模型的大小都不同:

image.png

image.png

如果我们想在加载进来的多个模型的中央位置,加一个圆环,如何做呢?

如何知道不同模型的大小呢?

这个就涉及到包围盒的概念了。

要计算模型的大小,就是计算它的最小包围盒的长宽高:

image.png

image.png

Three.js 提供了计算包围盒的 api

image.png

并且还提供了一个 helper 来可视化包围盒:

image.png

我们来试一下:

npx create-vite box3-test

image.png

创建 vite 项目。

进入项目,安装依赖:

npm install
npm install --save three
npm install --save-dev @types/three

改下 src/index.js

import './style.css';
import * as THREE from 'three';
import {
    OrbitControls
} from 'three/addons/controls/OrbitControls.js';
import mesh from './mesh.js';

const scene = new THREE.Scene();

scene.add(mesh);

const light = new THREE.DirectionalLight(0xffffff);
light.position.set(500, 300, 600);
scene.add(light);

const light2 = new THREE.AmbientLight();
scene.add(light2);

const axesHelper = new THREE.AxesHelper(1000);
scene.add(axesHelper);

const width = window.innerWidth;
const height = window.innerHeight;

const camera = new THREE.PerspectiveCamera(60, width / height, 1, 10000);
camera.position.set(500, 300, 400);
camera.lookAt(0, 0, 0);

const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height)

function render() {
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}

render();

document.body.append(renderer.domElement);

const controls = new OrbitControls(camera, renderer.domElement);

创建 Scene、Light、Camera、Renderer

改下 style.css

body {
    margin: 0;
}

然后创建 mesh.js

import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

const loader = new GLTFLoader();

const mesh = new THREE.Group();

loader.load("./Michelle.glb", function (gltf) {
    console.log(gltf);
    gltf.scene.scale.setScalar(150);
    mesh.add(gltf.scene);
})

export default mesh;

还是用之前的模型:

https://github.com/QuarkGluonPlasma/threejs-course-code/blob/main/dancing-mirror/public/Michelle.glb

image.png

放到 public 目录下:

image.png

先跑起来看下:

npm run dev

image.png

2025-05-31 22.19.50.gif

如何知道它的大小呢?

我们先用 BoxHelper 把包围盒可视化出来:

image.png

const helper = new THREE.BoxHelper(gltf.scene, 'pink');
mesh.add(helper);

看一下:

2025-05-31 22.22.54.gif

可以看到,模型外面的最小包围盒就可视化出来了。

那如何计算这个包围盒的大小呢?

用 Box3 的 api。

image.png

const box = new THREE.Box3();
box.expandByObject(gltf.scene);

const width = box.max.x - box.min.x;
const height = box.max.y - box.min.y;
const depth = box.max.z - box.min.z;

console.log('模型大小:', {
    width: width,
    height: height,
    depth: depth
});

创建一个 Box3 包围盒,它的 min、max 分别表示最小最大的 x、y、z

用 expandByObject 来计算包裹某个 obj 的包围盒大小,这里我们把模型包裹进去。

那最大和最小坐标的差值就是模型大小。

image.png

如何验证呢?

我们创建一个 BoxGeometry 看一下:

image.png

const geometry = new THREE.BoxGeometry(width, height, depth);
const material = new THREE.MeshBasicMaterial({
    color: 'orange'
});
const mesh2 = new THREE.Mesh(geometry, material);
mesh.add(mesh2);

2025-05-31 22.31.33.gif

可以看到,大小是一样的。

上移一下,加点透明度:

image.png

const geometry = new THREE.BoxGeometry(width, height, depth);
const material = new THREE.MeshBasicMaterial({
    color: 'orange',
    transparent: true,
    opacity: 0.5
});
const mesh2 = new THREE.Mesh(geometry, material);
mesh.add(mesh2);
mesh2.position.y = height / 2;

2025-05-31 22.33.10.gif

然后在模型中间位置加一个圆环。

RingGeometry

image.png

根据计算出的包围盒信息加一圈圆环:

image.png

const ringGeometry = new THREE.RingGeometry( width / 2, width /2 + 10);
const ringMaterial = new THREE.MeshBasicMaterial( { 
    color: 'green',
    side: THREE.DoubleSide 
});
const ring = new THREE.Mesh(ringGeometry, ringMaterial);
mesh.add(ring);
ring.position.y = height / 2;
ring.rotateX(Math.PI / 2);

指定内圆半径、外圆半径,移动到模型中间的高度

2025-05-31 22.38.46.gif

可以看到,正好在中间,计算的是对的。

我们换成鸭子的模型试一下:

从这里下载,放到 public 目录下:

https://github.com/QuarkGluonPlasma/threejs-course-code/blob/main/box3-test/public/duck.glb

image.png

2025-05-31 22.41.33.gif

也是对的。

案例代码上传了小册仓库

总结

这节我们学习了包围盒的计算。

计算一个对象的大小,就是计算它的最小包围盒的大小,用 Box3 的 api,它的 expandByObject 方法传入要包围的对象就好了,min、max 就是最小最大的 x、y、z,可以计算出包围盒的长宽高。

还可以通过 BoxHelper 来把目标对象的包围盒可视化出来。

当你想计算一个对象的大小,比如模型的大小,就可以通过 Box3 来做。

评论