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' ) );
相关推荐
m0_4711996312 小时前
【小程序】订单数据缓存 以及针对海量库存数据的 懒加载+数据分片 的具体实现方式
前端·vue.js·小程序
编程大师哥12 小时前
Java web
java·开发语言·前端
A小码哥12 小时前
Vibe Coding 提示词优化的四个实战策略
前端
Murrays12 小时前
【React】01 初识 React
前端·javascript·react.js
大喜xi12 小时前
ReactNative 使用百分比宽度时,aspectRatio 在某些情况下无法正确推断出高度,导致图片高度为 0,从而无法显示
前端
helloCat12 小时前
你的前端代码应该怎么写
前端·javascript·架构
电商API_1800790524712 小时前
大麦网API实战指南:关键字搜索与详情数据获取全解析
java·大数据·前端·人工智能·spring·网络爬虫
康一夏12 小时前
CSS盒模型(Box Model) 原理
前端·css
web前端12312 小时前
React Hooks 介绍与实践要点
前端·react.js
我是小疯子6612 小时前
JavaScriptWebAPI核心操作全解析
前端