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) 外部模型材质是否共享的问题

如果外部模型文件中,多个模型共享材质,改变一个模型颜色其它模型跟着变化。

解决方法:

  1. 三维建模软件中设置,需要代码改变材质的Mesh不要共享材质,要独享材质。
  2. 代码批量更改:克隆材质对象,重新赋值给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一样值

(5)纹理对象Texture翻转属性.flipY默认值

注意:如果你直接给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/

three.js教程6-加载外部三维模型gltf的更多相关文章

  1. WebGL three.js学习笔记 加载外部模型以及Tween.js动画

    WebGL three.js学习笔记 加载外部模型以及Tween.js动画 本文的程序实现了加载外部stl格式的模型,以及学习了如何把加载的模型变为一个粒子系统,并使用Tween.js对该粒子系统进行 ...

  2. FusionCharts简单教程(六)------加载外部Logo

    一.加载外部文件Logo       在使用FusionCharts时,我们可能需要在加载图像的时候需要在图表中显示标识.图片等等.这里我们可以使用logoURL属性来实现.如: <chart ...

  3. 无阻塞加载外部js(动态脚本元素,XMLHttpRequest注入,LazyLoad)

    动态脚本元素即在js中去创建<script>标签加载外部js并执行,这样加载的好处是文件的下载和执行过程不会阻塞页面的其他进程.通过下面两个例子对比出效果 <!DOCTYPE htm ...

  4. kettle转换JavaScript加载外部js文件

    日常开发中,时常会出现这样一种情况.有大量的函数是通用的.而每个JavaScript里面写一遍,给维护带来很大的困扰.因而需要将公共的函数写在外部js文件中.这时就需要引入外部的公共文件了.下面是在转 ...

  5. 使用js加载器动态加载外部Javascript文件

    原文:http://www.cnblogs.com/xdp-gacl/p/3927417.html 今天在网上找到了一个可以动态加载js文件的js加载器,具体代码如下: JsLoader.js var ...

  6. 浏览器加载外部js 的顺序,以及处理顺序。

    问题, 有事候按F12打开 google的调试台后,查看network下面加载过来的资源是, 有些资源的状态处于 pending.. 个人理解: 浏览器是可以同时开启多个http 请求去加载外部的资源 ...

  7. Vue 加载外部js文件

    Vue.js  加载外部js文件 在项目内新建一个config.js //变量的定义 export var config = { baseurl:'http://172.16.114.5:8088/M ...

  8. JavaScript学习总结(十九)——使用js加载器动态加载外部Javascript文件

    今天在网上找到了一个可以动态加载js文件的js加载器,具体代码如下: JsLoader.js 1 var MiniSite=new Object(); 2 /** 3 * 判断浏览器 4 */ 5 M ...

  9. 使用js加载器动态加载外部js、css文件

    let MiniSite = new Object(); /** * 判断浏览器 */ MiniSite.Browser = { ie: /msie/.test(window.navigator.us ...

  10. 关于js css html加载顺序整理

    1.js放在head中会立即执行,阻塞后续的资源下载与执行.因为js有可能会修改dom,如果不阻塞后续的资源下载,dom的操作顺序不可控. 正常的网页加载流程是这样的. 浏览器一边下载HTML网页,一 ...

随机推荐

  1. 【WCH以太网接口系列芯片】STM32+CH390+Lwip协议栈简单应用测试

    本篇文章基于STM32F103和CH390H芯片进行例程移植及相关注意事项,简单验证TCP\UDP\Ping基础功能. 硬件:STM32F103开发板+沁恒CH390H的评估版图一示,SPI使用接口为 ...

  2. #01背包#洛谷 2340 [USACO03FALL]Cow Exhibition G

    题目 有\(n\)个物品,对于第\(i\)个物品, 有两种属性,第一种属性为\(x_i\),第二种属性为\(y_i\) 问选择若干个物品使得\(\sum{x_j}\geq 0\)且\(\sum{y_j ...

  3. #二叉堆#JZOJ 4320 旅行

    分析 有一个很重要的性质就是如果经过道路数为奇数,把两个点到根节点的路径长加起来就是两个点间的路径长(正负消掉了) 而且众所周知的是奇数+偶数=奇数 可以预处理每个点到根节点的路径长度(按照题目要求) ...

  4. Java 编程指南:入门,语法与学习方法

    Java 是什么? Java 是一种流行的编程语言,诞生于 1995 年.由 Oracle 公司拥有,运行在超过 30 亿台设备上.Java 可以用于: 移动应用程序(尤其是 Android 应用) ...

  5. Go 语言中结构体的使用和示例

    结构体(简称struct)用于创建不同数据类型的成员集合,放入一个单一的变量中.虽然数组用于将相同数据类型的多个值存储在单一变量中,但结构体用于将不同数据类型的多个值存储在单一变量中.结构体对于将数据 ...

  6. VS2019快捷键

    快捷键功能CTRL + SHIFT + B生成解决方案CTRL + F7 生成编译CTRL + O 打开文件CTRL + SHIFT + O打开项目CTRL + SHIFT + C显示类视图窗口F4 ...

  7. os.path.splitext

    os.path.splitext是Python标准库中的一个函数,它可以将一个文件路径拆分成两部分:文件名和文件扩展名.例如: 点击查看代码 import os file_path='avercrop ...

  8. ViT:拉开Trasnformer在图像领域正式挑战CNN的序幕 | ICLR 2021

    论文直接将纯Trasnformer应用于图像识别,是Trasnformer在图像领域正式挑战CNN的开山之作.这种简单的可扩展结构在与大型数据集的预训练相结合时,效果出奇的好.在许多图像分类数据集上都 ...

  9. SEO — 搜索引擎优化

    一.多词排名标题设置 SEO(Search Engine Optimization)是指搜索引擎优化,即利用搜索引擎的规则提高网站在有关搜索引擎内的自然排名 优化目的是:让网站在搜索引擎上的曝光率达到 ...

  10. mysql 锁机制(一)

    前言 介绍mysql 锁的机制. 正文 锁类型 读锁,是一种共享锁,s锁,允许一个事务是读取一行,阻止其他事务获取相同的数据集的排他锁. 注:排它锁的意思就是说只能加相同的锁,不能加不同的锁,比如都加 ...