1、建模软件
3D美术常用的三维建模软件,比如Blender、3damx、C4D、maya等等
- Blender(轻量、免费、开源)
- 3damx
- C4D
- maya
机械相关:SW、UG等
建筑相关:草图大师、revit
2、GLTF格式简介 (Web3D领域JPG)
Khronos Group组织2015发布了GLTF 1.0版本,在2017年又发布了GLTF2.0的版本。
关于glTF的更多介绍和信息,可以查看github:https://github.com/KhronosGroup/glTF
(1)gltf的优点
相比较obj、stl等格式而言,.gltf格式可以包含更多的模型信息。
.gltf格式文件几乎可以包含所有的三维模型相关信息的数据,比如模型层级关系、PBR材质、纹理贴图、骨骼动画、变形动画...
(2)gltf格式
GLTF文件就是通过JSON的键值对方式来表示模型信息,比如meshes
表示网格模型信息,materials
表示材质信息...
(3).bin文件
有些glTF文件会关联一个或多个.bin文件,.bin文件以二进制形式存储了模型的顶点数据等信息。 .bin文件中的信息其实就是对应gltf文件中的BufferAttribute。
bin文件可以存储在.gltf文件中,也可以单独一个二进制.bin文件
(4)二进制.glb
.glb就是gltf格式的二进制文件。
比如你可以把.gltf模型和贴图信息全部合成得到一个.glb文件中,.glb文件相对.gltf文件体积更小,网络传输自然更快。
(5)导出gltf
blender:最新版本可以直接导出gltf。
3damx gltf相关插件:https://github.com/BabylonJS/Exporters/releases
(6)gltf不同文件形式
.gltf格式模型文件,有不同的文件组织形式,加载方法是一样的。
- 单独.gltf文件
- 单独.glb文件
- .gltf + .bin + 贴图文件
3、加载GLTF格式模型文件
(1)gltf模型加载器GLTFLoader.js
在three.js官方文件的examples/jsm/子文件loaders/目录下,可以找到一个文件GLTFLoader.js
,这个文件就是three.js的一个扩展库,专门用来加载gltf格式模型加载器。
// 引入gltf模型加载库GLTFLoader.js import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
// 创建GLTF加载器对象 const loader = new GLTFLoader(); //加载模型文件,返回gltf对象 loader.load( 'gltf模型.gltf', function ( gltf ) { console.log('控制台查看加载gltf文件返回的对象结构',gltf); console.log('gltf对象场景属性',gltf.scene); // 返回的场景对象gltf.scene插入到threejs场景中 scene.add( gltf.scene ); })
(2)相机选择
大部分3D项目,一般都是使用透视投影相机PerspectiveCamera
,比如游戏、物联网等项目都会选择透视投影相机PerspectiveCamera
。正投影相机OrthographicCamera使用较少。
如果你希望渲染的结果符合人眼的远小近大的规律,选择透视投影相机,使用较多。
如果不需要模拟人眼远小近大的投影规律,可以选择正投影相机。
threejs解析gltf模型默认材质一般是MeshStandardMaterial或MeshPhysicalMaterial。
(3)尺寸概念-单位要统一
程序员对一个模型或者说一个三维场景要有一个尺寸的概念,不用具体值,要有一个大概范围区间。一般通过三维建模软件blender可以轻松测试测量模型尺寸。
three.js的世界并没有任何单位,只有数字大小的运算。
obj、gltf格式的模型信息只有尺寸,并不含单位信息。
实际项目开发的时候,一般会定义一个单位,一方面甲方、前端、美术之间更好协调,甚至你自己写代码也要有一个尺寸标准。比如一个园区、工厂,可以m为单位建模,比如建筑、人、相机都用m为尺度去衡量,如果单位不统一,就需要你写代码,通过.scale
属性去缩放,比较麻烦。
(4)利用OrbitControls-设置相机参数
相机控件OrbitControls
旋转缩放平移本质上就是在改变相机Camera
的参数。
可以通过OrbitControls可视化调整场景,在渲染循环中打印相机的参数,然后根据这个参数设置相机即可。
function render() { requestAnimationFrame(render); // 浏览器控制台查看相机位置变化 console.log('camera.position',camera.position); // 浏览器控制台查看controls.target变化,辅助设置相机的lookAt参数 console.log('controls.target',controls.target); } render();
注意相机控件OrbitControls会影响lookAt设置,注意手动设置OrbitControls的目标参数
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)
(5)gltf模型色差问题-webgl渲染器编码方式设置-THREE.sRGBEncoding
three.js加载gltf模型的时候,可能会遇到three.js渲染结果颜色偏差。
模型中颜色贴图texture是THREE.sRGBEncoding,渲染器编码默认是THREE.LinearEncoding。需要修改一致才行。(单独加载的贴图图片,也是需要设置编码属性的,一般用sRGB)
- THREE.LinearEncoding:线性颜色空间,threeJs中值是3000
- THREE.sRGBEncoding:sRGB颜色空间,threeJs中值是3001
//解决加载gltf格式模型纹理贴图和原图不一样问题 renderer.outputEncoding = THREE.sRGBEncoding;
4、模型应用管理
加载gltf模型,通过gltf.scene
可以获取模型的数据,你可以通过浏览器控制打印gltf.scene
,然后和你三维建模软件中的模型目录树对比,比较两者的结构是否相同。
- 模型父对象节点可以用
Object3D
对象表示,也可以用组对象Group
表示。 - 通过
.children
属性可以查看一个父对象模型的的所有子对象。 - 通过
.name
属性可以查看模型节点的名称
(1)getObjectByName()根据name获取模型节点
一般三维建模软件的目录树,都有模型的名称,可以根据模型名称去查找模型。
// 返回名.name为"1号楼"对应的对象 const nameNode = gltf.scene.getObjectByName("1号楼"); nameNode.material.color.set(0xff0000);//改变1号楼Mesh材质颜色
//获得所有'洋房'房子的分组 const obj = gltf.scene.getObjectByName('洋房'); console.log('obj', obj); //控制台查看返回结果 // obj.children的所有子对象都是Mesh,改变Mesh对应颜色 obj.children.forEach(function (mesh) { mesh.material.color.set(0xffff00); })
(2)traverse()递归遍历层级模型修改材质
加载一个外部模型,如果你想批量修改每个Mesh的材质,一个一个设置比较麻烦,可以通过递归遍历方法.traverse()
批量操作更加方便。
gltf.scene.traverse(function(obj) { if (obj.isMesh) { // 重新设置材质 obj.material = new THREE.MeshLambertMaterial({ color:0xffffff, }); } });
(3) 外部模型材质是否共享的问题
如果外部模型文件中,多个模型共享材质,改变一个模型颜色其它模型跟着变化。
解决方法:
- 三维建模软件中设置,需要代码改变材质的Mesh不要共享材质,要独享材质。
- 代码批量更改:克隆材质对象,重新赋值给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);
(4)修改gltf模型中的纹理map
注意单独加载的纹理贴图的.encoding
和webgl渲染器的.outputEncoding
保持一致。
const texLoader = new THREE.TextureLoader(); const texture = texLoader.load('./黑色.png');// 加载手机mesh另一个颜色贴图 texture.encoding = THREE.sRGBEncoding; //和渲染器.outputEncoding一样值
注意:如果你直接给gltf模型材质设置.map
属性更换贴图,会出现纹理贴图错位的问题,这主要和纹理对象Texture
的翻转属性.flipY
有关。
(如果其他相关设置,可以分别打印模型的map和贴图的属性,然后对比材质属性的区别)
loader.load("../手机模型.glb", function (gltf) { const mesh = gltf.scene.children[0]; //获取Mesh mesh.material.map = texture; //更换不同风格的颜色贴图 })
如果更换单独加载的纹理贴图,比如颜色贴图.map
,注意把纹理贴图.flipY的值设置给gltf中纹理的值false。
//是否翻转纹理贴图 texture.flipY = false;
下面展示一个完整案例:
完整代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Three.js中文网:www.webgl3d.cn</title> <style> body { overflow: hidden; margin: 0px; } </style> </head> <body> <script type="importmap"> { "imports": { "three": "../../../three.js/build/three.module.js", "three/addons/": "../../../three.js/examples/jsm/" } } </script> <script type="module"> import * as THREE from 'three'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'; //场景 const scene = new THREE.Scene(); const loader = new GLTFLoader(); //创建一个GLTF加载器 const model = new THREE.Group(); //声明一个组对象,用来添加加载成功的三维场景 loader.load("../../工厂.glb", function (gltf) { // 修改name为"地面"的颜色 const nameNode = gltf.scene.getObjectByName("地面"); nameNode.material.color.set(0xff0000);//改变1号楼Mesh材质颜色 //修改name为'大货车'的所有孩子模型的颜色 const obj = gltf.scene.getObjectByName('大货车'); obj.children.forEach(function (mesh) { mesh.material.color.set(0xffff00); }) // 递归遍历所有模型节点批量修改name gltf.scene.traverse(function(obj) { if (obj.isMesh) {//判断是否是网格模型 obj.name='aa' } }); model.add(gltf.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; // 设置相机控件轨道控制器OrbitControls const controls = new OrbitControls(camera, renderer.domElement); // 渲染循环 function render() { renderer.render(scene, camera); requestAnimationFrame(render); } render(); // 画布跟随窗口变化 window.onresize = function () { renderer.setSize(window.innerWidth, window.innerHeight); camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); }; </script> </body> </html>
文章中部分素材选取自Threejs中文网:http://www.webgl3d.cn/
标签:const,模型,scene,js,three,obj,加载,gltf From: https://www.cnblogs.com/tiandi/p/17064109.html