0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

VRoidのモデルとモーションをThree.jsで動かすまでの道

0
Last updated at Posted at 2026-02-05

はじめに

(VRoidは)初投稿です。

今回は3Dキャラクターモデル作成ソフトVRoidStudioで作成したモデルに、ポージングデザインソフトVRM Posing Desktopでモーションを付け加えて、Three.jsでブラウザ上で表示させるまでのスリリング(個人感)なチャレンジを行い、その手順を簡潔にまとめようと思います。

実動作

以下で公開しています。

GtiHub Action - VRoidByThreejs

使用した機器情報

  • MacStudio2023(M2 Max)
  • macOS Tahoe(26.1)

モーション作成手順とツール紹介

1. VRoidStudioでモデル作成

  1. 公式サイトからツールのダウンロードしてインストール(無料)
  2. 動作確認のためにシンプルなデザインをテンプレートから選択
  3. 共有アイコンから「VRMエクスポート」で.vrmファイル出力

vroidstudio01.jpg

2. VRM Posing Desktopでモーション作成

VRM Posing Desktopは、ゲーム配信サービスのSteamで、有料で配信されています。(2026年2月現在では¥1,400)

起動までの流れ

  1. Steam公式からSteamアプリをダウンロードしてインストール(無料)
  2. Steamアカウントでログイン(登録は無料)
  3. VRMPosingDesktopのストアページへ移動(「vrmposing」で検索すれば見つかる)
  4. 購入のための情報を入力して、カートに入れてから購入決定
  5. 「ライブラリ」タグからツールのインストール
  6. Steamアプリから(重要)VRM Posing Desktopを起動

vrmposing01.jpg

ポーズからモーション作成

VRoidで作成したモデルは仕様が共通なので、モーション作成時にモデルが一致していなくても問題ないです。

  1. 「歯車」ボタンから設定を開いて「FPS」の数値を「60」にする
  2. 「ポーズ選択」ボタンを押してポーズ一覧から好みのポーズを選択
  3. 右側のウィンドウの「追加」ボタンを押して、モーションとして追加する
  4. ポーズを追加すると書くポーズの差分を補完してモーションが形成されていく
  5. 「再生」ボタンを押してモーションの確認
  6. ポーズ項目のスライダーで「次の」ポーズに移行するまでのフレーム数(1〜32)を調整

ポーズ組み合わせ例

注意点

  • VRMAはループ時に最後のポーズと最初のポーズとの保管を行わないため、最後には最初と同じポーズを登録しておく必要があります
0 1 2 3 4
pose01.jpg pose02.jpg pose03.jpg pose04.jpg pose05.jpg

エクスポート

モーションウィンドウ左下の「出し入れ」ボタンを押して、「エクスポート」から「.vrma」を選択してファイル出力します。

  • ツールを閉じるとモーションデータは保持されないので、「エクスポート」から「.vpdloop」を選択して保存もしたほうが良いです

ブラウザ表示手順とツール紹介

1. Node.jsでプロジェクト作成

今回はTypeScriptで書きました。

npm init -y
npm i -D vite typescript
npx tsc --init

2. VRMとVRMAの読み込み

インストール

npm install -S @pixiv/three-vrm @pixiv/three-vrm-animation

インポート

import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { VRMLoaderPlugin, VRMUtils, VRM } from '@pixiv/three-vrm';
import { createVRMAnimationClip, VRMAnimationLoaderPlugin, VRMAnimation } from "@pixiv/three-vrm-animation";

VRM系ファイルローダーの準備

const loader = new GLTFLoader();
loader.register((parser) => {
    return new VRMLoaderPlugin(parser);
});
loader.register((parser) => {
    return new VRMAnimationLoaderPlugin(parser);
});

VRM系ファイルの読み込み関数

function load(url: string) {
    loader.load(
        url,
        // ロード時に呼ばれる
        (gltf) => {
            const vrm = gltf.userData.vrm;
            const vrmAnimations = gltf.userData.vrmAnimations;
            // VRMが有効なら
            if (vrm) {
                tryInitVRM(vrm);
            }
            // VRMAが有効なら
            if (vrmAnimations) {
                tryInitVRMA(vrmAnimations);
            }
        },
        // プログレス時に呼ばれる
        (progress) => console.log(
            "Loading model...",
            100.0 * (progress.loaded / progress.total), "%"
        ),
        // エラー時に呼ばれる
        (error) => console.error(error)
    );
}

VRMインスタンスをシーンに追加する関数

function tryInitVRM(vrm: VRM) {
    currentVrm = vrm;
    scene.add(vrm.scene);
    VRMUtils.rotateVRM0(vrm); // VRM0.x読み込み時の回転補正(必要であれば)
    initAnimationClip();
}

