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' ) );
相关推荐
Xf3n1an1 小时前
html语法
前端·html
张拭心1 小时前
亚马逊 AI IDE Kiro “狙击”Cursor?实测心得
前端·ai编程
烛阴1 小时前
为什么你的Python项目总是混乱?层级包构建全解析
前端·python
@大迁世界2 小时前
React 及其生态新闻 — 2025年6月
前端·javascript·react.js·前端框架·ecmascript
红尘散仙2 小时前
Rust 终端 UI 开发新玩法:用 Ratatui Kit 轻松打造高颜值 CLI
前端·后端·rust
新酱爱学习3 小时前
前端海报生成的几种方式:从 Canvas 到 Skyline
前端·javascript·微信小程序
袁煦丞3 小时前
把纸堆变数据流!Paperless-ngx让文件管理像打游戏一样爽:cpolar内网穿透实验室第539个成功挑战
前端·程序员·远程工作
慧慧吖@3 小时前
关于两种网络攻击方式XSS和CSRF
前端·xss·csrf
徐小夕3 小时前
失业半年,写了一款多维表格编辑器pxcharts
前端·react.js·架构
ai小鬼头3 小时前
如何重装旁路由系统并优化AIStarter部署:一步步教程
java·css·github