什么是盒子辅助器
BoxHelper
是 Three.js 的"3D 物体尺子",能自动给任意物体包上一个会变形的彩色线框盒子,帮你直观看到物体的位置、大小和旋转状态。
假设我们在玩《我的世界》:
- 我们想知道一棵树的占地范围 → BoxHelper 会给树套上发光绿线框(显示树干到树叶的最大边界)。
- 我们旋转树时 → 线框会跟着旋转变形(实时更新包围盒)。
- 我们造了巨型城堡 → BoxHelper 立刻生成包裹整个城堡的大盒子(无论多复杂模型都能处理)。
一个基础的使用案例
效果如下

js
const mesh = new THREE.Mesh(
new THREE.SphereGeometry( 1, 32, 32 ),
new THREE.MeshNormalMaterial()
)
mesh.add(new THREE.BoxHelper(mesh, 'deepskyblue'))
scene.add(mesh);
移动并添加子元素案例
先看效果

想象一下,如果我们将盒子辅助器直接添加到场景中会怎么样
js
const mesh1 = new THREE.Mesh(
new THREE.SphereGeometry( 0.5, 32, 32 ),
new THREE.MeshNormalMaterial()
)
mesh1.add(new THREE.BoxHelper(mesh1, 'deepskyblue'))
scene.add(mesh1)
scene.add(new THREE.BoxHelper(mesh1, 'deeppink'));
这里scene.add(mesh1)
实现的效果和上一个案例无异,但是当我们直接将mesh1的盒子辅助器添加到场景中则会如下图

添加到场景中的辅助器和模型的辅助器重叠了, 通过改变mesh1
在Z轴上的位置就能发现
js
let frame = 0, lt = new Date();
const maxFrame = 90, fps = 60;
function animation() {
const now = new Date(),
secs = (now - lt) / 1000,
per = frame / maxFrame,
bias = 1 - Math.abs(0.5 - per) / 0.5; // 0 ~ 1
requestAnimationFrame( animation );
if (secs > 1 / fps) {
mesh1.position.z = 4* bias;
mesh1.rotation.y = Math.PI *2 * per;
renderer.render( scene, camera );
frame += fps * secs;
frame %= maxFrame;
lt = now;
}
}
实时更新案例
前面提及过辅助器的线框能够做到实时变形,动手尝试下

js
const mesh1 = new THREE.Mesh(
new THREE.BoxGeometry( 1, 2, 3 ),
new THREE.MeshNormalMaterial({ transparent: true, opacity: 0.5 })
)
scene.add( mesh1 );
const helper1 = new THREE.BoxHelper( mesh1, 'deepskyblue' );
helper1.material.vertexColors = true; // 顶点颜色
const data_color = [];
const att_pos = helper1.geometry.getAttribute( 'position' ); // 获取顶点位置
let i = 0;
while( i<att_pos.count ) {
const a_vertex = i / att_pos.count; // 顶点位置百分比
data_color.push(1*a_vertex, 1, 1-a_vertex, 0.8*0.9*Math.random() ); // 颜色
i++;
}
helper1.geometry.setAttribute('color', new THREE.BufferAttribute( new Float32Array( data_color ), 4));
scene.add(helper1);
const grid = new THREE.GridHelper( 10, 10, 'deeppink' );
scene.add( grid );
orbitControls.update();
let frame = 0, lt = new Date();
const maxFrame = 900, fps = 20;
function animation() {
const now = new Date(),
secs = (now - lt) / 1000,
a_frame = frame / maxFrame,
a_z = Math.sin( Math.PI * (a_frame * 2 % 1) ), // 0 ~ 1
a_x = Math.sin( Math.PI * (a_frame * 8 % 1) );
requestAnimationFrame( animation );
if( secs > 1 / fps ) {
mesh1.position.x = -2 + 4 * a_x;
mesh1.position.z = -2 + 4 * a_z;
mesh1.rotation.x = a_x * Math.PI * 2;
mesh1.rotation.y = a_z * Math.PI * 2;
helper1.update(); // 更新辅助对象
renderer.render( scene, camera );
frame += fps * secs;
frame %= maxFrame;
lt = now;
}
}
animation();
这个没有太多可说的,给辅助器添加了顶点着色,在更新函数中要注意调用下update
方法更新辅助器
在组合中使用辅助器
先看效果

创建一个组,将球体和立方体放置其中,然后为这个组添加盒子辅助器
js
const group = new THREE.Group();
group.add(new THREE.Mesh(
new THREE.SphereGeometry(0.5, 32, 32),
new THREE.MeshNormalMaterial()
))
group.add(new THREE.Mesh(
new THREE.BoxGeometry(0.5, 0.5, 0.5),
new THREE.MeshNormalMaterial()
))
group.children[1].position.x = 2;
group.add(new THREE.BoxHelper(group, 'deepskyblue'))
group.rotation.set(Math.PI/4, Math.PI/4, 0)
scene.add(group);
模型缩放对辅助器的影响
效果如下

左侧的模型不断地修改缩放值,我们可以看到辅助器在同步更新改变
js
const mesh1 = new THREE.Mesh(
new THREE.SphereGeometry(0.5, 30, 30),
new THREE.MeshNormalMaterial()
);
mesh1.add(new THREE.BoxHelper(mesh1, 'deepskyblue'));
scene.add(mesh1);
const helper = new THREE.BoxHelper(mesh1,'deeppink');
scene.add(helper);
mesh1.position.set(-2, 0, 0);
orbitControls.update();
let frame = 0, lt = new Date();
const maxFrame = 90, fps = 30;
function animation() {
const now = new Date(),
secs = (now - lt) / 1000,
per = frame / maxFrame,
bias = 1 - Math.abs(0.5 - per) / 0.5;
requestAnimationFrame( animation );
if (secs > 1 / fps) {
const v = new THREE.Vector3(0.5, 1, 0.5 + 2 * bias);
mesh1.scale.copy(v);
mesh1.rotation.y = Math.PI * per;
renderer.render(scene, camera);
frame += fps * secs;
frame %= maxFrame;
lt = now;
}
}
animation();
这里需要注意的是与之前的更新案例不同,更新函数中不用更新辅助器对象,这是因为我们的辅助器是直接添加在模型上,而不是直接添加到场景中,当前案例右侧的盒子辅助器则是直接添加到场景中的,尝试在更新函数中调用update
你将会看到如下结果

给随机分布的点模型添加盒子辅助器
这是一个查看随机分布的粒子边界的案例,很简单,先看效果

js
const point_count = 300;
const point_array = [];
let i = 0;
while(i < point_count){
const x = -2 + 4 * Math.random();
const y = Math.random();
const z = -2 + i / point_count * 4;
point_array.push(x, y, z);
i += 1;
}
const geo = new THREE.BufferGeometry();
geo.setAttribute('position', new THREE.BufferAttribute( new Float32Array(point_array), 3 ) );
const points = new THREE.Points( geo, new THREE.PointsMaterial({ size: 0.15, color: 'deepskyblue' }));
scene.add( points );
points.add( new THREE.BoxHelper( points, 'deepskyblue' ) );