three.js官方案例(animation / skinning / morph)webgl_animation_skinning_morph.html学习记录

目录

1 states部分

2 emotes部分

3 expressions部分

3.1 .morphTargetDictionary : Object


console.log(animations);

1 states部分

 const states = [ 'Idle', 'Walking', 'Running', 'Dance', 'Death', 'Sitting', 'Standing' ];//动作状态

2 emotes部分

const emotes = [ 'Jump', 'Yes', 'No', 'Wave', 'Punch', 'ThumbsUp' ];//其他动作表情

3 expressions部分

3.1 .morphTargetDictionary : Object

基于morphTarget.name属性的morphTargets字典。 默认情况下是未定义的,但是会被updateMorphTargets重建。

<!DOCTYPE html>
<html lang="en">
	<head>
		<title>three.js webgl - skinning and morphing</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
		<link type="text/css" rel="stylesheet" href="main.css">
		<style>
			body {
				color: #222;
			}

			a {
				color: #2fa1d6;
			}

			p {
				max-width: 600px;
				margin-left: auto;
				margin-right: auto;
				padding: 0 2em;
			}
		</style>
	</head>

	<body>
		<div id="info">
			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> webgl - skinning and morphing<br />
			<p>
				The animation system allows clips to be played individually, looped, or crossfaded with other clips. This example shows a character looping in one of several base animation states, then transitioning smoothly to one-time actions. Facial expressions are controlled independently with morph targets.
			</p>
			Model by
			<a href="https://www.patreon.com/quaternius" target="_blank" rel="noopener">Tomás Laulhé</a>,
			modifications by <a href="https://donmccurdy.com/" target="_blank" rel="noopener">Don McCurdy</a>. CC0.<br />
		</div>

		<script type="importmap">
			{
				"imports": {
					"three": "../build/three.module.js",
					"three/addons/": "./jsm/"
				}
			}
		</script>

		<script type="module">

			import * as THREE from 'three';

			import Stats from 'three/addons/libs/stats.module.js';
			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

			import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

			let container, stats, clock, gui, mixer, actions, activeAction, previousAction;
			let camera, scene, renderer, model, face;

			const api = { state: 'Walking' };

			init();
			animate();

			function init() {

				container = document.createElement( 'div' );
				document.body.appendChild( container );

				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.25, 100 );
				camera.position.set( - 5, 3, 10 );
				camera.lookAt( 0, 2, 0 );

				scene = new THREE.Scene();
				scene.background = new THREE.Color( 0xe0e0e0 );
				scene.fog = new THREE.Fog( 0xe0e0e0, 20, 100 );

				clock = new THREE.Clock();

				// lights

				const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x8d8d8d, 3 );
				hemiLight.position.set( 0, 20, 0 );
				scene.add( hemiLight );

				const dirLight = new THREE.DirectionalLight( 0xffffff, 3 );
				dirLight.position.set( 0, 20, 10 );
				scene.add( dirLight );

				// ground

				const mesh = new THREE.Mesh( new THREE.PlaneGeometry( 2000, 2000 ), new THREE.MeshPhongMaterial( { color: 0xcbcbcb, depthWrite: false } ) );
				mesh.rotation.x = - Math.PI / 2;
				scene.add( mesh );

				const grid = new THREE.GridHelper( 200, 40, 0x000000, 0x000000 );
				grid.material.opacity = 0.2;
				grid.material.transparent = true;
				scene.add( grid );

				// model

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

					model = gltf.scene;
					scene.add( model );
                    //重点就是这个方法
					createGUI( model, gltf.animations );

				}, undefined, function ( e ) {

					console.error( e );

				} );

				renderer = new THREE.WebGLRenderer( { antialias: true } );
				renderer.setPixelRatio( window.devicePixelRatio );
				renderer.setSize( window.innerWidth, window.innerHeight );
				container.appendChild( renderer.domElement );

				window.addEventListener( 'resize', onWindowResize );

				// stats
				stats = new Stats();
				container.appendChild( stats.dom );

			}

			function createGUI( model, animations ) {

				const states = [ 'Idle', 'Walking', 'Running', 'Dance', 'Death', 'Sitting', 'Standing' ];//动作状态
				const emotes = [ 'Jump', 'Yes', 'No', 'Wave', 'Punch', 'ThumbsUp' ];//其他动作表情
console.log(animations);
				gui = new GUI();//ui

				mixer = new THREE.AnimationMixer( model );//混合器

				actions = {};

				for ( let i = 0; i < animations.length; i ++ ) {

					const clip = animations[ i ];
					//返回所传入的剪辑参数的AnimationAction, 根对象参数可选,默认值为混合器的默认根对象。第一个参数可以是动画剪辑(AnimationClip)对象或者动画剪辑的名称。
					const action = mixer.clipAction( clip );
					actions[ clip.name ] = action;//动作
                     //设置只执行一次
					if ( emotes.indexOf( clip.name ) >= 0 || states.indexOf( clip.name ) >= 4 ) {
//.clampWhenFinished : Boolean
//如果 clampWhenFinished 值设为true, 那么动画将在最后一帧之后自动暂停(paused)
//如果 clampWhenFinished 值为false, enabled 属性值将在动作的最后一次循环完成之后自动改为false, 那么这个动作以后就不会再执行。
//默认值为false
//说明: 动作如果被中断了,clampWhenFinished将无效 (只有当最后一次循环执行完毕之后才能起效)
						action.clampWhenFinished = true;
						action.loop = THREE.LoopOnce;//THREE.LoopOnce - 只执行一次
					}

				}

				// states  状态 切换对应动作
 
				const statesFolder = gui.addFolder( 'States动作状态' );

				const clipCtrl = statesFolder.add( api, 'state' ).options( states );//选择

				clipCtrl.onChange( function () {

					fadeToAction( api.state, 0.5 );

				} );

				statesFolder.open();

				// emotes  表情

				const emoteFolder = gui.addFolder( 'Emotes表情' );

				function createEmoteCallback( name ) {

					api[ name ] = function () {

						fadeToAction( name, 0.2 );

						mixer.addEventListener( 'finished', restoreState );//监听时间

					};

					emoteFolder.add( api, name );

				}
                //动作的结束
				function restoreState() {

					mixer.removeEventListener( 'finished', restoreState );

					fadeToAction( api.state, 0.2 );

				}
                
				//添加 emotes的ui部分
				for ( let i = 0; i < emotes.length; i ++ ) {

					createEmoteCallback( emotes[ i ] );

				}
				emoteFolder.open();

				// expressions 表达

				face = model.getObjectByName( 'Head_4' );//模型里找到某个对象
                console.log(face);
                //.morphTargetDictionary基于morphTarget.name属性的morphTargets字典。 默认情况下是未定义的,但是会被updateMorphTargets重建
				const expressions = Object.keys( face.morphTargetDictionary );
				const expressionFolder = gui.addFolder( 'Expressions 情绪表达' );//Ui添加Expressions部分

				for ( let i = 0; i < expressions.length; i ++ ) {

					expressionFolder.add( face.morphTargetInfluences, i, 0, 1, 0.01 ).name( expressions[ i ] );

				}

				activeAction = actions[ 'Walking' ];
				activeAction.play();

				expressionFolder.open();

			}

			function fadeToAction( name, duration ) {

				previousAction = activeAction;//前一个动画
				activeAction = actions[ name ];//将要执行的动画

				if ( previousAction !== activeAction ) {

					previousAction.fadeOut( duration );//在传入的时间间隔内,逐渐将此动作的权重(weight)由1降至0

				}

				activeAction
					.reset()//重置动作
					.setEffectiveTimeScale( 1 )//设置时间比例(timeScale)以及停用所有的变形
					.setEffectiveWeight( 1 )//设置权重(weight)以及停止所有淡入淡出
					.fadeIn( duration )//在传入的时间间隔内,逐渐将此动作的权重(weight)由0升到1
					.play();//让混合器激活动作

			}

			function onWindowResize() {

				camera.aspect = window.innerWidth / window.innerHeight;
				camera.updateProjectionMatrix();

				renderer.setSize( window.innerWidth, window.innerHeight );

			}

			//

			function animate() {

				const dt = clock.getDelta();

				if ( mixer ) mixer.update( dt );//动画混合器更新

				requestAnimationFrame( animate );

				renderer.render( scene, camera );

				stats.update();

			}

		</script>

	</body>
</html>

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hemy1989

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值