概述

在 Three.js 中,动画蒙皮混合(Animation Skinning Blending)通常用于实现角色动画,其中角色的皮肤(Skin)被绑定到骨骼(Skeleton),并且可以通过混合不同的动画来实现平滑的过渡效果。

动画蒙皮混合的基本概念包括以下几个要点:

1. 骨骼动画(Skeleton Animation):在角色动画中,角色的皮肤(Skin)通常与一个骨骼(Skeleton)相关联。骨骼包含一组骨骼节点,这些节点定义了角色的姿势和动作。通过对骨骼节点应用动画,可以实现角色的动画效果。

2. 动画混合(Animation Blending):动画混合是指将多个动画混合在一起,以实现平滑的过渡效果。在角色动画中,您可以混合不同的动画片段(例如行走、奔跑、跳跃等),从而实现角色在不同动作之间的平滑过渡。

3. 权重(Weights):在动画混合过程中,每个动画片段都有一个权重值,用于控制该动画在最终混合结果中的影响程度。通过调整动画片段的权重值,可以实现动画之间的平滑过渡。

在 Three.js 中,您可以使用 AnimationMixer 和 AnimationAction 来控制动画的播放和混合。AnimationMixer 是动画混合器,用于管理角色的动画混合过程;AnimationAction 则代表一个动画动作,您可以创建多个 AnimationAction 来表示不同的动画片段,并通过调整它们的权重值来实现动画混合效果。

通过合理地使用 Three.js 的动画系统和蒙皮混合功能,您可以实现复杂的角色动画效果,包括平滑的动作过渡和自然的动画表现。如果您对动画蒙皮混合或 Three.js 动画系统的其他方面有更具体的问题,请随时告诉我,我将尽力为您提供帮助。

例子

以threejs官方的示例举例:(页面地址)

D9521021-5564-440A-A533-880AE52EF22B.png该模型内置有3个动画:idle、walk、run,下图是GUI控制台

在crossfading中,当我们点击任意一项

模型会丝滑的将动作过渡到下一段动画,如:从走到跑,从跑到站立。

在切换的过程中,我们可以清楚的看到,blend weights栏中的数值有变化:

这个数值就是动画丝滑切换的奥秘所在,通常我们切换动画的思路是:暂停上一段动画,播放下一段动画,这样的结果就是模型会回到初始状态,然后再继续播放;这样的操作会带来极大的卡顿,会显得很不自然。没有人在跑步的时候先原地站1秒。

为了解决这个问题我们将使用改变动画权重的方式,来丝滑的过渡动画切换。

此示例的源代码位于:Github

代码解析

打开threejs例子的代码,首先是构建基本的场景、相机、光照等信息,这里就不再赘述。

然后是加载一个预制动画的模型

const loader = new GLTFLoader();
loader.load( 'models/gltf/Soldier.glb', function ( gltf ) {
});

然后是解析出来模型的动画,然后将动画clipAction记录在actions数组中,

const animations = gltf.animations;

mixer = new THREE.AnimationMixer( model );
idleAction = mixer.clipAction( animations[ 0 ] );
walkAction = mixer.clipAction( animations[ 3 ] );
runAction = mixer.clipAction( animations[ 1 ] );

actions = [ idleAction, walkAction, runAction ];

然后调用activateAllActions方法,将动画的权重设置为对应值,并且播放所有动画。这里虽然播放了所有动画,由于权重的设置,只有权重不为0的动画才会播放出来。

function activateAllActions() {
	setWeight( idleAction, settings[ 'modify idle weight' ] );
	setWeight( walkAction, settings[ 'modify walk weight' ] );
	setWeight( runAction, settings[ 'modify run weight' ] );
	actions.forEach( function ( action ) {
		action.play();
	} );
}

然后通过crossFadeFrom、crossFadeTo、fadeIn、fadeOut来丝滑的切换动画

AC2A851E-9398-4A5B-A646-AE6312AE58D1.png

// crossFadeTo切换为例
const endAction=ClipAction
const startAction=ClipAction

endAction.time = 0
endAction.enabled = true
endAction.setEffectiveWeight(1)

startAction.crossFadeTo(endAction, 0.5, true)