+
95
-

回答

包括嘴巴说话,眨眼及手势动作都可以进行控制。

6688766fcd193.gif

完整代码如下:

点击查看全文

<!DOCTYPE html>

<html>
	<head>
		<meta charset="utf-8" />
	
		<meta
			name="viewport"
			content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
		/>
		<style>
			body {
				margin: 0;
			}
			canvas {
				display: block;
			}
		</style>
	</head>

	<body>
		<script type="importmap">
  {
    "imports": {
      "three": "//repo.bfw.wiki/bfwrepo/js/module/three/build/164/three.module.js",
      "three/addons/": "//repo.bfw.wiki/bfwrepo/js/module/three/examples/164/jsm/",
      "@pixiv/three-vrm": "//repo.bfw.wiki/bfwrepo/js/three-vrm.module.3.0.0.js"
    }
  }
		</script>

		<script type="module">
			import * as THREE from 'three';
			import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
			import { VRMLoaderPlugin, VRMUtils } from '@pixiv/three-vrm';

			// renderer
			const renderer = new THREE.WebGLRenderer();
			renderer.setSize( window.innerWidth, window.innerHeight );
			renderer.setPixelRatio( window.devicePixelRatio );
			document.body.appendChild( renderer.domElement );

			// camera
			const camera = new THREE.PerspectiveCamera( 30.0, window.innerWidth / window.innerHeight, 0.1, 20.0 );
			camera.position.set( 0.0, 1.0, 5.0 );

			// camera controls
			const controls = new OrbitControls( camera, renderer.domElement );
			controls.screenSpacePanning = true;
			controls.target.set( 0.0, 1.0, 0.0 );
			controls.update();

			// scene
			const scene = new THREE.Scene();

			// light
			const light = new THREE.DirectionalLight( 0xffffff, Math.PI );
			light.position.set( 1.0, 1.0, 1.0 ).normalize();
			scene.add( light );

			// gltf and vrm
			let currentVrm = undefined;
			let currentMixer = undefined;
			const loader = new GLTFLoader();
			loader.crossOrigin = 'anonymous';

			loader.register( ( parser ) => {

				return new VRMLoaderPlugin( parser );

			} );

			loader.load(

				'//repo.bfw.wiki/bfwrepo/threemodel/girl.vrm',

				( gltf ) => {

					const vrm = gltf.userData.vrm;

					// calling these functions greatly improves the performance
					VRMUtils.removeUnnecessaryVertices( gltf.scene );
					VRMUtils.removeUnnecessaryJoints( gltf.scene );

					// Disable frustum culling
					vrm.scene.traverse( ( obj ) => {

						obj.frustumCulled = false;

					} );

					scene.add( vrm.scene );
					currentVrm = vrm;
					prepareAnimation( vrm );
					console.log( vrm );

				},

				( progress ) => console.log( 'Loading model...', 100.0 * ( progress.loaded / progress.total ), '%' ),

				( error ) => console.error( error )

			);

			// animation
			function prepareAnimation( vrm ) {

currentMixer = new THREE.AnimationMixer(vrm.scene);

// 创建嘴巴说话的动画轨道
const mouthOpenTrack = new THREE.NumberKeyframeTrack(
    vrm.expressionManager.getExpressionTrackName('aa'),
    [0.0, 0.5, 1.0],
    [0.0, 1.0, 0.0]
);

// 调整眨眼动画轨道的时间值以降低眨眼速度
const blinkTrack = new THREE.NumberKeyframeTrack(
    vrm.expressionManager.getExpressionTrackName('blink'),
    [0.0, 6.0, 6.0],
    [0.0, 1.0, 0.0]
);

// 创建左上臂动画(略微放下)
const leftUpperArmTrack = new THREE.QuaternionKeyframeTrack(
    vrm.humanoid.getNormalizedBoneNode('leftUpperArm').name + '.quaternion',
    [0.0],
    [
        ...new THREE.Quaternion().setFromEuler(new THREE.Euler(6, -4, -4)).toArray()
        
      
    ]
);

// 创建右上臂动画(略微放下)
const rightUpperArmTrack = new THREE.QuaternionKeyframeTrack(
    vrm.humanoid.getNormalizedBoneNode('rightUpperArm').name + '.quaternion',
    [0.0],
    [
        ...new THREE.Quaternion().setFromEuler(new THREE.Euler(6, 10, 10)).toArray()
    ]
);

// 创建左小臂演讲手势动画
const leftLowerArmGesture = new THREE.QuaternionKeyframeTrack(
    vrm.humanoid.getNormalizedBoneNode('leftLowerArm').name + '.quaternion',
    [0.0, 5.0, 5.0],
    [
        ...new THREE.Quaternion().setFromEuler(new THREE.Euler(11, -21, 0)).toArray(),
        ...new THREE.Quaternion().setFromEuler(new THREE.Euler(11,-21, 1)).toArray(),
        ...new THREE.Quaternion().setFromEuler(new THREE.Euler(11, -21, 0)).toArray()
    ]
);

// 创建右小臂演讲手势动画
const rightLowerArmGesture = new THREE.QuaternionKeyframeTrack(
    vrm.humanoid.getNormalizedBoneNode('rightLowerArm').name + '.quaternion',
    [0.0, 1.0, 2.0],
    [
        ...new THREE.Quaternion().setFromEuler(new THREE.Euler(-0.5, 0, 0)).toArray(),
        ...new THREE.Quaternion().setFromEuler(new THREE.Euler(-0.8, 0, 0)).toArray(),
        ...new THREE.Quaternion().setFromEuler(new THREE.Euler(-0.5, 0, 0)).toArray()
    ]
);

// 创建一个新的动画剪辑,包含所有动画轨道
const clip = new THREE.AnimationClip('Animation', 2.0, [
    mouthOpenTrack, 
    blinkTrack, 
    leftUpperArmTrack,
    rightUpperArmTrack,
    leftLowerArmGesture, 
    rightLowerArmGesture
]);

const action = currentMixer.clipAction(clip);
action.play();

			}

			// helpers
			const gridHelper = new THREE.GridHelper( 10, 10 );
			scene.add( gridHelper );

			const axesHelper = new THREE.AxesHelper( 5 );
			scene.add( axesHelper );

			// animate
			const clock = new THREE.Clock();

			function animate() {

				requestAnimationFrame( animate );

				const deltaTime = clock.getDelta();

				if ( currentVrm ) {

					currentVrm.update( deltaTime );

				}

				if ( currentMixer ) {

					currentMixer.update( deltaTime );

				}

				renderer.render( scene, camera );

			}

			animate();
		</script>
	</body>
</html>

网友回复

我知道答案,我要回答