商品サンプル画像
パパにさよなら
商品ページ
Amazon
収益広告(自動登録)
※Amazonレビュー要確認
商品サンプル画像
劇場版「鬼滅の刃」無限列車編
商品ページ
Amazon
収益広告(自動登録)
※Amazonレビュー要確認
商品サンプル画像
アシュリー、魔法はよいこになってから!(全3巻)
商品ページ
Amazon
非収益広告(手動登録)
商品サンプル画像
『ヤマトよ永遠に REBEL3199』 第三章 群青のアステロイド(レンタル版)
商品ページ
Amazon
収益広告(自動登録)
※Amazonレビュー要確認
商品サンプル画像
わんだふるぷりきゅあ!ざ・むーびー!ドキドキ♡ゲームの世界で大冒険!(Amazon.co.jp限定Tシャツ購入権利コード付き)(7/18 正午まで)
商品ページ
Amazon
収益広告(自動登録)
※Amazonレビュー要確認
記事の概要
three.js・three-vrm.jsを使用して、VRoidをWebページ上に表示する Part-02
作成日:2025-04-14
最終更新日:2025-04-15
記事の文字数:7753
VRoidWebツール情報技術
本記事のトピック
  • 概要
  • three-vrmのupdate処理をまず呼び出す
  • カメラ目線にする
  • ポーズの変更方法(vroidposeデータから変更する)
  • 表情の変更方法
  • 口の形の変更方法
  • 変なテクスチャが表示されてしまう問題
  • 解像度問題の対応
  • vrm.update処理の中身について
  • サンプルコードはこちら
  • vroidposeにまつわる問題点
three.js・three-vrm.jsを使用して、VRoidをWebページ上に表示する Part-02
概要

three.js・three-vrm.jsを使用して、VRoidをWebページ上に表示する方法を載せています。
こちらはPart-02となり、Part-01の内容を前提としたものになっています。

今回は以下のコード例を載せています。
Part-01のコードがベースなので、「/* 追加 start */~/* 追加 end */」という形で、追加部分を明記しています。

今回紹介するコード例

  • three-vrmのupdate処理をまず呼び出す
  • カメラ目線にする
  • ポーズの変更方法(vroidposeデータから変更する)
  • 表情の変更方法
  • 口の形の変更方法
  • 変なテクスチャが表示されてしまう問題
  • 解像度問題の対応

また、今回も例によって、実際に動作確認できる(であろう)サンプルコードを下部に載せています。

three-vrmのupdate処理をまず呼び出す

今回行う内容(例えばカメラ目線にする)の中で、three-vrmが用意しているupdate処理を毎フレーム行うことでモデルに反映されるものがあります。

そのため事前準備として、animateメソッドの中でVRoidのアップデート処理を呼び出しておきましょう。

