Three.js-硬要自学系列35之专项学习盒子辅助器

什么是盒子辅助器

BoxHelper 是 Three.js 的"3D 物体尺子",能自动给任意物体包上一个会变形的彩色线框盒子,帮你直观看到物体的位置、大小和旋转状态。

假设我们在玩《我的世界》:

  1. 我们想知道一棵树的占地范围 → BoxHelper 会给树套上发光绿线框(显示树干到树叶的最大边界)。
  2. 我们旋转树时 → 线框会跟着旋转变形(实时更新包围盒)。
  3. 我们造了巨型城堡 → 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' ) );
相关推荐
鱼雀AIGC3 分钟前
如何仅用AI开发完整的小程序<6>—让AI对视觉效果进行升级
前端·人工智能·游戏·小程序·aigc·ai编程
duanyuehuan32 分钟前
Vue 组件定义方式的区别
前端·javascript·vue.js
veminhe36 分钟前
HTML5简介
前端·html·html5
洪洪呀36 分钟前
css上下滚动文字
前端·css
搏博1 小时前
基于Vue.js的图书管理系统前端界面设计
前端·javascript·vue.js·前端框架·数据可视化
掘金安东尼2 小时前
前端周刊第419期(2025年6月16日–6月22日)
前端·javascript·面试
bemyrunningdog2 小时前
AntDesignPro前后端权限按钮系统实现
前端
重阳微噪2 小时前
Data Config Admin - 优雅的管理配置文件
前端
Hilaku2 小时前
20MB 的字体文件太大了,我们把 Icon Font 压成了 10KB
前端·javascript·css
fs哆哆2 小时前
在VB.net中,文本插入的几个自定义函数
服务器·前端·javascript·html·.net