
Three.js开发必备:层级模型详解
Group层级模型树结构
基础知识
创建两个网格模型
mesh1
,mesh2
,通过THREE.Group
类创建一个组对象group
,然后通过add
方法把网格模型mesh1,mesh2
作为设置为组对象group
的子对象,然后通过执行scene.add(group)
把组对象group
作为场景对象的scene
的子对象,也就是说场景对象scene
是group
的父对象,group
是msh1,mesh2
的父对象,这样就构成了一个三层的层级结构,也可以通过group
自己创建新模型节点作为层级结构中的一层。
Group.add()方法
场景对象Scene
、组对象Group
的.add()
方法都是继承自他们的同类Object3D
,.add
方法可以单独插入一个对象,也可以同时插入多个子对象。
javascript
import * as THREE from 'three'
const geometry = new THREE.BoxGeometry(20,20,20);
const material = new THREE.MeshLambertMaterial({color:0x00ffff})
const mesh1 = new THREE.Mesh(geometry,material)
const mesh2 = new THREE.Mesh(geometry,material)
mesh2.translateX(30)
// 创建一个组对象
const group = new THREE.Group()
group.add(mesh1) // 网格模型mesh1作为group的子对象
group.add(mesh2)
export default group;

修改Group修改位置
javascript
// 创建一个组对象
const group = new THREE.Group()
group.add(mesh1) // 网格模型mesh1作为group的子对象
group.add(mesh2)
group.translateX(50)
group.translateY(50)
group.translateZ(50)
缩放:
javascript
group.scale.set(2,2,2)
得出:父对象旋转缩放属性改变的时候,子对象跟着改变。
Object3D表示模型对象节点
Three
学习过程中会看到很多Object3D
作为Group
来使用,在某种程度上两者化等号,只是Group
更加语义化,Object3D
本身就表示模型节点的意思。
javascript
import * as THREE from 'three'
const geometry = new THREE.BoxGeometry(20,20,20);
const material = new THREE.MeshLambertMaterial({color:0x00ffff})
const mesh1 = new THREE.Mesh(geometry,material)
const mesh2 = new THREE.Mesh(geometry,material)
const obj = new THREE.Object3D() // 作为mesh1和mehs2的父对象
mesh2.translateX(50)
obj.add(mesh1,mesh2)
export default obj;

递归遍历模型树结构、查询模型节点
模型命名
在层级模型中可以给一些模型对象通过.name
属性命名进行标记。
层级模型树结构案例
创建两个网格模型mesh1
,mesh2
,通过THREE.Group()
类创建一个组对象group
,然后通过add
方法把网格模型mesh1
,mesh2
作为设置组对象group
的子对象,然后通过执行scene.add(group)
方法把组对象group
作为场景对象的scene
的子对象,也就是说场景对象scene
是group
的父对象,group
是mesh1
,mesh2
的父对象,这样构成了三级结构。
javascript
import * as THREE from 'three'
// 批量创建多个长方体表示高层楼
const group1 = new THREE.Group() // 所有高层楼的父对象
group1.name = '高层'
for(let i=0;i<5;i++){
const gemotry = new THREE.BoxGeometry(20,60,10)
const material = new THREE.MeshLambertMaterial({
color:0x00ffff
})
const mesh = new THREE.Mesh(gemotry,material)
mesh.name = i+1+'号楼'
mesh.position.x = i*30;
group1.add(mesh)
}
group1.position.y = 30
const group2 = new THREE.Group() // 所有低层楼的父对象
group1.name = '洋房'
for(let i=0;i<5;i++){
const gemotry = new THREE.BoxGeometry(20,30,10)
const material = new THREE.MeshLambertMaterial({
color:0xff00ff
})
const mesh = new THREE.Mesh(gemotry,material)
mesh.name = i+6+'号楼'
mesh.position.x = i*30;
group2.add(mesh)
}
group2.position.z = 50
group2.position.y = 15
const model = new THREE.Group()
model.name = "小区房子"
model.add(group1,group2)
model.position.set(-50,0,-25)
export default model;

