💃
Three.js T-pose モデルにアニメーションを適用する。
Three.js を使って、T-pose モデルにアニメーションをブレンドして再生する方法です。
初歩的な内容なのに、なかなか情報が見つからなかったので、メモとして残しておきます。
※フレームワークにNext.js AppRouterを使用しています。
DEMO
Mixamo
モデルとアニメーションの配布場所Vangurad By T.Choonyung
DOWNLOAD SETTING
- Format: FBX
- Pose: T-Pose
保存名:Vanguard.fbx
Sitting Animation
DOWNLOAD SETTING
- Format: FBX
- Skin: Without Skin
保存名:Vanguard@Sitting.fbx
FBXファイルでも、glTFファイルでも、Three.js で読み込むことができますが、今回は FBX ファイルを使用します。
ソースコードの解説
ダウンロードした FBX ファイルは public/models
フォルダを作成して、modelsフォルダに保存してください。
//model loader
const fbxloader: FBXLoader = new FBXLoader();
let mixer: THREE.AnimationMixer;
fbxloader.load('models/Vanguard.fbx', (object) => {
object.position.set(0, 0, 0);
object.scale.set(.05,.05,.05);
//Animation loader
const anim = new FBXLoader();
anim.load('models/Vanguard@Sitting.fbx', (anim) => {
mixer = new THREE.AnimationMixer(object);
mixer.clipAction(anim.animations[0]).play();
});
scene.add(object);
});
このプログラムを実行すると、3Dモデルのキャラクターに、アニメーションが適用されます。アニメーションは、FBXLoader を使用して読み込まれ、AnimationMixer を使用して再生されます。
const animate = () => {
requestAnimationFrame(animate);
const delta = clock.getDelta();
if ( mixer ) mixer.update( delta );
renderer.render(scene, camera);
};
if ( mixer ) mixer.update( delta );
により、Three.jsのシーンが連続的に更新され、アニメーションが再生されます。
ソースコード全文
'use client'
import { useEffect, useRef} from 'react';
import * as THREE from 'three';
import { FBXLoader } from 'three/examples/jsm/Addons.js';
export default function Home() {
const ref: React.RefObject<HTMLCanvasElement> = useRef<HTMLCanvasElement>(null);
useEffect(() => {
// camera size settings
const cameraViewSize = {
width: 1000,
height: 800,
}
//scene
const scene: THREE.Scene = new THREE.Scene();
//Clock
const clock = new THREE.Clock();
//camera
const camera: THREE.PerspectiveCamera = new THREE.PerspectiveCamera(
75,
cameraViewSize.width / cameraViewSize.height,
0.1,
1000
);
camera.position.set(0, 5, 13);
//renderer
const renderer: THREE.WebGLRenderer = new THREE.WebGLRenderer({
canvas: ref.current??undefined,
antialias: true,
alpha: true,
});
renderer.setSize(cameraViewSize.width, cameraViewSize.height);
//light
const ambientLight: THREE.AmbientLight = new THREE.AmbientLight(0xffffff, 5);
scene.add(ambientLight);
const pointLight: THREE.PointLight = new THREE.PointLight(0xffffff, 100);
scene.add(pointLight);
//grid (本番環境ではコメントアウトすること)
const gridHelper: THREE.GridHelper = new THREE.GridHelper(100, 100);
scene.add(gridHelper);
//model loader
const fbxloader: FBXLoader = new FBXLoader();
let mixer: THREE.AnimationMixer;
fbxloader.load('models/Vanguard.fbx', (object) => {
object.position.set(0, 0, 0);
object.scale.set(.05,.05,.05);
const anim = new FBXLoader();
anim.load('models/Vanguard@Sitting.fbx', (anim) => {
mixer = new THREE.AnimationMixer(object);
mixer.clipAction(anim.animations[0]).play();
});
scene.add(object);
});
//Animation
const animate = () => {
requestAnimationFrame(animate);
const delta = clock.getDelta();
if ( mixer ) mixer.update( delta );
renderer.render(scene, camera);
};
animate();
}, []);
return (
<main className='h-screen'>
<canvas ref={ref}/>
</main>
);
}
Discussion