group.remove(mesh1,mesh2);
Vector3与模型位置、缩放属性
Group层级模型(树结构)
创建了两个网格模型mesh1、mesh2,通过THREE.Group
类创建一个组对象group,然后通过add
方法把网格模型mesh1、mesh2作为设置为组对象group的子对象,然后在通过执行scene.add(group)
把组对象group作为场景对象的scene的子对象。也就是说场景对象是scene是group的父对象,group是mesh1、mesh2的父对象。
//创建两个网格模型mesh1、mesh2
const geometry = new THREE.BoxGeometry(20, 20, 20);
const material = new THREE.MeshLambertMaterial({color: 0x00ffff});
const group = new THREE.Group();
const mesh1 = new THREE.Mesh(geometry, material);
const mesh2 = new THREE.Mesh(geometry, material);
mesh2.translateX(25);
//把mesh1型插入到组group中,mesh1作为group的子对象
group.add(mesh1);
//把mesh2型插入到组group中,mesh2作为group的子对象
group.add(mesh2);
//把group插入到场景中作为场景子对象
scene.add(group);
查看子对象.children
hreejs场景对象Scene、组对象Group都有一个子对象属性.children
通过该属性可以访问父对象的子对象,子对象属性.children
的值是数组,所有子对象是数组的值,父对象执行.add()
方法的本质就是把参数中的子对象添加到自身的子对象属性.children
中。
console.log('查看group的子对象',group.children);
场景对象结构
场景对象Scene的子对象,除了组对象Group
之外,还可以看到环境光AmbientLight
、平行光DirectionalLight
、辅助坐标对象AxesHelper
。
console.log('查看Scene的子对象',scene.children);
场景对象对象scene构成的层级模型本身是一个树结构,场景对象层级模型的第一层,也就是树结构的根节点,一般来说网格模型Mesh、点模型Points、线模型Line是树结构的最外层叶子结点。构建层级模型的中间层一般都是通过Threejs的Group
类来完成,Group
类实例化的对象可以称为组对象。
.add()
方法总结
场景对象Scene
、组对象Group
的.add()
方法都是继承自它们共同的基类(父类)Object3D
group.add(mesh1);
group.add(mesh2);
// 或
group.add(mesh1,mesh2);
父对象旋转缩放平移变换,子对象跟着变化
格模型mesh1、mesh2作为设置为父对象group的子对象,如果父对象group进行旋转、缩放、平移变换,子对象同样跟着变换。
//沿着Y轴平移mesh1和mesh2的父对象,mesh1和mesh2跟着平移
group.translateY(100);
//父对象缩放,子对象跟着缩放
group.scale.set(4,4,4);
//父对象旋转,子对象跟着旋转
group.rotateY(Math.PI/6)
Object3D
表示模型对象节点
受threejs历史影响,你会在很多别的代码中看到Object3D
作为Group
来使用,某种程度上,你可把两者画等号,只是Group
更加语义化,Object3D本身就是表示模型节点的意思。
const mesh1 = new THREE.Mesh(geometry, material);
const mesh2 = new THREE.Mesh(geometry, material);
const obj = new THREE.Object3D();//作为mesh1和mesh2的父对象
obj.add(mesh1,mesh2);
mesh也能添加mesh子对象
threejs默认mesh也可以添加子对象,其实原因很简单,mesh和Group父类都是Object3D,本质上也可以认为都是Object3D。
//threejs默认mesh也可以添加子对象,mesh基类也是Object3D
mesh1.add(mesh2);
遍历模型树结构、查询模型节点
模型命名(.name
属性)
在层级模型中可以给一些模型对象通过.name
属性命名进行标记
const group = new THREE.Group();
group.name='小区';
const mesh = new THREE.Mesh(geometry, material);
mesh.name='五号楼';
树结构层级模型设置.name
属性
下面是通过代码创建了一个层级模型,一般实际开发的时候,会加载外部的模型,然后从模型对象通过节点的名称.name
查找某个子对象。
// 批量创建多个长方体表示高层楼
const group1 = new THREE.Group(); //所有高层楼的父对象
group1.name = "高层";
for (let i = 0; i < 5; i++) {
const geometry = new THREE.BoxGeometry(20, 60, 10);
const material = new THREE.MeshLambertMaterial({
color: 0x00ffff
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.x = i * 30; // 网格模型mesh沿着x轴方向阵列
group1.add(mesh); //添加到组对象group1
mesh.name = i + 1 + '号楼';
// console.log('mesh.name',mesh.name);
}
group1.position.y = 30;
const group2 = new THREE.Group();
group2.name = "洋房";
// 批量创建多个长方体表示洋房
for (let i = 0; i < 5; i++) {
const geometry = new THREE.BoxGeometry(20, 30, 10);
const material = new THREE.MeshLambertMaterial({
color: 0x00ffff
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.x = i * 30;
group2.add(mesh); //添加到组对象group2
mesh.name = i + 6 + '号楼';
}
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);
递归遍历方法.traverse()
Threejs层级模型就是一个树结构,可以通过递归遍历的算法去遍历Threejs一个模型对象包含的所有后代。
// 递归遍历model包含所有的模型节点
model.traverse(function(obj) {
console.log('所有模型节点的名称',obj.name);
// obj.isMesh:if判断模型对象obj是不是网格模型'Mesh'
if (obj.isMesh) {//判断条件也可以是obj.type === 'Mesh'
obj.material.color.set(0xffff00);
}
});
查找某个具体的模型.getObjectByName()
Threejs和前端DOM一样,可以通过一个方法查找树结构父元素的某个后代对象,对于普通前端而言可以通过name或id等方式查找一个或多个DOM元素,Threejs同样可以通过一些方法查找一个模型树中的某个节点。
// 返回名.name为"4号楼"对应的对象
const nameNode = scene.getObjectByName ("4号楼");
nameNode.material.color.set(0xff0000);
本地坐标和世界坐标
本地(局部)坐标和世界坐标
mesh的世界坐标就是mesh.position与group.position的累加
const mesh = new THREE.Mesh(geometry, material);
mesh.position.set(10, 20, 0);
const group = new THREE.Group();
group.add(mesh);
group.position.set(50, 30, 0);
改变子对象的
.position
,子对象在3D空间中的坐标会发生改变。改变父对象的
.position
,子对象在3D空间中的位置也会跟着变化,也就是说父对象.position
和子对象.position
叠加才是才是子对象的.position
。
任何一个模型的本地坐标(局部坐标)就是模型的.position
属性。
一个模型的世界坐标,说的是,模型自身.position
和所有父对象.position
累加的坐标。
.getWorldPosition()
获取世界坐标
mesh.getWorldPosition(Vector3)
读取一个模型的世界坐标,并把读取结果存储到参数Vector3
中。
// 声明一个三维向量用来表示某个坐标
const worldPosition = new THREE.Vector3();
// 获取mesh的世界坐标,你会发现mesh的世界坐标受到父对象group的.position影响
mesh.getWorldPosition(worldPosition);
console.log('世界坐标',worldPosition);
console.log('本地坐标',mesh.position);
给子对象添加一个局部坐标系
mesh.add(坐标系)
给mesh添加一个局部坐标系。
//可视化mesh的局部坐标系
const meshAxesHelper = new THREE.AxesHelper(50);
mesh.add(meshAxesHelper);
改变模型相对局部坐标原点位置
通过改变几何体顶点坐标,可以改变模型自身相对坐标原点的位置。
//长方体的几何中心默认与本地坐标原点重合
const geometry = new THREE.BoxGeometry(50, 50, 50);
// 平移几何体的顶点坐标,改变几何体自身相对局部坐标原点的位置
geometry.translate(50/2,0,0,);;
旋转测试
局部坐标相对模型发生改变,旋转轴自然也会发生变化。
// .rotateY()默认绕几何体中心旋转,经过上面几何体平移变化,你会发现.rotateY()是绕长方体面上一条线旋转
mesh.rotateY(Math.PI/3);
//
function render() {
model.rotateY(0.01);//旋转动画
requestAnimationFrame(render);
}
render();
移除对象.remove()
.remove()
方法和.add()
方法是相反的,是把子对象从父对象的.children()
属性中删除。
查看父类Object3D的移除方法.remove()
场景对象Scene
、组对象Group
、网格模型对象Mesh
的.remove()
方法都是继承自它们共同的基类(父类)Object3D
。
.remove()
方法使用
.add()
方法是给父对象添加一个子对象,.remove()
方法是删除父对象中的一个子对象。
// 删除父对象group的子对象网格模型mesh1
group.remove(mesh1);
scene.remove(ambient);//移除场景中环境光
scene.remove(model);//移除场景中模型对象
一次移除多个子对象
group.remove(mesh1,mesh2);
模型隐藏或显示
开发web3d项目,有时候需要临时隐藏一个模型,或者一个模型处于隐藏状态,需要重新恢复显示。
模型属性.visible
模型对象的父类Object3D
封装了一个属性.visible
,通过该属性可以隐藏或显示一个模型。
mesh.visible =false;// 隐藏一个网格模型,visible的默认值是true
group.visible =false;// 隐藏一个包含多个模型的组对象group
mesh.visible =true;// 使网格模型mesh处于显示状态
材质属性.visible
材质对象的父类Material
封装了一个.visible
属性,通过该属性可以控制是否隐藏该材质对应的模型对象。
// 隐藏网格模型mesh,visible的默认值是true
mesh.material.visible =false;
// 注意如果mesh2和mesh的.material属性指向同一个材质,mesh2也会跟着mesh隐藏