递归遍历修改属性
threeJS
层级模型就是一个树结构,可以通过递归遍历的算法去遍历three.js
一个模型对象包含的所有后代。
javascript
import * as THREE from 'three'
// 批量创建多个长方体表示高层楼
const group1 = new THREE.Group() // 所有高层楼的父对象
group1.name = '高层'
for(let i=0;i<5;i++){
const gemotry = new THREE.BoxGeometry(20,60,10)
const material = new THREE.MeshLambertMaterial({
color:0x00ffff
})
const mesh = new THREE.Mesh(gemotry,material)
mesh.name = i+1+'号楼'
mesh.position.x = i*30;
group1.add(mesh)
}
group1.position.y = 30
const group2 = new THREE.Group() // 所有低层楼的父对象
group1.name = '洋房'
for(let i=0;i<5;i++){
const gemotry = new THREE.BoxGeometry(20,30,10)
const material = new THREE.MeshLambertMaterial({
color:0xff00ff
})
const mesh = new THREE.Mesh(gemotry,material)
mesh.name = i+6+'号楼'
mesh.position.x = i*30;
group2.add(mesh)
}
group2.position.z = 50
group2.position.y = 15
const model = new THREE.Group()
model.name = "小区房子"
model.add(group1,group2)
model.position.set(-50,0,-25)
// 递归遍历所有模型节点
model.traverse(function(obj){
console.log("递归遍历",obj.name)
if(obj.isMesh){
console.log("obj.name",obj.name);
obj.material.color.set(0xffff00)
}
})
export default model;
通过递归遍历,打印所有网格模型的name值
查找某个具体的模型.getObjectByName()
threejs
和前端DOM
一样,可以通过一个方法查找树结构父元素和某个后代对象,对于普通前端而言可以通过name
或id
方式查找一个或多个dom
元素,threeJs
同样也可以通过一些方法查找一个模型树中的某个节点,更多的查找方法和方法的使用详细细节可以查看基类Object3D
。
javascript
const obj = model.getObjectByName('9号楼')
obj.material.color.set(0xff0f00)

本地(局部)坐标和世界坐标
本地(局部)坐标和世界坐标了解
改变子对象的positionh
,子对象在3D
空间中的坐标会发生改变,改变父对象的position
,子对象在3D
空间中的位置也会跟着变化,也就是说父对象.position
和子对象.position
叠加才是子对象的.position
,任何一个模型的本地坐标就是模型的.position
属性,一个模型的世界坐标说的是模型自身.position
和所有父对象.position
累加的坐标。
javascript
import * as THREE from 'three'
const gemotry = new THREE.BoxGeometry(20,20,20)
const material = new THREE.MeshLambertMaterial({
color:0x00ffff
})
const mesh = new THREE.Mesh(gemotry,material)
mesh.position.x = 50
const group = new THREE.Group();
group.add(mesh)
group.position.x = 50
export default group;

getWorldPosition()获取世界坐标
mesh.getWorldPosition()
读取一个模型的世界坐标,并把读取结果存储到Vector3
类型数组中。
javascript
const worldPostion = new THREE.Vector3();
mesh.getWorldPosition(worldPostion)
console.log("世界坐标",worldPostion)
console.log("本地坐标|局部坐标",mesh.position)

给子对象添加一个局部坐标
javascript
import * as THREE from 'three'
const gemotry = new THREE.BoxGeometry(20,20,20)
const material = new THREE.MeshLambertMaterial({
color:0x00ffff
})
const mesh = new THREE.Mesh(gemotry,material)
mesh.position.x = 50
const group = new THREE.Group();
group.add(mesh)
group.position.x = 50
const worldPostion = new THREE.Vector3();
mesh.getWorldPosition(worldPostion)
// 给子对象添加一个局部坐标辅助观察对象
const meshAxesHelper = new THREE.AxesHelper(50)
mesh.add(meshAxesHelper)
mesh.position.y = 50
console.log("世界坐标",worldPostion)
console.log("本地坐标|局部坐标",mesh.position)
export default group;

改变模型相对局部坐标原点位置
改变模型相对局部坐标原点位置
通过改变几何体顶点坐标,改变模型自身相对坐标原点的位置。
javascript
import * as THREE from 'three'
const gemotry = new THREE.BoxGeometry(50,50,50)
const material = new THREE.MeshLambertMaterial({
color:0x00ffff
})
const mesh = new THREE.Mesh(gemotry,material)
gemotry.translate(50/2,0,0)
mesh.rotateY(Math.PI/4)
export default mesh;

