three.js morphTargets使用总结

MorophTargets 学习总结

MorphTargets 用处

原理上同Maya中的BlendShape。通过直接使模型的每个点位置发生改变,适用于细节变化的时候(变化非常细节时,用骨架难以操控,不然要设置非常多个骨架,不然就不够真实)。
通常是用来做人体模型的表情时使用的。展示效果如下:

图1
图1

图2
图2

图3
图3

图4
图4

具体使用方法参考实例

参考示例:three.js-r79/examples/webgl_morphtargets.html

Morph的基本原理

The value is a scalar that determines the effect of a morph target. A morph target is another list of verticies (same length) that go along side the original list of verticies. Say we have a list of size 2 (a line),
var list1 = [0.1, -0.2]
and a morph target:
var list2 = [0.2, -0.3]
the scalar value is used like so:
finalVertexPosition = list1[0] + (list2[0] * scalar);
Its mainly used for animation (to transition from key to key).

引用自:http://stackoverflow.com/questions/30960854/three-js-morphtargets
由上文可以很容易的理解二维层次的morph,在三维层次也是同样的原理。

Morph通常被用作动画

Indeed, skeletal animation takes slightly more processing, because the bones’ matrices must be calculated (in JS) in every frame and uploaded to the GPU. While with morph animation, all interpolation can be done GPU-side. So it is really a memory-performance trade.
When you do have many animations playing at the same time, the processing will indeed have some cost. At the same time, however, several different morph animations will have big filesizes.
As you mentioned.
So it depends on case. There are some fine points you should take note of:

  • Morph animations can be stored with reduced frames per second if the animation isn’t too fast or complex, so that you will have smaller files.
  • Skeletal animations have bone information, that you can use to attach things to your model, e.g. you have a soldier model that you want to attach different guns to. With morph animation, you can’t do this, so you’d have to animate both the soldier and also every single gun.
  • You can actually use both types of animation in the same application, choosing the most adequate for each use.
  • SkinnedMesh-es, contrary to MorphAnimMesh-es, currently have no support for raycasting. If you need raycasting, e.g. for selecting objects, you will need to use morph animation for the time being.

上文引用自http://stackoverflow.com/questions/26431117/three-js-animation-skeleton-skinning-vs-morph
由上文可以理解,morph 文件大,加载慢,但实际在网页上计算量小。骨骼动画文件小的多,但在网页上运行要耗费较大的计算量,如果有很多个模型同时播放骨骼动画,会给使用的机器带来不小的压力。

morphTargets初始化的两种方式

Json 模型文件中初始化

文件中初始化morphTargets
图5 文件中初始化morphTargets

由上图中可见,在json文件中直接将morphTargets的数据安置好,这个通常是使用blender建模后直接生成的,使用起来较为方便。

将模型和morph的数据分成多个文件

如果你没有使用blender建模,也不会把你的多个模型合成在同一个文件里,你可以使用一种比较笨的方法:先把模型全部加载进来,再把想用作morph的模型的vertices拷贝出来,给想用作主要的模型做morphTargets的点集。

正确的初始化流程如下:

正确的morphTargets流程
图6 正确的morphTargets流程

主要代码流程如下:

首先,先加载主要模型:
var loader = new THREE.JSONLoader();
loader.load( url, function( geometry, mat ){        // 加载模型
    body = new THREE.SkinnedMesh( geometry, body_mat );
    body.name = "body";                 // 设置名称
    console.log('body 加载完成');
});
第二步,加载morph模型
var morph = [];                     // 储存morphTargets
// 加载 用作morph的模型
morph_loader.load( morph_url, function( geometry, material ){
    morph.push({ name:"chang", vertices:geometry.vertices });   // 为morph 数组追加
    load_finished();        // 加载完成后统一执行
});
第三步,利用morph和原始模型新建一个模型
// 下面为load_finished的主要内容
var body_mat = body.material.clone();           // 获得原始头部的材质
body_mat.morphTargets = true;                   // 开始材质的morphTargets功能
var body_geometry = body.geometry.clone();      // 获得原始geometry
body_geometry.morphTargets = morph;             // 给geometry追加morph数组

// 建立新的模型
var new_body = new THREE.SkinnedMesh( body_geometry, body_mat);
这个思路应该回避的一种做法

我最开始,从Mesh源码中发现了这么一个函数updateMorphTargets() ,经判断,他是用来给mesh初始化Morph的函数。于是我测试时,采用了这样的流程:

图 7 错误的morphTargets初始化流程
图 7 错误的morphTargets初始化流程

但出现了bug,很奇怪的bug。滑动进度条,模型没按预想的方向变化,反而变得特别小,缩成了一个点。

图 8 错误流程的效果示意图
图 8 错误流程的效果示意图

图 9 错误流程的效果示意图
图 9 错误流程的效果示意图

经过分析,发现问题出现在了WebGLRenderer上,这个Renderer,在每个模型第一次添加进来会对模型进行初始化,如果对模型的局部进行修改后,Renderer会对模型进行更新。然而更新部分的函数可能没有初始化那么详细,所以关于Morph部分的更新代码做的并不好,所以,我们重新建了一个模型,让Renderer为模型进行一次初始化,保证morph能得到正确的初始化。

操作变形

操作变形的代码比较简单,只需要设置对应的morph的权重数值就好,数值范围最好是0 到 1 。

body.morphTargetInfluences[ head.getMorphTargetIndexByName( "chang" ) ] = 0.5;

最后

这是近几日关于morph的研究,可能理解的不是特别详细,欢迎补充。

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值