threejs(六)加载外部模型 .gltf
一、GLTF科普篇
1、三维建模软件简介
3D美术常用的三维建模软件,比如Blender、3dmax、C4D、maya等等
可以用Blender软件导出绘制好的三维模型,也可以打开和预览gltf格式文件模型。
blender可以导出不同形式的 gltf 模型
- 单独.gltf文件
- 单独.glb文件
- .gltf + .bin + 贴图文件
2、GLTF格式简介
- GLTF格式是三维模型格式
- .gltf格式文件几乎可以包含所有的三维模型相关信息的数据,比如模型层级关系、PBR材质、纹理贴图、骨骼动画、变形动画
- GLTF文件就是通过JSON的键值对方式来表示模型信息,比如meshes表示网格模型信息,materials表示材质信息…
{"asset": {"version": "2.0",},
...
// 模型材质信息"materials": [{"pbrMetallicRoughness": {//PBR材质"baseColorFactor": [1,1,0,1],"metallicFactor": 0.5,//金属度"roughnessFactor": 1//粗糙度}}],// 网格模型数据"meshes": ...// 纹理贴图"images": [{// uri指向外部图像文件"uri": "贴图名称.png"//图像数据也可以直接存储在.gltf文件中}],"buffers": [// 一个buffer对应一个二进制数据块,可能是顶点位置 、顶点索引等数据{"byteLength": 840,//这里面的顶点数据,也快成单独以.bin文件的形式存在 "uri": "data:application/octet-stream;base64,AAAAPwAAAD8AAAA/AAAAPwAAAD8AAAC/.......}],
}
3、.bin文件
二进制文件
4、.glb
.glb就是gltf格式的二进制文件
。比如你可以把.gltf模型和贴图信息全部合成得到一个.glb文件中,.glb文件相对.gltf文件体积更小,网络传输自然更快
二、加载.gltf文件
效果
关键点
- 引入
GLTFLoader.js
// 引入gltf模型加载库GLTFLoader.js
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
- 执行
new GLTFLoader()
就可以实例化一个gltf的加载器对象
// 创建GLTF加载器对象
const loader = new GLTFLoader();
- 通过gltf加载器方法
.load()
就可以加载外部的gltf模型
loader.load( 'gltf模型.gltf', function ( gltf ) {console.log('控制台查看加载gltf文件返回的对象结构',gltf);console.log('gltf对象场景属性',gltf.scene);// 返回的场景对象gltf.scene插入到threejs场景中scene.add( gltf.scene );
})
- 相机选择:要近大远小的规律的话,就采用透视投影相机
PerspectiveCamera
;如果不需要模拟人眼远小近大的投影规律,可以选择正投影相机OrthographicCamera
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
camera.position.set(200, 200, 200);
camera.lookAt(0, 0, 0);
- 相机参数设置:可以借助 Blender 看下美工的模拟图是多大,然后给相机设置
.position
,比如下面这张图就可以设置相机位置在200
camera.position.set(200, 200, 200);
6. 让位置在canvas画布居中:调 camera.lookAt()。如果美术建模,把工厂整体居中,也就是说模型的几何中心,大概位于世界坐标原点。你设置camera.lookAt(0,0,0)
,相机视线指向坐标原点。
注意相机控件OrbitControls会影响lookAt设置
,注意手动设置OrbitControls的目标参数
camera.lookAt(100, 0, 0); // 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement); // 相机控件.target属性在OrbitControls.js内部表示相机目标观察点,默认0,0,0
controls.target.set(100, 0, 0);
controls.update(); //update()函数内会执行camera.lookAt(controls.targe)
- 解决加载 gltf 格式模型纹理贴图和原图不一样的问题
renderer.outputEncoding = THREE.sRGBEncoding; // 解决加载 gltf 格式模型纹理贴图和原图不一样的问题
完整代码
index.js
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';import model from './model.js';//模型对象//场景
const scene = new THREE.Scene();
scene.add(model); //模型对象添加到场景中//辅助观察的坐标系
const axesHelper = new THREE.AxesHelper(100);
scene.add(axesHelper);//光源设置
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(400, 200, 300);
scene.add(directionalLight);
const ambient = new THREE.AmbientLight(0xffffff, 0.4);
scene.add(ambient);//渲染器和相机
const width = window.innerWidth;
const height = window.innerHeight;
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
camera.position.set(200, 200, 200);
camera.lookAt(0, 0, 0);const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);// 解决加载 gltf 格式模型纹理贴图和原图不一样的问题
renderer.outputEncoding = THREE.sRGBEncoding;// 渲染循环
function render() {renderer.render(scene, camera);requestAnimationFrame(render);
}
render();const controls = new OrbitControls(camera, renderer.domElement);// 画布跟随窗口变化
window.onresize = function () {renderer.setSize(window.innerWidth, window.innerHeight);camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();
};
model.js
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';// *实例化加载器对象
const loader = new GLTFLoader();
const model = new THREE.Group();loader.load("../../工厂.gltf", function (gltf) {console.log('gltf', gltf);model.add(gltf.scene);
})export default model
三、相机初始位置的调试技巧
1、修改相机位置
通过OrbitControls
旋转和缩放,本质上就是在改变透视投影相机PerspectiveCamera
的位置.position
在render中,打印 camera.position 即可查看相机位置改变,这样就可以根据这个位置设置相机初始化时的位置
function render() {requestAnimationFrame(render);// 浏览器控制台查看相机位置变化console.log('camera.position',camera.position);
}
render();
camera.position.set(200, 200, 200);//第1步:根据场景渲染范围尺寸设置
camera.position.set(-144, 95, 95); //第2步:通过相机控件辅助设置OrbitControls
2、改变相机观察目标
function render() {requestAnimationFrame(render);// 浏览器控制台查看controls.target变化,辅助设置lookAt参数console.log('controls.target',controls.target);
}
render();
// camera.lookAt(0, 0, 0);
const x = -1.2,y = -15,z = 10;//通过OrbitControls辅助设置
camera.lookAt(x, y, z);// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);
// 相机控件.target属性在OrbitControls.js内部表示相机目标观察点,默认0,0,0
// console.log('controls.target', controls.target);
controls.target.set(x, y, z); //与lookAt参数保持一致
controls.update(); //update()函数内会执行camera.lookAt(controls.target)
四、程序命名(程序与美工的协作)
美工提供的三维模型中的名称,可以在 threejs 中查看,并可通过名称获取节点
1、查看3D模型树结构 ---- gltf.scene
加载gltf模型,通过gltf.scene
可以获取模型的数据,你可以通过浏览器控制打印gltf.scene,然后和你三维建模软件中的模型目录树对比,比较两者的结构是否相同。
模型父对象节点可以用Object3D对象表示,也可以用组对象Group表示。
通过.children属性可以查看一个父对象模型的的所有子对象。
通过.name属性可以查看模型节点的名称
loader.load("./简易小区.glb", function (gltf) { console.log('场景3D模型树结构', gltf.scene);model.add(gltf.scene);
})
2、根据名称查看节点 ---- getObjectByName
// 返回名.name为"1号楼"对应的对象
const nameNode = gltf.scene.getObjectByName("1号楼");
nameNode.material.color.set(0xff0000);//改变1号楼Mesh材质颜色
五、遍历模型节点 traverse
1、递归遍历所有模型节点批量修改材质
// 递归遍历所有模型节点批量修改材质
gltf.scene.traverse(function(obj) {if (obj.isMesh) {console.log('gltf默认材质',obj.material);}
});
2、批量修改gltf所有Mesh的材质
gltf.scene.traverse(function(obj) {if (obj.isMesh) {// 重新设置材质obj.material = new THREE.MeshLambertMaterial({color:0xffffff,});}
});
3、代码方式解决多个mesh共享材质的问题
//用代码方式解决mesh共享材质问题
gltf.scene.getObjectByName("小区房子").traverse(function (obj) {if (obj.isMesh) {// .material.clone()返回一个新材质对象,和原来一样,重新赋值给.material属性obj.material = obj.material.clone();}
});
mesh1.material.color.set(0xffff00);
mesh2.material.color.set(0x00ff00);