使用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 中的动画,它可以管理单个动作,让这个动作开始暂停等。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