本章主要学习知识点
- 了解three.js中的组对象group,并练习一些基本操作
- 练习遍历模型树结构、查询模型节点
- 了解本地坐标与世界坐标概念
- 练习修改模型相对于局部坐标原点的位置
- 练习移除隐藏模型对象操作
模型建组group
在Three.js 中,模型建组 是通过THREE.Group
类实现的,它可以将多个模型对象组织成一个逻辑整体,便于统一管理和操作,想象一下如果场景中的模型对象都散乱放置,那该有多恐怖~
树形结构
Three.js 场景采用树状结构,Group
继承自Object3D
,可包含任意子对象(Mesh、Light、Camera等)
js
const group = new THREE.Group();
group.add(mesh1, mesh2, light);
scene.add(group);
坐标继承
组内所有子对象使用组坐标系,当组发生变换时,子对象会跟随整体移动/旋转/缩放,通俗点说就是老大一动小弟全都要动
js
group.position.set(30, 0, 0); // 所有子对象整体右移30单位
实际操作下 ,创建2个立方体,并设置cube2沿X轴向右移动5个单位(为了区别,将cube进行自旋转)
js
cube = new THREE.Mesh( geometry, material );
cube2 = new THREE.Mesh( geometry, material)
cube2.position.x = 5;

接下来新建一个组对象,将cube和cube2放入其中编组,同时将gounp组对象沿Z轴平移5个单位,看看会发生什么
js
// 创建一个THREE.Group对象
const group = new THREE.Group();
group.add(cube,cube2)
group.translateZ(5)
scene.add(group)

从图中可发现,cube和cube2都被沿Z轴平移了5个单位,只就是老大一动小弟全都要动
尝试打印组对象下的children属性,看看有什么
js
console.log(group.children);

gounp
下包含2个mesh
,因为我们为cube和cube2两个mesh
进行了编组,但到目前为止,我们并不知道这个组叫什么,让我们来给组起个名字
js
//给组对象命名
group.name = 'cubeMeshGroup';
console.log(group);

组与组之间嵌套,形成庞大的树形结构
假设我们正在构建一个汽车模型时,就需要对每个部件进行编组,再将这些组再进行组合,最终形成一个完整的车辆, 如下是一个简单的示例
js
const car = new THREE.Group();
const chassis = new THREE.Group(); // 底盘组
const wheels = new THREE.Group(); // 车轮组
// 构建底盘
chassis.add(bodyMesh, seatMesh, steeringWheel);
// 构建车轮(子组嵌套)
const wheelFL = createWheel();
const wheelFR = createWheel();
wheels.add(wheelFL, wheelFR);
car.add(chassis, wheels);
遍历树结构
在开始遍历之前先要来了解下,树结构的基本组成
树结构的基本组成
树根
:Scene
场景对象是整个树的根节点树枝
:Group
组对象或复杂模型,可包含子对象树叶
: 单个模型、光源、相机等
好,我们先来搭建一个场景
js
const group = new THREE.Group();
group.name = '高楼';
for(let i=0; i<6; i++) {
// 创建几何体
const geometry = new THREE.BoxGeometry(1,1,1);
// 兰伯特材质
const material = new THREE.MeshLambertMaterial({color: 'deepskyblue'})
const mesh = new THREE.Mesh(geometry, material)
mesh.position.x = i*2;
group.add(mesh)
mesh.name = i+1+'号楼'; //给每个mesh对象命名
}
group.position.y = 6;
const group2 = new THREE.Group();
group2.name = '洋房'
for(let i=0; i<6; i++) {
// 创建几何体
const geometry = new THREE.BoxGeometry(1,1,1);
// 兰伯特材质
const material = new THREE.MeshLambertMaterial({color: 'deeppink'})
const mesh = new THREE.Mesh(geometry, material)
mesh.position.x = i*2;
group2.add(mesh)
mesh.name = i+1+'号楼'; //给每个mesh对象命名
}
const model = new THREE.Group();
model.name = '小区房子'
model.add( group, group2 )
scene.add(model)

这里我创建了两个组对象,分别为高楼(蓝色立方体)、洋房(红色立方体),每个组对象内部包含6个命名立方体,将这两个组对象添加到了名为小区房子的组对象中,接下来我们就可以使用three.js中内置的遍历方法进行遍历,注意我在遍历到子元素为mesh时将他们的材质颜色改为绿色
js
model.traverse(obj=> {
if(obj.isMesh) {
obj.material.color.set('green')
}
})

traverse
是一种深度优先遍历,很适合用于批量操作
深度优先遍历和广度优先遍历策略
深度优先遍历(家族长辈优先)
- 先访问父节点,再递归访问所有子节点
js
function traverse(obj) {
console.log(obj.name); // 处理当前节点
obj.children.forEach(child => traverse(child)); // 递归子节点
}
traverse(scene); // 从根节点开始遍历
适用于批量修改材质、统计对象数量等
广度优先遍历(家族同辈优先)
- 逐层访问,使用队列结构实现
js
function traverseBFS(root) {
const queue = [root]; // 初始化队列
while (queue.length > 0) {
const obj = queue.shift(); // 取出队首元素
console.log(obj.name);
queue.push(...obj.children); // 子节点入队尾
}
}
适用于快速查找最近物体、层级分析
查找某个具体的模型
通过getObjectByName
可以获取对应的模型
js
const findMesh = model.getObjectByName('1号楼')
findMesh.material.color.set('red')
找到模型后,将模型的材质改为红色,如下图所示

哦,太棒了,又学到了
本地坐标与世界坐标
我又想打比方了, 假设一个快递站有3层楼,每层楼有5个柜子
本地坐标
: 每个柜子相对于所在楼层的坐标,比如2楼第3个柜子的本地坐标是(0,0,3)
(以2楼入口为原点)世界坐标
: 整个快递站的全局坐标, 比如2楼入口的世界坐标是(0,5,0)
,那么该柜子的世界坐标就是(0,5,3)
在Three.js世界中,mesh.position
获取的是本地坐标,mesh.getWorldPosition()
获取的是世界坐标
两种坐标系之间的本质差异
特性 | 本地坐标 | 世界坐标 |
---|---|---|
参照物 | 父容器(如Group) | 场景原点(Scene原点) |
获取方式 | object.position |
object.getWorldPosition() |
变化条件 | 父物体移动时自动保持相对关系 | 任何物体移动都会改变 |
应用场景 | 部件组装、相对位置调整 | 碰撞检测、全局定位 |
获取世界坐标和本地局部坐标
js
// 获取世界坐标
const worldPosition = new THREE.Vector3();
findMesh.getWorldPosition(worldPosition)
console.log(worldPosition);
// 获取本地局部坐标
const localPosition = findMesh.position;
console.log(localPosition);
获取模型的父级以及子级
js
// 获取父级
const parent = findMesh.parent
console.log(parent);
// 获取子级
const children = parent.children
console.log(children);
移除隐藏模型对象
移除模型(彻底删除)
通过 .remove()
方法将模型从场景中删除,释放内存空间。
js
//删除组对象下的cube2模型
group.remove(cube2)
注意:
- 模型被彻底删除,无法再显示。
- 如果模型有材质或几何体,需要手动调用
.dispose()
释放 GPU 内存。
隐藏模型(临时不可见)
通过 .visible
属性让模型暂时不可见,但仍存在于场景中。
js
// 隐藏cube模型
cube.visible = false;
注意:
- 模型仍在场景中,可以随时通过
model.visible = true
恢复显示 - 如果多个模型共享同一材质,设置材质
.visible
会隐藏所有关联模型
以上案例均可在案例中心查看体验
