three.js进阶之骨骼绑定
使用three.js也能实现3D模型的骨骼绑定,使用代码控制模型!参考这里的呈现效果。
更加常见的应用场景应该是:给一个模型设置多套骨骼动画如唱、跳、Rap等,然后在浏览器中根据用户的输入选择执行不同的动画,这就需要对骨骼动画进行管理,本文先基于three.js官网提供的demo学习其实现机制。
源码解析
这里参考官方提供的demo应用的实现源码,关键代码片段,我已经在代码中作了详细的注释:
const loader = new GLTFLoader();
loader.load( 'models/gltf/Xbot.glb', function ( gltf ) {
// 将模型加载到场景中
model = gltf.scene;
scene.add( model );
// 遍历模型,对其中的Mesh添加投射阴影
model.traverse( function ( object ) {
if ( object.isMesh ) object.castShadow = true;
} );
// 创建骨骼工具
skeleton = new THREE.SkeletonHelper( model );
// visible属性是类Object3D的,设置为false将不会被渲染
skeleton.visible = false;
// 将骨骼工具添加到场景中
scene.add( skeleton );
// 读取模型中的动画数据
const animations = gltf.animations;
// 创建AnimationMixer实例
mixer = new THREE.AnimationMixer( model );
// 读取动画的个数
numAnimations = animations.length;
// 遍历每一个动画
for ( let i = 0; i !== numAnimations; ++ i ) {
let clip = animations[ i ];
const name = clip.name;
/**
* 已经预定义了基础动作集合,包括未激活状态、走、跑,这三个动作
* const baseActions = {
* idle: { weight: 1 },
* walk: { weight: 0 },
* run: { weight: 0 }
* };
* 同时也预定义了额外的动作集合:
* const additiveActions = {
* sneak_pose: { weight: 0 },
* sad_pose: { weight: 0 },
* agree: { weight: 0 },
* headShake: { weight: 0 }
* };
*/
if ( baseActions[ name ] ) {
// 如果是基础动作,将其激活并保存到动作集合中
const action = mixer.clipAction( clip );
// 自定义函数,用于给动作
activateAction( action );
baseActions[ name ].action = action;
allActions.push( action );
} else if ( additiveActions[ name ] ) {
// 如果是附加的动画,使用AnimationUtils工具类将给定动画剪辑的关键帧转换为附加格式
THREE.AnimationUtils.makeClipAdditive( clip );
// 这是针对特定动作的定制化需求,可以忽略
if ( clip.name.endsWith( '_pose' ) ) {
// 创建新剪辑,仅保留2到3帧之间的内容,fps为30,
clip = THREE.AnimationUtils.subclip( clip, clip.name, 2, 3, 30 );
}
const action = mixer.clipAction( clip );
activateAction( action );
additiveActions[ name ].action = action;
allActions.push( action );
}
}
animate();
} );
function activateAction( action ) {
const clip = action.getClip();
const settings = baseActions[ clip.name ] || additiveActions[ clip.name ];
setWeight( action, settings.weight );
action.play();
}
function setWeight( action, weight ) {
action.enabled = true;
action.setEffectiveTimeScale( 1 );
action.setEffectiveWeight( weight );
}
下面是animate()
函数的代码:
function animate() {
// Render loop
requestAnimationFrame( animate );
for ( let i = 0; i !== numAnimations; ++ i ) {
const action = allActions[ i ];
const clip = action.getClip();
const settings = baseActions[ clip.name ] || additiveActions[ clip.name ];
settings.weight = action.getEffectiveWeight();
}
// Get the time elapsed since the last frame, used for mixer update
const mixerUpdateDelta = clock.getDelta();
// Update the animation mixer, the stats panel, and render this frame
mixer.update( mixerUpdateDelta );
stats.update();
renderer.render( scene, camera );
}
SkeletonHelper介绍
通过阅读上面的源码, 发现SkeletonHelper是使得模型能够运动起来的关键,在three.js官网中也给出了简短的介绍:
A helper object to assist with visualizing a Skeleton. The helper is rendered using a LineBasicMaterial.
翻译过来就是:这个辅助化工具能够使得骨骼可视化,使用LineBasicMaterial材质进行渲染。
属性
总而言之,这个工具类非常简单,只有如下三个属性:
-
bones : Array
当前模型的骨骼列表,这些骨骼将会以线性的形式渲染出来,如下图:
-
isSkeletonHelper : Boolean
这是一个只读的标志变量,用于检查传入的object是否为SkeletonHelper类型。 -
root : Object3D
该属性保存的是构造函数中传入的模型。
方法
该工具类的方法和 LineSegments类的方法保持一致,说明是继承自 LineSegments类,而 LineSegments是继承自Line类,因此其基本方法就是和Line的绘制相关的方法,这一块直接查看官方文档即可。
AnimationMixer
AnimationMixer能够控制播放模型中包含的动画,如果一个场景中有多个模型存在动画,需要使用多个AnimationMixer分别控制。
The AnimationMixer is a player for animations on a particular object in the scene. When multiple objects in the scene are animated independently, one AnimationMixer may be used for each object.
方法
clipAction (clip : AnimationClip, optionalRoot : Object3D) : AnimationAction
该方法将会返回一个AnimationAction对象。
AnimationClip
动画剪辑(AnimationClip)是一个可重用的关键帧轨道集,它表示一个对象的一个动作。
AnimationAction
AnimationActions 用来调度存储在 AnimationClips 中的动画,它可以管理单个动作,让这个动作开始暂停等。
更多推荐
所有评论(0)