【VRMA正式リリース】 three-vrm による VRMアニメーション を試す
「three-vrm」による VRMアニメーションを試したので、まとめました。
1. はじめに
2024年2月21日、VRM用のアニメーションファイル「VRMA」が正式リリースしました。「VRMA」を利用することで、人型モデル向けアニメーションをアプリやモデルをまたいで共通フォーマットとして利用できます。
そこで今回は、「three-vrm」による VRMアニメーションを試していみます。「three-vrm」は「three.js」ベースの3Dグラフィックスライブラリで、Webブラウザ上でVRMを表示できるようになります。
2. VRMの準備
今回は、「VRoid Studio」でオリジナルモデルを作成して利用します。
(1) 「VRoid Studio」をサイトからダウンロードしてインストール。
(2) 「新規作成」をクリック。
(3) 「ベース」を選択。
(4) 各種項目を設定。
(5) 右上のシェアボタンで、「VRMエクスポート」を選択。
今回は、「model.vrm」という名前で出力しました。
3. VRMAの準備
今回は、「VRoid」公式で提供されてる「VRMアニメーション7種セット(.vrma)」を使います。
(1) 「BOOTH」の「VRMアニメーション7種セット(.vrma)」のページを開いてダウンロード。
4. プロジェクトの準備
プロジェクトの準備手順は、次のとおりです。
(1) 「Node.js」のインストール。
(2) 「LiveServer」の準備。
$ npm i -g live-server
(3) プロジェクトの作成。
$ mkdir helloworld
$ cd helloworld
$ npm init -y
(4) TypeScriptの準備。
$ npm i -D webpack webpack-cli typescript ts-loader
$ npx tsc --init
(5) パッケージのインストール。
「three-vrm」に加えて「three-vrm-animation」もインストールしています。
$ npm i -S three @pixiv/three-vrm
$ npm i -S @pixiv/three-vrm-animation
(6) 「package.json」の "scripts" を以下のように編集。
"scripts": {
"start": "live-server dist",
"build": "webpack",
"watch": "webpack -w"
},
5. VRMの表示
VRMの表示手順は、次のとおりです。
(1) 「src」フォルダを作成し、「index.ts」を作成して配置。
・src/index.ts
import * as THREE from "three"
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"
import { VRMLoaderPlugin } from "@pixiv/three-vrm";
window.addEventListener("DOMContentLoaded", () => {
// canvasの取得
const canvas = document.getElementById("canvas");
if (canvas == null) return;
// シーンの生成
const scene = new THREE.Scene();
// カメラの生成
const camera = new THREE.PerspectiveCamera(
30, canvas.clientWidth/canvas.clientHeight, 0.1, 20);
camera.position.set(0.0, 1.0, -4.0)
camera.rotation.set(0.0, Math.PI, 0.0)
// レンダラーの生成
const renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(canvas.clientWidth, canvas.clientHeight);
renderer.setClearColor(0x7fbfff, 1.0);
canvas.appendChild(renderer.domElement);
// ライトの生成
const light = new THREE.DirectionalLight(0xffffff, Math.PI);
light.position.set(1.0, 1.0, 1.0);
scene.add(light);
// ローダーの準備
const loader = new GLTFLoader();
loader.register((parser) => {
return new VRMLoaderPlugin(parser);
});
// VRMの読み込み
loader.load("./model.vrm",
// ロード時に呼ばれる
(gltf) => {
const vrm = gltf.userData.vrm;
scene.add(vrm.scene);
console.log(vrm);
},
// プログレス時に呼ばれる
(progress) => console.log(
"Loading model...",
100.0 * (progress.loaded / progress.total), "%"
),
// エラー時に呼ばれる
(error) => console.error(error),
);
// フレーム毎に呼ばれる
const update = () => {
requestAnimationFrame(update);
renderer.render(scene, camera);
}
update();
})
(2) 「dist」フォルダを作成し、「model.vrm」を配置。
(3) ビルドと実行。
$ npm run build
$ npm run start
6. VRMのアニメーション
VRMのアニメーション表示手順は、次のとおりです。
(1) 「src」フォルダを作成し、「index.ts」を作成して配置。
・src/index.ts
import * as THREE from "three"
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader"
import { VRMLoaderPlugin } from "@pixiv/three-vrm";
import { createVRMAnimationClip, VRMAnimationLoaderPlugin } from "@pixiv/three-vrm-animation";
window.addEventListener("DOMContentLoaded", () => {
// canvasの取得
const canvas = document.getElementById("canvas");
if (canvas == null) return;
// シーンの生成
const scene = new THREE.Scene();
// カメラの生成
const camera = new THREE.PerspectiveCamera(
30, canvas.clientWidth/canvas.clientHeight, 0.1, 20);
camera.position.set(0.0, 1.0, -4.0)
camera.rotation.set(0.0, Math.PI, 0.0)
// レンダラーの生成
const renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(canvas.clientWidth, canvas.clientHeight);
renderer.setClearColor(0x7fbfff, 1.0);
canvas.appendChild(renderer.domElement);
// ライトの生成
const light = new THREE.DirectionalLight(0xffffff, Math.PI);
light.position.set(1.0, 1.0, 1.0);
scene.add(light);
// VRM要素の準備
let currentVrm: any = undefined;
let currentVrmAnimation: any = undefined;
let currentMixer:any = undefined;
// ファイルの読み込み
function load(url: string) {
loader.load(
url,
// ロード時に呼ばれる
(gltf) => {
tryInitVRM(gltf);
tryInitVRMA(gltf);
},
// プログレス時に呼ばれる
(progress) => console.log(
"Loading model...",
100.0 * (progress.loaded / progress.total), "%"
),
// エラー時に呼ばれる
(error) => console.error(error)
);
}
// VRMの読み込み
function tryInitVRM(gltf: any) {
const vrm = gltf.userData.vrm;
if ( vrm == null ) {
return;
}
currentVrm = vrm;
scene.add(vrm.scene);
initAnimationClip();
}
// VRMAの読み込み
function tryInitVRMA(gltf: any) {
const vrmAnimations = gltf.userData.vrmAnimations;
if (vrmAnimations == null) {
return;
}
currentVrmAnimation = vrmAnimations[0] ?? null;
initAnimationClip();
}
// オーディオクリップの初期化
function initAnimationClip() {
if (currentVrm && currentVrmAnimation) {
currentMixer = new THREE.AnimationMixer(currentVrm.scene);
const clip = createVRMAnimationClip(currentVrmAnimation, currentVrm);
currentMixer.clipAction(clip).play();
}
}
// ローダーの準備
const loader = new GLTFLoader();
loader.register((parser) => {
return new VRMLoaderPlugin(parser);
});
loader.register((parser) => {
return new VRMAnimationLoaderPlugin(parser);
});
// VRMとVRMAの読み込み
load("./model.vrm");
load("./VRMA_01.vrma");
// clockの準備
const clock = new THREE.Clock();
clock.start();
// フレーム毎に呼ばれる
const update = () => {
requestAnimationFrame(update);
const deltaTime = clock.getDelta();
if (currentMixer) {
currentMixer.update(deltaTime);
}
if (currentVrm) {
currentVrm.update(deltaTime);
}
renderer.render(scene, camera);
}
update();
})
(2) 「dist」フォルダを作成し、「VRMA_01.vrma」を配置。
(3) ビルドと実行。
$ npm run build
$ npm run start