VRMAインスタンスを保持する関数

function tryInitVRMA(vrmAnimations: VRMAnimation[]) {
    currentVrmAnimation = vrmAnimations[0] ?? null;
    initAnimationClip();
}

VRMとVRMAをミキシングする関数

function initAnimationClip() {
    if (currentVrm && currentVrmAnimation) {
        currentMixer = new THREE.AnimationMixer(currentVrm.scene);
        const clip = createVRMAnimationClip(currentVrmAnimation, currentVrm);
        currentAction = currentMixer.clipAction(clip);
        currentAction.play();
        setupGUI(currentAction, currentMixer);
    }
}

ファイル読み込み

load("./models/SimpleGirl02.vrm");
load("./models/vpdloop_kick01.vrma");

3. 表示処理

// clockの準備
const clock = new THREE.Clock();
clock.start();

// アニメーションループ
function animate() {
    requestAnimationFrame(animate);
    // モーション更新
    const deltaTime = clock.getDelta();
    if (currentMixer) {
        if (!settings.paused) {
            currentMixer.update(deltaTime);
            if (currentAction) {
                settings.time = currentAction.time;
            }
        }
    }
    // VRMの更新
    if (currentVrm) {
        currentVrm.update(deltaTime);
    }
    // レンダリング
    renderer.render(scene, camera);
}
animate();

4. その他

カメラ制御はOrbitControlsを使用

import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
...
const controls = new OrbitControls(camera, renderer.domElement);
// 自動回転設定
controls.autoRotate = true;
controls.autoRotateSpeed = 4.0;
// 注視点設定
controls.target.set(0, 1.0, 0);
controls.update();

function animate() {
    requestAnimationFrame(animate);

    controls.update(); // 自動回転のためにフレーム実行
    ...
}

モーション時間操作にlil-guiを使用

npm install lil-gui
import GUI from 'lil-gui'
...
function setupGUI(action: THREE.AnimationAction, mixer: THREE.AnimationMixer) {
    if (!action) return;
    // モーションの長さを取得
    const clip = action.getClip();
    const duration = clip.duration;
    // ポーズ|レジュームボタン
    gui.add(settings, 'togglePause').name('Pause | Resume');
    // モーション時間指定スライダー
    gui.add(settings, 'time', 0, duration).name('Time').listen().onChange((t: number) => {
        if (action) {
            action.time = t;
            // 停止中もタイムスライダーを動かしたときは強制的にポーズ状態として更新
            settings.paused = true;
            // アニメーションを即座に反映
            if (mixer) {
                mixer.update(0);
            }
        }
    });
}

コード全体

GitHub - VRoidByThreejs

GitHub Pages での公開方法

大枠はTypeScript × Vite × GitHub Pages 超入門に従っています。

  1. プロジェクトのカレントに.github/workflows/deploy.ymlファイルを新規作成
  2. deploy.ymlの内容はここ
  3. カレントにvite.config.tsファイルを新規作成
  4. vite.config.tsの内容はここ
    1. 記述<repo>はGitのリポジトリ名を記載
  5. 変更をPushして、GitHubプロジェクトの Setting -> Pages の Build and deployment で GitHub Actions を選択

今後の展望

  • 複数のモーションの切り替え
  • 背景やオブジェクトの追加
  • モーションから独立したモデル操作

参考文献

あとがき

自分語りになりますが、まだ社会人になる30年以上前に出会ったゲーム、「バーチャファイター(セガ)」に大変衝撃を受けまして、当時は3DCG黎明期でプリレンダーなら映像作品で広まり始めてましたが、ゲームのようなインタラクティブなものはデザインもスピードも不満だらけだった時代に、ゲームの超絶進化を感じ取って自分もゲーム開発の道を歩む事を決めた歴史的瞬間でした。

そして現在、高品質な3Dキャラクターはもとよりモーションまでもが個人でも作成可能で、しかもプログラムでも制御可能な所まで来ているのは本当に感慨深いです。

ちなみにバーチャファイターが動作するシステム「MODEL1」の性能の一部をウィキペディアから抜粋しました。正直この性能で60fpsで機敏に動いているのは魔法か何かかと今でも思わざるを得ません。

  • CPU
    • V60(32bit) @ 16MHz
  • 数値演算プロセッサ
    DSP(32bit) @ 40MHz
    3Dマトリックス・軸回転・浮動小数点演算機能搭載
  • RAM
    1MB(ROM:47MB)

おわりに

ここまでご閲覧いただきありがとうございます。

今後も思いつくままに記事投稿を続けて行きたい所存であります。

功夫が足りないわ!

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?