注:本文章内容基于 Three.js 88dev 实现
作为刚接触three.js的小萌新,励志将自己开荒历程记录下来,希望对后来人有所帮助。
网上有很多demo,文档却不多。每次都是,照搬别人的数据没问题,换成自己的模型/动画总会报错! (╯‵□′)╯︵┻━┻
多次踩坑后,总结出三种常用格式的加载方法。
1、fbx文件
three.js有官方的fbx插件,可以直接将模型加载至网页,并且支持动画数据,代码量也是最少的。
但是,该格式存在很大弊端:插件对文件格式的规范很严格,换言之,插件支持性不太好。从网上下载的fbx动画,十有八九会加载失败。
首先需要引入FBXLoader.js插件,如果 报错 “Error: THREE.FBXLoader: External library Inflate.min.js required, obtain or import from https://github.com/imaya/zlib.js ” ,则还需引入inflate.min.js文件。
var fbx_loader = new THREE.FBXLoader(manager);
1.1、静态模型
fbx_loader.load('./models/miku/miku.fbx', function(object) {
object.scale.multiplyScalar(.1); // 缩放模型大小
scene.add(object);
}, onProgress, onError);
1.2、动画
fbx_loader.load('./models/gf/run.fbx', function(object) {
object.mixer = new THREE.AnimationMixer(object);
mixers.push(object.mixer);
var action = object.mixer.clipAction(object.animations[0]);
action.play();
object.scale.multiplyScalar(.5);
scene.add(object);
}, onProgress, onError);
1、如果遇到报错 “URIError: URI malformed”,说明fbx文件格式不符合插件要求。可能是fbx版本过低导致的。
检测方法:
新版blender导入该fbx文件,如果提示 “Version xxxx unsupported, must be xxxx or later”,说明你的模型文件版本太低。
解决方案有三:
(1)导入3dmax,再重新导出成fbx文件,将低版本转换为最新版本。经实践,虽然在3dmax和blender中动画显示正常,但是载入网页后模型变得支离破碎。可能是坐标丢失,目前还在寻找原因及解决办法。
(2)用官方提供的插件将fbx文件转换成json动画数据。
(3)根据 [blender]version 6100 unsupported,must be 7100 or later问题怎么办 提供的方法,可通过FBX_Converter_2013将低版本fbx转换成可支持的fbx文件,亲测可用。
2、如果遇到报错 “TypeError: Cannot read property 'has' of undefined”,同样是因为模型文件不符合规范。
检测方法:
Chrome Devtools断点调试,你会发现很多参数都是undefined。
解决方案:
同上(2)。
2、json文件
three.js自带了加载json的方法,所以不需要额外引用插件。
Three.js展示模型问题总结 中讲到:
现在的JSON格式有两个类型,一个是Geometry类型,需要JSONLoader加载;一个是Object类型,需要ObjectLoader加载。
用错loader.js的话,会报错 “THREE.ObjectLoader: Can't load xxx.json. Use THREE.JSONLoader instead.” 或者 “THREE.JSONLoader: xxx.json should be loaded with THREE.ObjectLoader instead.”。
2.1、静态模型
2.1.1、Geometry类型
var js_loader = new THREE.JSONLoader(manager);
js_loader.load('./models/hmj/frame001.json', function(geometry, materials) {
var material = new THREE.MultiMaterial(materials); // 多个纹理
var mesh = new THREE.Mesh(geometry, material);
mesh.scale.multiplyScalar(.06);
scene.add(mesh);
}, onProgress, onError);
var object_loader = new THREE.ObjectLoader(manager);
object_loader.load('./models/teapot-claraio.json', function(object) {
object.scale.multiplyScalar(5);
scene.add(object);
var js_loader = new THREE.JSONLoader(manager);
js_loader.load('./models/body/climb.js', function(geometry, materials) {
for(var i = 0; i < materials.length; i++) {
materials[i].skinning = true;
var material = new THREE.MultiMaterial(materials);
var mesh = new THREE.SkinnedMesh(geometry, material); // 划重点啊!!!
var mixer = new THREE.AnimationMixer(mesh);
mixer.clipAction(geometry.animations[0]).play();
mixers.push(mixer);
mesh.scale.multiplyScalar(.05);
mesh.lookAt(new THREE.Vector3(0, 0, 0));
scene.add(mesh);
}, onProgress, onError);
说到json格式动画,一把辛酸泪(╥﹏╥)。从动画制作到maya导出,再到网页载入,无不有坑。
1、由于不了解three.js的数据需求,动画制作方用maya插件advancedSkeleton进行绑骨,导致动画可以展示,数据却导出不来,只能自掏腰包重做。
制作动画时,切记要做成能导出fbx格式的。
2、用maya做动画,虽然官方有提供maya转three.js的插件,不过导出数据时,还是碰到了不少问题。
maya2016用官方插件报错 “IOError: file C:\Program Files\Autodesk\Maya2016\bin\python27.zip\shutil.py line 82: 2”,Maya 导出动画到THREE.js 的博主给出修改后的插件,亲测可以使用。然而还是不可避免地掉了坑。
Maya导出动画到THREE.js(补充) 对修改后的插件进行了总结,其中隐藏网格和报错 “More than 4 influences on a vertex in xxx” 的问题我这边也遇到了。
当然,以上两个bug最直接有效的解决办法是,在做动画时就让设计师删除隐藏网格并导出可用的fbx文件。
3、json动画载入网页时,报错 “THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.”,google了好久才查出,是因为自己创建的不是骨骼蒙皮网格对象(代码划重点部分)。
从3dMax导出供threeJS使用的带动作... 里 “代码中如何加载动态模型” 有具体讲解。
3、obj文件
obj格式文件不支持动画数据存储,只用于静态模型。
首先需要引入OBJLoader.js插件,如果纹理贴图是tga或dds格式的,则还需要另外引入TGALoader.js或DDSLoader.js(纹理贴图问题同样适用于其他模型格式)。
var obj_loader = new THREE.OBJLoader(manager);
obj_loader.setPath('./models/mooncake/'); // 设置文件路径
自己总结了两种加载方法。
3.1、外部载入纹理
var tga_loader = new THREE.TGALoader();
var material = new THREE.MeshPhongMaterial({
map: tga_loader.load('./models/mooncake/Diffuse.tga'),
normalMap: tga_loader.load('./models/mooncake/Normal.tga'),
specularMap: tga_loader.load('./models/mooncake/S.tga'),
bumpMap: tga_loader.load('./models/mooncake/Bump.tga')
}); // 存在多个纹理材质,具体参数查看[官方文档 - MeshPhongMaterial]
obj_loader.load('mooncake.obj', function(group) {
var geometry = group.children[0].geometry;
geometry.attributes.uv2 = geometry.attributes.uv;
geometry.center();
var mesh = new THREE.Mesh(geometry, material);
mesh.scale.multiplyScalar(.1);
scene.add(mesh);
}, onProgress, onError);
3.2、obj+mtl
需要额外引用MTLLoader.js文件
THREE.Loader.Handlers.add(/\.tga$/i, new THREE.TGALoader()); // 划重点!
var mtl_loader = new THREE.MTLLoader();
mtl_loader.setPath('./models/mooncake/');
mtl_loader.load('mooncake.mtl', function(materials) {
materials.preload();
obj_loader.setMaterials(materials);
obj_loader.load('mooncake.obj', function(object) {
object.scale.multiplyScalar(.1);
scene.add(object);
}, onProgress, onError);
后来偶然看到THREE.Loader.Handlers.add(/\.tga$/i, new THREE.TGALoader());
这句代码。经实践,果然能载入某些不常用格式的纹理材质。
2018.07.13更新
在1、fbx文件
→1.2、动画
→解决方案
中新增加了fbx低版本转高版本的方法
一开始打算用obj+mtl方法加载obj模型,但由于项目是tga格式贴图,参考大佬的代码,却怎么都显示不了纹理,最后放弃转而琢磨出第一种方法来。