expandByObject扩张包围盒

expandByObject与setFromObject的区别:

image

  • 作用:把当前 Box3 扩大,直到能包住传入 object(以及它所有子节点)的世界空间范围。

  • 签名box.expandByObject(object: THREE.Object3D): this

  • 特点:是累加(并集),不是覆盖。多次调用会逐步扩大 box

  • 返回:仍是这个 box 本身,方便链式调用。

对比:setFromObject(object)直接覆盖,把 box 设置为该对象的范围;而 expandByObject 是在原有 box 基础上扩张

使用要点(易踩坑)

  1. 先更新矩阵object.updateMatrixWorld(true),否则拿到旧的包围盒。

  2. 重算 vs 扩张expandByObject 只会变大不会变小;若场景变化需要“收缩”,先 box.makeEmpty() 再重新 expand。

  3. 几何的 boundingBox:Three 会在需要时计算,但自定义/动态几何建议手动:geometry.computeBoundingBox()

  4. 可见性/图层:它不关心 visiblelayers,纯几何范围计算。

  5. InstancedMesh:实例很多时,expandByObject 可能成本较高;确保实例矩阵已更新(instanceMatrix.needsUpdate=false 之后)。

 

常用示例

1) 计算整个场景(或若干组)的全局高度

// 1) 创建空盒子
const globalBox = new THREE.Box3().makeEmpty();

// 2) 累加若干 group 的范围(或直接传 scene)
for (const group of [...this.initModelGroup, ...this.pglModelGroup]) {
  group.updateMatrixWorld(true);
  globalBox.expandByObject(group);
}

// 3) 取最高点 + 安全余量
this.defaultHeight = globalBox.max.y + 1000;

2) 向下发射射线(基于全局最高点)

const origin = new THREE.Vector3(x, this.defaultHeight, z);
const dir = new THREE.Vector3(0, -1, 0); // 向下
raycaster.set(origin, dir);

// 注意传入 { recursive: true } 或第二参数 true
const targets = [...this.initModelGroup, ...this.pglModelGroup];
const hits = raycaster.intersectObjects(targets, true);

3) 只算一个组的范围(比如一栋楼)

const box = new THREE.Box3().makeEmpty();
buildingGroup.updateMatrixWorld(true);
box.expandByObject(buildingGroup);

const size = new THREE.Vector3();
box.getSize(size);      // 楼的宽高深
const center = new THREE.Vector3();
box.getCenter(center);  // 楼的中心点(世界坐标)

4) 相机自适应取景(frame object)

function frameBox(camera, controls, box, fitOffset = 1.2) {
  const size = new THREE.Vector3(); box.getSize(size);
  const center = new THREE.Vector3(); box.getCenter(center);

  const maxSize = Math.max(size.x, size.y, size.z);
  const fov = THREE.MathUtils.degToRad(camera.fov);
  const dist = (maxSize / 2) / Math.tan(fov / 2) * fitOffset;

  const dir = new THREE.Vector3()
    .subVectors(camera.position, controls.target)
    .normalize();

  camera.position.copy(center).addScaledVector(dir, dist);
  camera.near = dist / 100;
  camera.far  = dist * 100;
  camera.updateProjectionMatrix();

  controls.target.copy(center);
  controls.update();
}

// 用法:
const box = new THREE.Box3().makeEmpty();
scene.updateMatrixWorld(true);
box.expandByObject(scene);
frameBox(camera, controls, box);

5) 动态场景的“重算”套路

// 每次需要精确更新全局盒子时:
globalBox.makeEmpty();
for (const group of groups) {
  group.updateMatrixWorld(true);
  globalBox.expandByObject(group);
}

什么时候选它

  • 你要的是多个对象的总体边界(全场景/若干组的包围盒)——用 expandByObject

  • 你只关心单个对象当前范围——用 setFromObject 更直接。

posted @ 2025-08-26 16:34  SimoonJia  阅读(8)  评论(0)    收藏  举报