Clair Obscur: Expedition 33(クレールオブスキュール:エクスペディション33) - PS5
商品ページ
Amazon
収益広告自動登録)
対応 Nintendo Switch2 用 ガラスフィルム ガイド枠付き 対応 NintendoSwitch2 用 フィルム 指紋防止 NS2 液晶 保護フィルム 硬度9H 耐衝撃 飛散防止 自動吸着 気泡ゼロ ラウンドエッジ加工 超薄 超高質感 貼
商品ページ
Amazon
収益広告自動登録)
GIANNA BOYFRIEND #09 通常版 (表紙:吉沢亮) (メディアパルムック)
商品ページ
Amazon
収益広告自動登録)
ファンタジーライフi グルグルの竜と時をぬすむ少女 -Switch
商品ページ
Amazon
収益広告自動登録)
ファンタジーライフi グルグルの竜と時をぬすむ少女 -PS5
商品ページ
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件が表示されます
コメント投稿




画面下部の「コンタクト」からも連絡可能です。
管理人ツイート
【3枚セット】Switch 2対応 強化ガラスフィルム|JSAUX取付ガイド付き・かんたん貼付・9H硬度・気泡ゼロ・指紋防止・高透過率・ドック対応
商品ページ
Amazon
収益広告自動登録)
司法試験&予備試験 体系別 短答過去問題集 刑法 第3版 司法試験&予備試験 体系別短答過去問題集シリーズ
商品ページ
Amazon
収益広告(手動登録)
[Switch 2 モデル対応] Oldstar Switch 2 用 ガラスフィルム 2枚入り 画面 保護フィルム 2025強化ガラス 高硬度9H 日本硝子素材 指紋防止 耐衝撃 飛散防止 2.5Dラウンドエッジ加工 気泡ゼロ 自動吸着
商品ページ
Amazon
収益広告自動登録)
【PS5】DEATH STRANDING 2: ON THE BEACH (デス・ストランディング 2: オン・ザ・ビーチ)【早期購入特典】ゲーム内アイテム: ・カスタマイズホロ: クオッカワラビー 早期アンロック ・バトルスケルトン/シルバー [L
商品ページ
Amazon
収益広告自動登録)
管理人作品宣伝
剣と魔法と学園モノ。3 - パーティ編成確認ツール
Webサイト / 最終更新:2025-03-10
『ととモノ。3』のパーティ編成を考える際に使えるツールです。…『ととモノ。3』のパーティ編成を考える際に使えるツールです。

HPで閲覧する利用素材等の詳細情報
【アークナイツ】アークナイツ運動会-関所破壊レース
動画 / 最終更新:2025-01-16
アークナイツ生息演算の岸壁の関の関門を誰が最速で破壊できるかを競います。…アークナイツ生息演算の岸壁の関の関門を誰が最速で破壊できるかを競います。

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知的財産権統合戦略白夜極光本サイトについて魔王スライム様がんばる!漫画
人気記事
メイド・オブ・ザ・デッド-攻略お助け情報
最終更新日:2024-05-01
スコア:1032.2952 pt
ネタバレ注意! 本ページは『メイド・オブ・ザ・デッド』の情報を記録しているものです。 攻略の参考に…
記事を閲覧する
ポケットタウン_パズル一覧
最終更新日:2025-05-02
スコア:888.3364 pt
グレーのピースの数 (Number of gray pieces):検索グレーピースの数を入力して、…
記事を閲覧する
剣と魔法と学園モノ。2G - パーティ編成確認ツール
最終更新日:2024-05-09
スコア:212.4454 pt
ツール概要 ととモノ。2Gのパーティ編成を考える際に使うツールです。 あくまでストーリークリアまで…
記事を閲覧する
ゲーム『イカれた狩場の看板娘』の紹介・レビュー
最終更新日:2025-05-01
スコア:102.3115 pt
記事概要 イカれたゲームを紹介するぜ! イカれた狩場の看板娘! 以上だ! ちなみにそんなイカれてな…
記事を閲覧する
地獄先生ぬ~べ~で好きな切ないエピソード
最終更新日:2025-05-19
スコア:101.9759 pt
概要 初代というべきか、週刊少年ジャンプで連載されていた地獄先生ぬ~べ~の切ないエピソードの中で好…
記事を閲覧する
アークナイツ-常設商品-理性換算
最終更新日:2024-04-28
スコア:86.7000 pt
概要 "常設商品でお得な商品はどれか"というのを理性に換算して一覧化したものとなります。 絶対的に…
記事を閲覧する
本サイトについて
最終更新日:2025-05-28
スコア:83.0123 pt
本サイトの概要 概要 個人ブログのようなものです。とくにジャンルはありません。 本サイト内の情報に…
記事を閲覧する
剣と魔法と学園モノ。3 - パーティ編成確認ツール
最終更新日:2025-05-07
スコア:81.3065 pt
ツール概要 ととモノ。3のパーティ編成を考える際に使うツールです。 攻略本や攻略wikiを参考にし…
記事を閲覧する
最新記事
DOM要素の操作でよく使うプロパティ・メソッド一覧
最終更新日:2025-06-03
概要 JavaScriptでDOM操作(取得・作成・更新・削除)をする際によく使うプロパティとメソ…
記事を閲覧する
JavaScriptでアナログ時計とデジタル時計を表示するコード
最終更新日:2025-05-29
概要 最近アナログ時計を使ったので、アナログ時計を表示するJavaScriptコードを載せます。 …
記事を閲覧する
VRoidナビゲーターが回答できる質問リスト
最終更新日:2025-05-28
VRoidナビゲーターが回答できる質問リスト 「よくある質問」というわけではないですが、「VRoi…
記事を閲覧する
本サイトについて
最終更新日:2025-05-28
本サイトの概要 概要 個人ブログのようなものです。とくにジャンルはありません。 本サイト内の情報に…
記事を閲覧する
機動戦士ガンダムSEED BATTLE DESTINY REMASTERED -Switch
商品ページ
Amazon
収益広告自動登録)