一、Box3 基础概念
1. 什么是 Box3?
Box3 是 Three.js 中用于表示三维空间中轴对齐边界框(Axis-Aligned Bounding Box, AABB)的类。它由最小点(min)和最大点(max)两个三维向量定义,形成一个与坐标轴对齐的立方体空间。
2. 核心作用
- 碰撞检测:快速判断物体是否相交
- 视锥体剔除:优化渲染性能,只渲染可见物体
- 空间分区:在八叉树等数据结构中作为基本单元
- 计算包围盒:获取物体的空间范围
二、Box3 基本操作
1. 创建 Box3 对象
import * as THREE from 'three';
// 方法一:创建空的 Box3,后续再设置范围
const box1 = new THREE.Box3();
// 方法二:通过最小点和最大点直接定义
const min = new THREE.Vector3(0, 0, 0);
const max = new THREE.Vector3(10, 10, 10);
const box2 = new THREE.Box3(min, max);
// 方法三:从一组点计算包围盒
const points = [
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(5, 10, 5),
new THREE.Vector3(-5, 5, 5)
];
const box3 = new THREE.Box3().setFromPoints(points);
2. 常用属性和方法
属性
- min:边界框的最小点坐标(Vector3)
- max:边界框的最大点坐标(Vector3)
基础方法
// 1. 设置包围盒范围
box.set(min, max);
// 2. 从点集合计算包围盒
box.setFromPoints(points);
// 3. 从对象计算包围盒(需要先计算几何体的边界)
mesh.geometry.computeBoundingBox();
box.setFromObject(mesh);
// 4. 扩展包围盒以包含某个点
box.expandByPoint(new THREE.Vector3(20, 20, 20));
// 5. 扩展包围盒大小
box.expandByScalar(5); // 各方向扩展5个单位
// 6. 收缩包围盒
box.expandByScalar(-2); // 各方向收缩2个单位(注意不要导致min > max)
三、Box3 高级应用
1. 碰撞检测
// 检测两个 Box3 是否相交
const boxA = new THREE.Box3(
new THREE.Vector3(0, 0, 0),
new THREE.Vector3(10, 10, 10)
);
const boxB = new THREE.Box3(
new THREE.Vector3(5, 5, 5),
new THREE.Vector3(15, 15, 15)
);
if (boxA.intersectsBox(boxB)) {
console.log("BoxA 与 BoxB 相交");
}
// 检测点是否在 Box3 内
const point = new THREE.Vector3(8, 8, 8);
if (boxA.containsPoint(point)) {
console.log("点在 BoxA 内部");
}
2. 视锥体剔除优化
// 创建相机视锥体
const frustum = new THREE.Frustum();
const cameraMatrixWorldInverse = new THREE.Matrix4();
// 每一帧更新视锥体
function update() {
// 更新相机矩阵
camera.updateMatrixWorld();
cameraMatrixWorldInverse.copy(camera.matrixWorld).invert();
// 从投影矩阵和相机矩阵计算视锥体
frustum.setFromProjectionMatrix(
new THREE.Matrix4().multiplyMatrices(
camera.projectionMatrix,
cameraMatrixWorldInverse
)
);
// 检查物体是否在视锥体内
if (frustum.intersectsBox(mesh.geometry.boundingBox)) {
// 物体在视锥体内,渲染
renderer.render(scene, camera);
} else {
// 物体不在视锥体内,跳过渲染
console.log("物体在视锥体外部,跳过渲染");
}
}
3. 空间查询与分区
// 查找与指定 Box3 相交的所有物体
function findIntersectingObjects(box, objects) {
const result = [];
objects.forEach(object => {
// 确保几何体有边界框
if (object.geometry && !object.geometry.boundingBox) {
object.geometry.computeBoundingBox();
}
// 获取物体世界坐标系下的边界框
const objectBox = object.geometry.boundingBox.clone();
objectBox.applyMatrix4(object.matrixWorld);
// 检查是否相交
if (box.intersectsBox(objectBox)) {
result.push(object);
}
});
return result;
}
四、性能优化技巧
1. 缓存边界框
对于静态物体,只需计算一次边界框:
// 初始化时计算一次
mesh.geometry.computeBoundingBox();
const staticBox = mesh.geometry.boundingBox.clone();
// 使用克隆的边界框,避免重复计算
function checkCollision() {
if (staticBox.intersectsBox(anotherBox)) {
// 处理碰撞
}
}
2. 使用层级边界框
对于复杂场景,建立层级边界框结构:
// 为每个组计算边界框
const group = new THREE.Group();
group.add(mesh1, mesh2, mesh3);
// 计算子物体边界框的并集
const groupBox = new THREE.Box3();
group.children.forEach(child => {
if (child.geometry) {
child.geometry.computeBoundingBox();
const childBox = child.geometry.boundingBox.clone();
childBox.applyMatrix4(child.matrixWorld);
groupBox.union(childBox);
}
});
五、常见问题与解决方案
1. 边界框计算不准确
- 原因:几何体未计算边界框或变换矩阵未更新
- 解决方案:
// 确保几何体计算了边界框
mesh.geometry.computeBoundingBox();
// 如果物体有变换,确保矩阵已更新
mesh.updateMatrixWorld();
// 获取世界坐标系下的边界框
const worldBox = mesh.geometry.boundingBox.clone();
worldBox.applyMatrix4(mesh.matrixWorld);
2. 动态物体边界框更新
- 解决方案:
// 每帧更新动态物体的边界框
function update() {
// 更新物体变换
mesh.position.x += 0.1;
mesh.updateMatrixWorld();
// 更新边界框
mesh.geometry.computeBoundingBox();
const worldBox = mesh.geometry.boundingBox.clone();
worldBox.applyMatrix4(mesh.matrixWorld);
// 进行碰撞检测等操作
checkCollisions(worldBox);
}
六、实践案例:自定义物理引擎碰撞检测
下面是一个使用 Box3 实现简单碰撞检测的完整示例:
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
// 场景初始化
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 控制器
const controls = new OrbitControls(camera, renderer.domElement);
camera.position.z = 20;
// 创建两个立方体
const geometry1 = new THREE.BoxGeometry(5, 5, 5);
const material1 = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube1 = new THREE.Mesh(geometry1, material1);
cube1.position.x = -10;
scene.add(cube1);
const geometry2 = new THREE.BoxGeometry(5, 5, 5);
const material2 = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const cube2 = new THREE.Mesh(geometry2, material2);
cube2.position.x = 10;
scene.add(cube2);
// 为两个立方体创建 Box3 对象
const box1 = new THREE.Box3();
const box2 = new THREE.Box3();
// 动画循环
let direction1 = 0.1;
let direction2 = -0.1;
function animate() {
requestAnimationFrame(animate);
// 更新立方体位置
cube1.position.x += direction1;
cube2.position.x += direction2;
// 边界检查
if (cube1.position.x > 10 || cube1.position.x < -10) {
direction1 = -direction1;
}
if (cube2.position.x > 10 || cube2.position.x < -10) {
direction2 = -direction2;
}
// 更新矩阵
cube1.updateMatrixWorld();
cube2.updateMatrixWorld();
// 更新边界框
box1.setFromObject(cube1);
box2.setFromObject(cube2);
// 检测碰撞
if (box1.intersectsBox(box2)) {
material1.color.set(0xffff00);
material2.color.set(0xffff00);
} else {
material1.color.set(0x00ff00);
material2.color.set(0xff0000);
}
renderer.render(scene, camera);
}
animate();
这个示例展示了如何使用 Box3 实现两个立方体的碰撞检测,并通过颜色变化直观地显示碰撞状态。
通过掌握 Box3 的这些知识,你可以在 Three.js 项目中实现高效的碰撞检测、视锥体剔除和空间查询等功能,大大提升场景性能和交互体验。