💃

Three.js T-pose モデルにアニメーションを適用する。

2024/05/18に公開

Three.js を使って、T-pose モデルにアニメーションをブレンドして再生する方法です。
初歩的な内容なのに、なかなか情報が見つからなかったので、メモとして残しておきます。
※フレームワークにNext.js AppRouterを使用しています。

DEMO

sitting_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>
  );
}

GitHubで編集を提案

Discussion