Three.js开发必备:层级模型详解

Three.js开发必备:层级模型详解

Group层级模型树结构

基础知识

创建两个网格模型mesh1,mesh2,通过THREE.Group类创建一个组对象group,然后通过add方法把网格模型mesh1,mesh2作为设置为组对象group的子对象,然后通过执行scene.add(group)把组对象group作为场景对象的scene的子对象,也就是说场景对象scenegroup的父对象,groupmsh1,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的子对象,也就是说场景对象scenegroup的父对象,groupmesh1,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一样,可以通过一个方法查找树结构父元素和某个后代对象,对于普通前端而言可以通过nameid方式查找一个或多个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;

完结

相关推荐
Carlos_sam1 小时前
OpenLayers:封装一个自定义罗盘控件
前端·javascript
前端南玖1 小时前
深入Vue3响应式:手写实现reactive与ref
前端·javascript·vue.js
wordbaby1 小时前
React Router 双重加载器机制:服务端 loader 与客户端 clientLoader 完整解析
前端·react.js
itslife1 小时前
Fiber 架构
前端·react.js
3Katrina1 小时前
妈妈再也不用担心我的课设了---Vibe Coding帮你实现期末课设!
前端·后端·设计
hubber1 小时前
一次 SPA 架构下的性能优化实践
前端
可乐只喝可乐2 小时前
从0到1构建一个Agent智能体
前端·typescript·agent
Muxxi2 小时前
shopify模板开发
前端
Yueyanc2 小时前
LobeHub桌面应用的IPC通信方案解析
前端·javascript
我是若尘3 小时前
利用资源提示关键词优化网页加载速度
前端