第二章
这一章主要创建3D模型,3D模型分为Three.js自带的基础模型以及外部导入模型
基础模型
基础的实体模型嘛,大概就是正方体,长方体,球等基础的固体形状,先说我们会议室的地板和墙面,其实就是基础的长方体,对于这种基础的实体模型,我们主要使用THREE.BoxBufferGeometry
对象来进行创建:
墙面
关于墙面,这次都是做的实体墙,所以比较简单,我大概分了两个步骤
创建长方体
export const addCubewall = function (
width,
height,
depth,
angle,
material,
x,
y,
z,
name
) {
let cubeGeometry = new THREE.BoxGeometry(width, height, depth);
let cube = new THREE.Mesh(cubeGeometry, material);
cube.position.x = x;
cube.position.y = y;
cube.position.z = z;
cube.rotation.y += angle * Math.PI; //-逆时针旋转,+顺时针
cube.name = name;
return cube;
};
我将这段代码抽出来当了公用方法,由此代码我们可以看出创建一个长方体需要的基本条件是宽度、高度、厚度、材质、方位,角度,具体属性可以去Three.js
官网查看。
而网络模型(Mesh)就是由几何类型(geometry)和材质(material )构成。
创建墙面
创建好长方体,其实创建墙面就很简单了,只需要更改属性值即可,直接上代码
data() {
// 基础尺寸
baseX: 600, // 基础长
baseY: 1, // 基础高
baseZ: 240, // 基础宽
}
createWell(long, width, height, x, y, z, name) {
let well = createMetry.addCubewall(
long,
height,
width,
0,
new THREE.MeshPhongMaterial({
color: 0xaaaaaa,
opacity: 0.5,
transparent: true,
}),
x,
y,
z,
name
);
this.scene.add(well);
},
// 初始化
init() {
this.createWell(
this.baseY,
this.baseZ,
100,
this.baseX / 2,
-10,
0,
"东墙"
);
this.createWell(
this.baseX,
this.baseY,
100,
0,
-10,
-this.baseZ / 2,
"南墙"
);
this.createWell(
this.baseY,
this.baseZ,
100,
-this.baseX / 2,
-10,
0,
"西墙"
);
this.createWell(
this.baseX,
this.baseY,
100,
0,
-10,
this.baseZ / 2,
"北墙"
);
上面是创建墙面的函数,然后在初始化函数中调用,根据四个墙面不同的方位,调整不同的属性,这一块相对来说较为简单,大家直接看代码即可
然后我大致讲一下创建一个几何体的过程:
1.首先需要创建一个几何类型,Three.js
对此提供的对象有很多,创建几何类型大致需要长度、宽度、厚度、角度、方位这些元素,当然像是圆柱或者不规则的几何体是由其他属性的,这里只是拿长方体举一个例子。
2. 接下里是创建材质,材质的类型也有很多,文中用的MeshPhongMaterial
是可以高亮的材质,其他类型大家可以去官网上看。
3. 有了几何类型和材质,就可以创建网络模型Mesh
,创建好之后,需要将网络模型添加到我们的3D场景Scene
当中去this.scene.add(Mesh)
地板
地板的创建和墙面一样,依旧是基础的实体模型——长方体,话不多说,直接上代码
createFloor() {
let that = this;
let geometry = new THREE.BoxBufferGeometry(
this.baseX,
this.baseY,
this.baseZ
);
// 请求图片资源
this.textureLoader = new THREE.TextureLoader();
this.textureLoader.load("/floor.png", function (texture) {
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.x = 10;
texture.repeat.y = 5;
let material = new THREE.MeshPhongMaterial({
map: texture,
});
that.ground = new THREE.Mesh(geometry, material);
that.ground.position.y = -60;
that.scene.add(that.ground);
});
},
过程和创建墙面一样,只不过我们在此基础上为地板贴了一张图片,这里需要用到THREE.TextureLoader
对象,有一点需要注意,如果是起了vue项目,那么load方法可以正常使用,如果是原生项目,记得起一个服务以便支持请求图片资源。
有了图片资源后,关键是将此值赋予地板材质中的map
属性,也就是颜色贴图。
这样我们就将地板和墙面创建出来了
外部加载模型文件
在我们做3D开发时,有时候一些复杂的模型,如果我们用基础的模型去拼接会十分消耗时间,这时候就可以采用外部加载一些已经成型的3D模型来达到我们的需求。Three.js
加载外部文件格式有stl
,obj
,gltf
,fbx
等,接下来我主要举例我用到的obj
与gltf
加载OBJ文件
首先需要导入插件OBJLoader
,import { OBJLoader } from "three/examples/jsm/loaders/OBJLoader"
还有插件MTLLoader
,import { MTLLoader } from "three/examples/jsm/loaders/MTLLoader"
使用OBJ文件时,我们主要是需要.mtl
和.obj
文件,其中.obj
文件是核心,基本上只要加载了.obj
文件就已经可以将模型显示出来,而.mtl
包含的就是模型的材质信息,比如模型的颜色以及贴图路径、反射方式等。
addObjItem() {
this.loader = new OBJLoader();
this.mat = new MTLLoader();
let that = this;
that.mat.load("/table/file.mtl", function (materials) {
materials.preload();
that.loader.setMaterials(materials);
that.loader.load(
"/table/file.obj",
function (object) {
object.position.set(0, 60, 0);
object.scale.set(0.05, 0.05, 0.05);
that.scene.add(object);
}
// function (xhr) {
// console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
// },
// function (error) {
// console.log(error);
// }
);
});
},
大致讲解一下加载过程,首先请求.mtl
文件资源,将获取的材质信息替换到当前创建对象的材质信息,然后请求.obj
文件资源,将获取的模型添加到3D场景中,这里有一点需要注意:我们加载的外部模型的大小相对于我们的场景来说可能过大或者过小,所以一定要根据此使用.scale.set()
方法去缩放或放大模型
注释的代码第一个时加载进度的回调函数,第二个是加载失败的回调函数,建议大家第一次使用OBJ文件时,将其加载的结果打印出来,看看其对象结构。
加载GLTF文件
首先需要导入插件GLTFLoader
,import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"
加载GLTF文件相对来说要比OBJ文件简单,因为我们只需要加载一个.gltf
文件,并且流程和加载OBJ文件时只加载.obj
文件一致,所以直接上代码
addGltfItem() {
let that = this;
let loader = new GLTFLoader();
loader.load("/flat/model.gltf", function (gltf) {
gltf.scene.position.set(0, 0, 95);
gltf.scene.scale.set(16, 12, 12);
that.scene.add(gltf.scene);
});
},
这里有点需要注意的是,加载GLTF文件和加载OBJ文件虽然过程很相似,但是加载过来的对象结构是有区别的,详细大家可以在使用时,打印加载的结果对比一下
总结
这章主要是讲解模型的创建,基础几何模型以及外部模型文件导入。
文中代码有一些具体属性的调整,大家在看的时候可以忽略,根据自己的需求去调整这些属性