通过Three.js的材质和几何体,我们可以很方便的创建基础3D模型,但涉及到复杂模型时,一般是由专业建模工具 生成模型 文件再导入的方式将模型引入到我们的3D场景中进行使用
Three.js提供多种加载器以支持市面上多种格式的3D文件导入操作
加载器 | 支持文件格式 |
---|---|
3DMLoader | .3dm |
DRACOLoader | .drc |
FontLoader | .json |
GLTFLoader | glTF(gl传输格式)是一种开放格式的规范 (open format specification), 用于更高效地传输、加载3D内容。该类文件以JSON(.gltf)格式或二进制(.glb)格式提供, 外部文件存储贴图(.jpg、.png)和额外的二进制数据(.bin) |
KTX2Loader | .ktx2 |
LDrawLoader | LDraw资源(一个文本文件,通常扩展名为.ldr、.dat或.txt)可以只描述单个构造件或整个模型。 |
MMDLoader | MMDLoader从MMD资源(例如PMD(.pmd)、PMX(.pmx)、VMD(,vmd)和VPD(.vpd)文件)中创建Three.js物体(对象) |
MTLLoader | .mlt或.mlt与.obj的配套文件 |
OBJLoader | .obj |
PCDLoader | .pcd |
PDBLoader | .pdb |
PRWMLoader | .prwm |
SVGLoader | .svg |
TGALoader | .tga |
模型获取:
通过建模工具或者模型素材网站获取,学习用的话官网的模型也是可以使用的,在官方示例文件夹中有很多模型文件目录:three.js-master\examples\models
模型导入:
模型导入的要素
- 模型文件
- 模型加载器
模型导入的流程
- 引入加载器并实例化
- 使用加载器导入模型文件
模型导入的操作
本文将以典型 GLTF 加载器(GLTFLoader)为例进行模型导入操作,模型使用的是官方示例中的Soldier模型,文件位置:three.js-master\examples\models\gltf\Soldier.glb
为了方便操作我们将文件拷出来放在static\3dmod\gltf文件夹下,static与three.js-master同级
代码基于文章《Three.js基础入门介绍——Three.js学习三【借助控制器操作相机】》的基础上进行修改,文件结构如下
index.html
//1、引入加载器并实例化
import { GLTFLoader } from "./three.js-master/examples/jsm/loaders/GLTFLoader.js";
const gltfLoader = new GLTFLoader();
加载器方法官方文档链接:https://threejs.org/docs/index.html?q=loader#examples/zh/loaders/GLTFLoader
这里用了一个Loader(实现加载器的基类)公有方法.setPath ( path : String ) : this设置资源基本路径,不用的话也可以直接在.load方法第一个url参数中写完整路径。加载器解析基于glTF的ArrayBuffer或JSON字符串,并在完成后触发onLoad回调。onLoad的参数将是一个包含有已加载部分的Object:.scene、 .scenes、 .cameras、 .animations 和 .asset。
.load ( url : String, onLoad : Function, onProgress : Function, onError : Function ) : undefined
- url — 包含有.gltf/.glb文件路径/URL的字符串。
- onLoad — 加载成功完成后将会被调用的函数。该函数接收parse所返回的已加载的JSON响应。
- onProgress — (可选)加载正在进行过程中会被调用的函数。其参数将会是XMLHttpRequest实例,包含有总字节数.total与已加载的字节数.loaded。
- onError — (可选)若在加载过程发生错误,将被调用的函数。该函数接收error来作为参数。
- 开始从url加载,并使用解析过的响应内容调用回调函数。
//2、使用加载器导入模型文件,这里注意,需要用在scene创建后
gltfLoader.setPath('./static/3dmod/gltf/')
.load('Soldier.glb', function (gltf) {
console.log("gltf",gltf)
gltf.scene.scale.set(1, 1, 1)
gltf.scene.traverse(function (child) {
if (child instanceof THREE.Mesh) {
child.castShadow = true; //阴影
child.receiveShadow = true; //接受别人投的阴影
}
});
scene.add(gltf.scene);
}, function(res){
console.log(res.total, res.loaded)
});
当我们添加好后会发现是黑黑的,只能看到在转动的绿色立方体,但是我们使用鼠标转动一下视角还是能发现有模型的存在
追其原因是我们场景中缺少了光照,添加光照
//添加一个白色点光源
const light = new THREE.PointLight(0xffffff, 1, 100, 1);
light.position.set(0, 10, 10 );
scene.add(light);
模型可以看见了是一个伸展双臂的Soldier,但是背景还是黑色的,而且,这个Soldier是背向的。
调整一下转向以及背景颜色,调整角度如果不知道怎么转,可以引入坐标系来辅助找旋转角度
// 增加坐标系红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.
// 显示三维坐标系,辅助找旋转角度
const axes = new THREE.AxesHelper(20);
// 添加坐标系到场景中
scene.add(axes);
//2、使用加载器导入模型文件,这里注意,需要用在scene创建后,加载完后修改转向
gltfLoader.setPath('./static/3dmod/gltf/')
.load('Soldier.glb', function (gltf) {
console.log("gltf",gltf)
gltf.scene.rotation.y = Math.PI; //沿着Y轴转180
gltf.scene.scale.set(1, 1, 1)
gltf.scene.traverse(function (child) {
if (child instanceof THREE.Mesh) {
child.castShadow = true; //阴影
child.receiveShadow = true; //接受别人投的阴影
}
});
scene.add(gltf.scene);
}, function(res){
console.log(res.total, res.loaded)
});
//在renderer渲染器创建后设置场景颜色
renderer.setClearColor(new THREE.Color(0xbbbbbb));
效果:
完整代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My first three.js app</title>
<style>
body { margin: 0; }
</style>
</head>
<body>
<script type="importmap">
{
"imports": {
"three": "./three.js-master/build/three.module.js"
}
}
</script>
<script type="module">
import * as THREE from "three";
import { OrbitControls } from "./three.js-master/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "./three.js-master/examples/jsm/loaders/GLTFLoader.js";
const gltfLoader = new GLTFLoader();
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
// 增加坐标系红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.
// 显示三维坐标系
const axes = new THREE.AxesHelper(20);
// 添加坐标系到场景中
scene.add(axes);
gltfLoader.setPath('./static/3dmod/gltf/')
.load('Soldier.glb', function (gltf) {
console.log("gltf",gltf)
gltf.scene.rotation.y = Math.PI;
gltf.scene.scale.set(1, 1, 1)
gltf.scene.traverse(function (child) {
if (child instanceof THREE.Mesh) {
child.castShadow = true; //阴影
child.receiveShadow = true; //接受别人投的阴影
}
});
scene.add(gltf.scene);
}, function(res){
console.log(res.total, res.loaded)
});
//添加一个白色点光源
const light = new THREE.PointLight(0xffffff, 1, 100, 1);
light.position.set(0, 10, 10);
scene.add(light);
const renderer = new THREE.WebGLRenderer();
renderer.setClearColor(new THREE.Color(0xbbbbbb));
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
const controls = new OrbitControls(camera,renderer.domElement);//创建控件对
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
const cube = new THREE.Mesh( geometry, material );
scene.add( cube );
camera.position.z = 5;
function animate() {
requestAnimationFrame( animate );
cube.rotation.x += 0.01;
cube.rotation.y += 0.01;
renderer.render( scene, camera );
};
animate();
</script>
</body>
</html>