THREE.js中加载不同格式的模型及动画(fbx、json和obj)

注:本文章内容基于 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);
fbx静态模型

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);

fbx动画

1、如果遇到报错 “URIError: URI malformed”,说明fbx文件格式不符合插件要求。可能是fbx版本过低导致的。

检测方法:
新版blender导入该fbx文件,如果提示 “Version xxxx unsupported, must be xxxx or later”,说明你的模型文件版本太低。

解决方案有三:
(1)导入3dmax,再重新导出成fbx文件,将低版本转换为最新版本。经实践,虽然在3dmax和blender中动画显示正常,但是载入网页后模型变得支离破碎。可能是坐标丢失,目前还在寻找原因及解决办法。

fbx低版本转高版本后,坐标丢失

(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);
json静态模型 - Geometry类型
2.1.2、Object类型
var object_loader = new THREE.ObjectLoader(manager);

object_loader.load('./models/teapot-claraio.json', function(object) {
    object.scale.multiplyScalar(5);
    scene.add(object);
});
json静态模型 - Object类型

下面给出两种数据类型的区别:


Geometry类型

Object类型

2.2、动画

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动画

说到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、外部载入纹理

官方文档 - MeshPhongMaterial

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);
obj静态模型 - 外部载入纹理

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);
});

obj静态模型 - obj+mtl

一开始打算用obj+mtl方法加载obj模型,但由于项目是tga格式贴图,参考大佬的代码,却怎么都显示不了纹理,最后放弃转而琢磨出第一种方法来。
后来偶然看到THREE.Loader.Handlers.add(/\.tga$/i, new THREE.TGALoader());这句代码。经实践,果然能载入某些不常用格式的纹理材质。




2018.07.13更新

1、fbx文件1.2、动画解决方案中新增加了fbx低版本转高版本的方法



2019.03.20更新

github项目地址



以上就是自己总结的一些经验,欢迎小伙伴留言讨论ε=ε=(ノ≧∇≦)ノ

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,504评论 4 365
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,898评论 1 300
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,218评论 0 248
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,322评论 0 214
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,693评论 3 290
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,812评论 1 223
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,010评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,747评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,476评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,700评论 2 251
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,190评论 1 262
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,541评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,206评论 3 240
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,129评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,903评论 0 199
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,894评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,748评论 2 274

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,631评论 4 59
  • 宝的入学问题已经尘埃落定——终于步入了理想的学校。非常感谢帮助过我的人,内心说不出的喜悦,终究为了孩子能,又尽了自...
    lesinlee阅读 255评论 0 0
  • 回首往事我无言以对 今夜,所有影子飞回我身边 在月光下排着队向我讨债 时光这一把梳子 地摊上的廉价货 在脑海里梳头...
    子健阅读 652评论 0 1
  • 首先,我希望标题被你当做一个疑问句或是设问来读,而不是咄咄逼人的反问句。 晚上,坐着校车从一个校区回到另一个校区,...
    cogitoergo_sum阅读 459评论 0 3
  • 很多爸爸们在知道了自己在家庭教育中的重要作用之后,也非常想容易其中,可是他们发现自己融入不进去。 我曾经遇到一位父...
    爱家心理阅读 449评论 0 1