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' ) );
相关推荐
电商API_1800790524714 分钟前
电商商品价格监控工具淘宝京东商品价格抓取API项目实操分享
java·大数据·开发语言·前端·数据挖掘
sxmas25 分钟前
用状态机与DAG建模四阶段业务流程:以MIND模型为例
前端
梦醒沉醉1 小时前
3、CSS入门——CSS选择器
css
倔强的石头1061 小时前
【Linux指南】动静态库系列(七):符号表与重定位:链接器如何把函数调用接起来
linux·前端·javascript
a1117762 小时前
3D 脑图谱 / Bilingual 3D Brain Atlas 项目
前端
T1mzhou3 小时前
ARM64 Linux 6.10内核启动流程2-rootfs 怎么挂上
linux·服务器·前端
小小尚@3 小时前
WebGIS/ECharts 地图开发|MapLand 在线行政区划边界一键下载 GeoJSON 工具
前端·javascript·echarts
默_笙4 小时前
🚍 一条 Todo 的奇幻漂流:TypeScript 全栈类型安全的"护照检查"
前端·javascript
计算机魔术师5 小时前
NVIDIA 以 129.3 亿美元收购 Hugging Face
前端
平头哥技术团队5 小时前
Day 01 | 用 HTML 写第一个网页:双击就能在浏览器打开
前端