function animate() { requestAnimationFrame(animate); /* 追加 start */ // VRoid自動アップデート const deltaTime = 1 / 60; // 本当は毎フレームごとに計算した方が良いと思うけど、概ね1/60秒で動くと思うので vrm.update(deltaTime); // これが大切 /* 追加 end */ renderer.render(scene, camera); }

最後の方でこのvrm.updateについて簡単にまとめますが、色んな自動計算を行ってくれているようです。

また、この追加とともにvrmの初期化のあたりで、以下を入れておいてください。
これも後述しますが、これをしないと後の手順で行う「ポーズの変更」がVRoidに反映されないはずです

// VRMモデルを設定してシーンに追加 vrm = gltf.userData.vrm; scene.add(vrm.scene); /* 追加 start */ // 姿勢はvroidposeに従って制御するので、自動制御はOFFにする vrm.humanoid.autoUpdateHumanBones = false; /* 追加 end */
カメラ目線にする

VRoidをカメラ目線にします。
より正しく言えば、vrmの初期化時にVRoidの注視点をカメラに設定します。

// カメラ設定を反映 controls.update(); /* 追加 start */ // VRMをカメラ目線にする vrm.lookAt.target = camera; vrm.lookAt.autoUpdate = true; // 初期値だとあまりカメラに目を向けないようになっているので、補正する vrm.lookAt.applier.rangeMapHorizontalInner.outputScale = 20; vrm.lookAt.applier.rangeMapHorizontalOuter.outputScale = 20; vrm.lookAt.applier.rangeMapVerticalDown.outputScale = 20; vrm.lookAt.applier.rangeMapVerticalUp.outputScale = 20; /* 追加 end */ animate();

行数で言うとたった6行で完了(後は前項のvrm.updateを呼び出す必要あり)。

最初の2行が「VRoidがカメラを見る」(VRoidの眼球をカメラに向ける)というものになります。まぁそのままですね。

最後の4行ですが、デフォルト設定だとそんなにカメラを見てくれなくて、微妙に違う場所を見ている感じになってしまいます(一旦この行を消して試してみると分かりやすいです)。

そのため、カメラに目を向ける補正値を大きくすることでカメラ目線になるようにします。あまりに大きい値にすると今度は行き過ぎてしまうので丁度いい値を探すと良いと思います。

ポーズの変更方法(vroidposeデータから変更する)

VRoidのポーズを変更します。
手動で設定するのも良いですが、vroidposeファイルを使用するのが楽だと思うのでその方法で書きます。

/* 追加 start */ // ポーズデータを読み込んだら、それをvrmに適用する document.getElementById("poseDataInput").addEventListener('change', (event)=>{ const file = event.target.files[0]; if(file && vrm){ // ファイルを読み込む const reader = new FileReader(); reader.onload = () => { const poseData = JSON.parse(reader.result); const bonePose = poseData.BoneDefinition; // ボーンごとに保持する for (const boneName in bonePose) { const lowerBoneName = boneName.charAt(0).toLowerCase() + boneName.slice(1); // ポーズデータとVRMボーンの名前を同じにするために、ローワーキャメルにする // vrmにポーズを適用 const vrmBone = vrm.humanoid.getRawBoneNode(lowerBoneName); if(vrmBone){ const quat = bonePose[boneName]; vrmBone.quaternion.set(quat.x, quat.y, -quat.z, -quat.w); } } }; reader.readAsText(file); } }); /* 追加 end */

html側でもファイルを読み込めるようにする

<input type="file" id="poseDataInput" accept=".vroidpose">

ファイルを読み込む処理が入っているので若干行数多めですが、肝心のポーズを変更する処理は「vrmBone.quaternion.set(quat.x, quat.y, -quat.z, -quat.w)」の一行のみです。

もしこの処理を入れても、上手くいかないようであれば最初のvrm.update処理の中で記述した「vrm.humanoid.autoUpdateHumanBones = false;」と言うのを忘れているのではないかと思います。

ところでthree-vrmで読み込んだvroidモデル情報には「rawBone」と「normalizedBone」という二つのボーン情報が入っています。
・rawBone:threejsが持っている大元のボーン情報。本処理で書き換えてるのはこっち。
・normalizedBone:three-vrmjsによってvrm用に正規化されたボーン情報。

vrm.updateメソッドを呼び出すと、このnormalizedBoneからrawBoneに自動的にいい感じにボーンを設定してくれます。

なので逆に言うと、vrm.updateメソッドを使ってしまうとrawBoneを書き換えても勝手にnormalizedBone情報からrawBone側を勝手に書き換えてしまい、元のポーズに戻ってしまいます。「vrm.humanoid.autoUpdateHumanBones = false;」とすることで、その書き換えをさせないようにしています。

本処理ではrawBoneを使っていますが、normalizedBoneを使うことで、vrm側の設定違い(軸やポジションの違い)を補正してrawBone側に設定してくれるようです。なので、normalizedBoneを使う方が無難ではあるかと思います。

normalizedBoneを使いたい場合は上のコードの「vrm.humanoid.getRawBoneNode(lowerBoneName)」を「vrm.humanoid.getNormalizedBoneNode(lowerBoneName)」としたうえで、「vrm.humanoid.autoUpdateHumanBones = false;」を消せば良いです。

また、「手の形」は反映されません。これは元のvroidposeデータを見れば分かると思いますが、手だけはボーンではなくテンプレートを指定して設定されているためです。テンプレートを手のボーンに変換しないとVRoidには反映できないかなと思います。

表情の変更方法

表情変更も簡単です。

/* 追加 start */ // 表情変更 document.getElementById("expressionOn").addEventListener('click', (event)=>{ if(!vrm) return; vrm.expressionManager.setValue('relaxed', 1.0); }); document.getElementById("expressionOff").addEventListener('click', (event)=>{ if(!vrm) return; vrm.expressionManager.setValue('relaxed', 0.0); }); /* 追加 end */

html側で切り替えできるようにする

<input type="button" id="expressionOn" value="笑う"> <input type="button" id="expressionOff" value="笑わない">

実質「vrm.expressionManager.setValue('relaxed', 1.0);」の一行のみです。

setValueの引数に、「表情」と「変更の度合い」を設定すれば良くて、もし怒りと悲しみを0.5ずつ設定したいときは「setValue('sad', 0.5)」「setValue('angry', 0.5)」にすれば良いだけです。

ちなみにVRoidStudioで作ったモデルであれば、以下の表情が設定できると思います。
・relaxed
・sad
・angry
・happy
・Surprised
・neutral

口の形の変更方法

口の形の変え方はほぼ表情変更と同じです。

/* 追加 start */ // 口の開け閉め document.getElementById("mouthOpen").addEventListener('click', (event)=>{ if(!vrm) return; vrm.expressionManager.setValue('aa', 1.0); }); document.getElementById("mouthClose").addEventListener('click', (event)=>{ if(!vrm) return; vrm.expressionManager.setValue('aa', 0.0); }); /* 追加 end */

html側で切り替えできるようにする

<input type="button" id="mouthOpen" value="口を開ける"> <input type="button" id="mouthClose" value="口を閉じる">

実質「vrm.expressionManager.setValue('aa', 1.0);」の一行のみです。

表情変更とほぼ同じですね。

ちなみにVRoidStudioで作ったモデルであれば、以下の口の形が設定できると思います。
・aa
・ih
・ou
・ee
・oh

変なテクスチャが表示されてしまう問題

Part-01では、モデルの首の下や背中部分に不自然に黒いテクスチャが出現していたかと思います。

これというのは実は服のテクスチャに、「ほぼ透明に近いけど透明ではない部分」があり、それが不透明度1.00(最大)として表示されてしまっていたためです。

しかし、恐らく現状のコードであれば多分透明になっていると思います。

一番最初に行った「vrm.update」メソッドがこの部分をいい感じにしてくれているようで、それによって透明になっているようです。
なので、現状対応不要なのですが、vrm.updateメソッドの処理をすべて行うとは限らない(後述)ので、一応個別に設定したい場合の方法を書きます。

// VRMモデルを設定してシーンに追加 vrm = gltf.userData.vrm; scene.add(vrm.scene); /* 追加 start */ Object.values(vrm.materials).forEach((val) => { if(val?.uniforms?.alphaTest?.value !== undefined){ val.uniforms.alphaTest.value = 0.02; // これ以上の不透明度のテクスチャは不透明になる。初期値は0。 } }); /* 追加 end */

テクスチャは複数あるので、ループで回す必要はありますが、肝心の個別設定は「val.uniforms.alphaTest.value = 0.02;」の一行のみで設定できます。

このalphaTest.valueという値が閾値になっており、これ以上の不透明度のテクスチャは不透明度1.00(最大)として表示されるようです。初期値は0なので、ごくわずかでもテクスチャに不透明な場所があるとそれが不透明度1.00(最大)として表示されてしまうということです。

vrm.updateメソッド内で行われているテクスチャ関連の自動更新処理がこのalphaTestの値をいい感じに変更してくれるようなので、vrm.updateメソッドをきちんと呼んでいればこの問題は多分起こらないと思います。

解像度問題の対応

スマホなどの高解像度の端末で表示すると、綺麗にcanvasが表示されず若干崩れて表示されることがあります。

まぁ原因は解像度が高いから、とそれ以上でもそれ以下でも無いですが、解像度の設定も容易にできます。

// canvasの用意 renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize( canvasWidth, canvasHeight ); /* 追加 start */ renderer.setPixelRatio(window.devicePixelRatio); // 解像度の高い端末でも綺麗に表示されるようにする /* 追加 end */ document.body.appendChild(renderer.domElement);

window.devicePixelRatioの値が、Android端末だと大体3.5倍くらいなのですが、割と顕著に処理が重くなります。もちろん元のcanvasサイズが大きければ更に重くなります。

実際にはwindow.devicePixelRatioの値をそのまま設定するのではなく、「Math.min(window.devicePixelRatio, 2.0)」などとすることで、上限値を設けた方が良いと思います。

vrm.update処理の中身について

何度も話に出てくる「vrm.update」メソッドについて、簡単に書いておきます。

このメソッドですがざっくり以下のことをやっているようです(更に深堀りすると複雑になるので、更に細かく見たい場合は御自身でご確認ください)。

// normalizedBoneから、rawBoneを更新する if(this.autoUpdateHumanBones){ this._normalizedHumanBones.update(); } // モデルの目を注視点(現在の設定ではカメラ)に向ける if(this.lookAt){ this.lookAt.update(t); } // モデルの表情を更新 if(this.expressionManager){ this.expressionManager.update(); } // ボーンの連動関係があれば更新する if (this.nodeConstraintManager) { this.nodeConstraintManager.update(); } // モデルのボーンを更新 if (this.springBoneManager) { this.springBoneManager.update(e); } // モデルのマテリアルを更新 if (this.materials) { this.materials.forEach(n => { if (n.update) { n.update(e); } }); }

今回の所で言うと、this.lookAt.updateメソッドはかなり関係します。カメラ目線にする際、カメラをターゲットにするだけで良かったですが、それはこのメソッドのおかげです。

コード量を減らすためにvrm.updateメソッドはきちんと利用するべき(というかそうしないならthree-vrmを使う意味が……)だとは思いますが、毎フレーム呼ばれている処理なので不要な処理はなるべく呼びたくないという人もいると思います。そういう方はvrm.updateメソッドの中身を見て個別に呼び出すのが良いかと思います。

サンプルコードはこちら

サンプルコードはPart-01と同じ場所(GitHub)にあります。

vroidposeにまつわる問題点

vroidposeデータはVRoidStudio用のポーズデータですが、前述の通り「手の形」はそのままではボーンに反映されない問題があります。
こちらは対応がまだできるのですが、「SpineControlPointDeltaPosition」という独自の設定をボーンに反映するのが難しいです。

何か良い対応方法が思いつけば記事にするかもしれません。

コメントログ
※コメントは最新50件が表示されます
コメント投稿




画面下部の「コンタクト」からも連絡可能です。
管理人ツイート
商品サンプル画像
鬼太郎誕生 ゲゲゲの謎 真生版
商品ページ
Amazon
収益広告(自動登録)
※Amazonレビュー要確認
商品サンプル画像
映画しまじろう しまじろうと ゆうきのうた
商品ページ
Amazon
収益広告(自動登録)
※Amazonレビュー要確認
商品サンプル画像
『怪獣8号』第1期総集編
商品ページ
Amazon
収益広告(自動登録)
※Amazonレビュー要確認
商品サンプル画像
LUPIN THE IIIRD 銭形と2人のルパン
商品ページ
Amazon
収益広告(自動登録)
※Amazonレビュー要確認
管理人作品宣伝
ジグソーパズル風パズルゲーム
Webサイト / 最終更新:2025-01-23
ジグソーパズル風パズルゲームです。自分で画像を設定してパズルにできます。…ジグソーパズル風パズルゲームです。自分で画像を設定してパズルにできます。

HPで閲覧する利用素材等の詳細情報
AIの考えた怖い話-Part01
動画 / 最終更新:2024-11-28
怪談系の怖い話を載せています。特に設定部分は人の手が入ってますが、なるべく生成AI(C…怪談系の怖い話を載せています。特に設定部分は人の手が入ってますが、なるべく生成AI(ChatGPT)を利用して書いています。

YouTubeで閲覧するニコニコ動画で閲覧する利用素材等の詳細情報
作品一覧はこちら
関連ページ
JavaScriptでアナログ時計とデジタル時計を表示するコード
最終更新日:2025-05-29
概要 最近アナログ時計を使ったので、アナログ時計を表示するJavaScriptコードを載せます。 …
記事を閲覧する
three.js・three-vrm.jsを使用して、VRoidをWebページ上に表示する Part-03
最終更新日:2025-04-20
概要 three.js・three-vrm.jsを使用して、VRoidをWebページ上に表示する方…
記事を閲覧する
three.js・three-vrm.jsを使用して、VRoidをWebページ上に表示する Part-02
最終更新日:2025-04-15
概要 three.js・three-vrm.jsを使用して、VRoidをWebページ上に表示する方…
記事を閲覧する
three.js・three-vrm.jsを使用して、VRoidをWebページ上に表示する Part-01
最終更新日:2025-04-13
概要 three.js・three-vrm.jsを使用して、VRoidをWebページ上に表示する方…
記事を閲覧する
GIF / APNG(アニメーション付きPNG)ファイル解析ページ
最終更新日:2025-03-31
ファイル読込・操作 以下に調べたいファイルを読み込ませてください。 ファイル情報 カラーパレットを…
記事を閲覧する
gifler.js仕様メモ
最終更新日:2025-03-23
本ページの趣旨 「gifler.js」という、gifアニメーションをcanvasに簡単に表示できる…
記事を閲覧する
普通の文章をホラーっぽく変換
最終更新日:2024-12-24
テキスト:ホラー変換 ※表示までに時間が掛かることがあります。 変換結果 …
記事を閲覧する
【プログラミング】実例で分かるかもしれない再帰処理
最終更新日:2024-12-15
本ページは以下動画の台本を書き起こしたものです 解説の趣旨・方向性 皆さん、こんばんは今回はプログ…
記事を閲覧する
管理人について
最終更新日:2024-12-12
「ふじみ むい」と言います ひょんなことから肉体を得たのでその肉体を使って活動をしています。 とい…
記事を閲覧する
SNSツイート一元化対応(Twitter・Misskey・Mastodon・Bluesky)-公開
最終更新日:2024-12-06
概要 SNSツイートを一元化するためのツールを作成しています(古い記事ですが、こちらのページで紹介…
記事を閲覧する
本サイトのタグ一覧
NovelAIR18VRoidWebサイト作成Webツールととモノ。アークナイツアークナイツ-ステージ攻略日記アズールレーンアズールレーン-日記ウマ娘ギャラリーゲームデビラビローグネットスラング系プログラミングホラーポケットタウン怪談気ままな日記情報技術情報技術-WebAPI知的財産権統合戦略白夜極光本サイトについて魔王スライム様がんばる!漫画
人気記事
ポケットタウン_パズル一覧
最終更新日:2025-05-02
スコア:830.3878 pt
グレーのピースの数 (Number of gray pieces):検索グレーピースの数を入力して、…
記事を閲覧する
メイド・オブ・ザ・デッド-攻略お助け情報
最終更新日:2024-05-01
スコア:505.7972 pt
ネタバレ注意! 本ページは『メイド・オブ・ザ・デッド』の情報を記録しているものです。 攻略の参考に…
記事を閲覧する
地獄先生ぬ~べ~で好きな切ないエピソード
最終更新日:2025-07-08
スコア:214.1782 pt
概要 初代というべきか、週刊少年ジャンプで連載されていた地獄先生ぬ~べ~の切ないエピソードの中で好…
記事を閲覧する
ゲーム『イカれた狩場の看板娘』の紹介・レビュー
最終更新日:2025-05-01
スコア:178.5306 pt
記事概要 イカれたゲームを紹介するぜ! イカれた狩場の看板娘! 以上だ! ちなみにそんなイカれてな…
記事を閲覧する
剣と魔法と学園モノ。2G - パーティ編成確認ツール
最終更新日:2024-05-09
スコア:171.9205 pt
ツール概要 ととモノ。2Gのパーティ編成を考える際に使うツールです。 あくまでストーリークリアまで…
記事を閲覧する
黒バス:キセキの世代級の人たちの技一覧
最終更新日:2024-07-21
スコア:112.8617 pt
黒バスのキセキの世代級の人たちの技一覧です 概要 黒バスのキセキの世代級の人たちの技を記載したもの…
記事を閲覧する
アークナイツ-常設商品-理性換算
最終更新日:2024-04-28
スコア:104.5333 pt
概要 "常設商品でお得な商品はどれか"というのを理性に換算して一覧化したものとなります。 絶対的に…
記事を閲覧する
本サイトについて
最終更新日:2025-07-10
スコア:97.0105 pt
本サイトの概要 概要 個人ブログのようなものです。とくにジャンルはありません。 本サイト内の情報に…
記事を閲覧する
最新記事
地獄先生ぬ~べ~新アニメで設定変わったところ(3話まで)
最終更新日:2025-07-13
概要 地獄先生ぬ~べ~の新アニメが2025-07-02(木)よりやっていますが、そこで設定が変わっ…
記事を閲覧する
本サイトについて
最終更新日:2025-07-10
本サイトの概要 概要 個人ブログのようなものです。とくにジャンルはありません。 本サイト内の情報に…
記事を閲覧する
地獄先生ぬ~べ~で好きな切ないエピソード
最終更新日:2025-07-08
概要 初代というべきか、週刊少年ジャンプで連載されていた地獄先生ぬ~べ~の切ないエピソードの中で好…
記事を閲覧する
htmlタグ混在のテキストをhtmlタグとプレーンテキストで分けるJavaScriptコード
最終更新日:2025-06-20
概要 「よくある質問は a href="/" こ span ち /span ら /a です」 とい…
記事を閲覧する
商品サンプル画像
ドラゴンキーパーVSデスメシア
商品ページ
Amazon
収益広告(自動登録)
※Amazonレビュー要確認