旋转测试
局部坐标相对模型发生改变,旋转轴自然也会发生变化,下面设置旋转动画观察几何体平移前后旋转动画差异。
不改变局部坐标系情况
javascript
import * as THREE from 'three'
const gemotry = new THREE.BoxGeometry(50,50,50)
const material = new THREE.MeshLambertMaterial({
color:0x00ffff
})
const mesh = new THREE.Mesh(gemotry,material)
export default mesh;
index.js
文件
javascript
// 渲染循环
function render() {
mesh.rotateY(0.01)
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();

改变局部坐标系后的情况
javascript
import * as THREE from 'three'
const gemotry = new THREE.BoxGeometry(50,50,50)
const material = new THREE.MeshLambertMaterial({
color:0x00ffff
})
const mesh = new THREE.Mesh(gemotry,material)
gemotry.translate(50/2,0,0)
export default mesh;
index.js
文件
javascript
// 渲染循环
function render() {
mesh.rotateY(0.01)
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();

移除对象.remove()
.add()
方法可以把模型或者光源添加到场景中,.remove()
方法和.add
方法相反,把子对象从父对象的.children()
属性中删除。、
Object3D的移除方法.remove()
场景对象.scene
,组对象Group
,网格模型对象Mesh
的.remove()
方法都是继承自他们公共的基类也就是父类Object3D
。 移除前
移除后
场景中使用.remove()
javascript
scene.remove(ambient) // 场景中移除环境光
scene.remove(mesh) // 场景中移除模型对象

一次性移除多个子对象
javascript
scene.remove(ambient,mesh) // 场景中移除环境光,场景中移除模型对象
和上面一样的移除效果
模型对象隐藏或显示
在数字孪生开发中,有时候需要隐藏一个模型或者一个模型处于吟唱状态,需要重新恢复显示。
模型属性.visible
模型对象的父类Object3D
封装了一个属性.visible
,通过该属性可以隐藏或者显示一个模型。
隐藏模型或者组对象
javascript
import * as THREE from 'three'
const gemotry = new THREE.BoxGeometry(50,50,50)
const material = new THREE.MeshLambertMaterial({
color:0x00ffff
})
const group = new THREE.Group();
const mesh = new THREE.Mesh(gemotry,material)
const mesh2 = new THREE.Mesh(gemotry,material)
mesh2.translateX(55)
group.add(mesh)
group.add(mesh2)
mesh.visible = false
export default group;
或者
javascript
group.visible = false // 隐藏一个包含多个模型的组对象group

显示模型或者组对象
javascript
import * as THREE from 'three'
const gemotry = new THREE.BoxGeometry(50,50,50)
const material = new THREE.MeshLambertMaterial({
color:0x00ffff
})
const group = new THREE.Group();
const mesh = new THREE.Mesh(gemotry,material)
const mesh2 = new THREE.Mesh(gemotry,material)
mesh2.translateX(55)
group.add(mesh)
group.add(mesh2)
group.visible = false // 隐藏一个包含多个模型的组对象group
group.visible = true // 使网格模型处于显示状态
export default group;

材质属性.visible
材质对象的父类Material
封装了一个.visible
属性,通过该属性可以控制是否隐藏该材质对应的模型对象,如果是多个模型引用了同一个材质,如果该材质的visible
设置为false
,意味着隐藏该材质的所有模型。共享材质隐藏一个所有的对应使用材质的模型都隐藏,可以克隆一个不受影响。
javascript
import * as THREE from 'three'
const gemotry = new THREE.BoxGeometry(50,50,50)
const material = new THREE.MeshLambertMaterial({
color:0x00ffff
})
const group = new THREE.Group();
const mesh = new THREE.Mesh(gemotry,material)
const mesh2 = new THREE.Mesh(gemotry,material)
mesh2.translateX(55)
mesh.material.visible = false
group.add(mesh)
group.add(mesh2)
export default group;
克隆
mesh
的材质,独立出来,并设置材质隐藏,就会发现隐藏的mesh
,发现通过clone
克隆的没有被隐藏。
javascript
import * as THREE from 'three'
const gemotry = new THREE.BoxGeometry(50,50,50)
const material = new THREE.MeshLambertMaterial({
color:0x00ffff
})
const group = new THREE.Group();
const mesh = new THREE.Mesh(gemotry,material)
const mesh2 = new THREE.Mesh(gemotry,material)
mesh2.translateX(55)
mesh2.material = material.clone()
mesh.material.visible = false
group.add(mesh)
group.add(mesh2)
export default group;
