ThreeJSでHSP3オフィシャル3D素材「珠音」を表示させて走らせてみます。
ThreeJSを使えば3Dオブジェクトを描画することができます。ただこれまでは自作したヘンテコな物体の描画しかしてきませんでした。やるならモデルデータの読み込みをしてみたいものです。
人間の形をしていて動作させることができるものはないかと探していたら、こんなものをみつけました。
珠音(たまね)は、Hot Soup Processor(HSP)とともに使用することのできる 3Dサンプル素材です。HSP3の3D描画ライブラリであるHGIMG3/HGIMG4により表示可能なデータとスクリプト、及び元データがすべて収められています。
HSP3はここからダウンロードできます。
解凍したあとhsp37beta\sample\hgimg4\fbxのなかにhigh_school_girl.fbxがあります。これをつかいましょう。
最初にhigh_school_girl.fbxをダブルクリックすると3Dビューアが起動します。
まず3Dビューアのメニューからファイル ⇒ 名前をつけて保存を選択します。そしてファイル形式はGLTFバイナリを指定して保存します。GLTF形式に変換する理由は鳩でもわかるC#管理人がfbxで3Dオブジェクトを描画する方法を知らないのと、GLTF形式に変換することでfbxファイルがあったフォルダのなかのtgaファイルがなくても動作させることができるからです。ファイルはひとつにまとめてしまいましょう。
ではさっそくコードを示します。まずはHTML部分です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>ThreeJSで珠音ちゃんを走らせてみる</title> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="description" content="ThreeJSで珠音ちゃんを走らせてみる" /> </head> <body> <canvas id="canvas"></canvas> <script src="https://unpkg.com/three@0.140.2/build/three.min.js"></script> <script src="https://unpkg.com/three@0.137.4/examples/js/loaders/GLTFLoader.js"></script> <script src="./index.js"></script> </body> </html> |
JavaScript部分を示します。
ページが読み込まれたらinit関数が実行されます。
ここではレンダラー、シーン、カメラを作成したあと、シーンにライトを追加しています。そのあと後述するLoadedGltf関数を呼び出してGLTF形式のモデルデータを読み込んで3Dオブジェクトをシーンに追加しています。
index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
// ページの読み込みを待つ window.addEventListener('DOMContentLoaded', init); let mixer; let clock = new THREE.Clock(); let tamane_gltf; function init() { // レンダラーを作成 renderer = new THREE.WebGLRenderer({ canvas: document.querySelector("#canvas") }); // 幅、高さ let width = 600; let height = 500; renderer.setPixelRatio(1); renderer.setSize(width, height); // シーンを作成 scene = new THREE.Scene(); // カメラを作成 camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000); camera.position.set(0, 0, 500); camera.lookAt(new THREE.Vector3(0, 50, 0)); // 平行光源 const light = new THREE.DirectionalLight(0xffffff); light.intensity = 2; // 光の強さを倍に light.position.set(0, 0, 1); scene.add(light); // GLTF形式のモデルデータを読み込む const loader = new THREE.GLTFLoader(); const buildingUrl = "./high_school_girl.glb"; loader.load(buildingUrl, LoadedGltf); // 第二引数はコールバック関数(後述) renderer.gammaOutput = true; renderer.gammaFactor = 2.2; tick(); // 後述 } |
GLTF形式のモデルデータを読み込んで3Dオブジェクトをシーンに追加するLoadedGltf関数を示します。
GLTFファイルにはシーンの情報の他にさまざまな情報が含まれます。そのため、コールバック関数の引数からシーンの情報だけ抜き出して追加します。さらにアニメーションさせる処理も追加します。
珠音のアニメーションには「停止」「歩く」「走る」の3種類が用意されています。最初は停止状態にしておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
function LoadedGltf(gltf){ tamane_gltf = gltf; const model = gltf.scene; model.scale.set(1.0, 1.0, 1.0); model.position.set(0, -100, 0); model.position.set(0, -100, 0); scene.add(model); const animations = gltf.animations; if(animations && animations.length) { mixer = new THREE.AnimationMixer(model); let animation = animations[0]; let action = mixer.clipAction(animation) ; // Animation Actionを生成 action.clampWhenFinished = true; // アニメーションの最後のフレームでアニメーションが終了 action.play(); // アニメーションを再生 } } |
アニメーションを更新し、レンダラーを呼び出す処理を示します。
1 2 3 4 5 6 7 8 9 |
function tick() { renderer.render(scene, camera); requestAnimationFrame(tick); //Animation Mixerを実行 if(mixer){ mixer.update(clock.getDelta()); } } |
これだけでは面白くないのでキー操作をすると方向を変えたり、走ったり止まったりするようにしてみます。左右のキーで方向転換して(向かい合っている状態では左右が入れ替わっているので違和感あるかも)、↑キーで走り出し↓キーで止まります。いきなり止まるとおかしいので0.25秒間歩かせてから止まります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
document.onkeydown = (e) =>{ const model = tamane_gltf.scene; if(e.key == 'ArrowRight') model.rotation.y -= 0.05; if(e.key == 'ArrowLeft') model.rotation.y += 0.05; if(e.key == 'ArrowUp') Run(); if(e.key == 'ArrowDown') Stop(); } function Run(){ const model = tamane_gltf.scene; const animations = tamane_gltf.animations; mixer = new THREE.AnimationMixer(model); let animation = animations[2]; // animations[2]は走る動作 let action = mixer.clipAction(animation) ; action.clampWhenFinished = true; action.play(); } function Stop(){ const model = tamane_gltf.scene; const animations = tamane_gltf.animations; mixer = new THREE.AnimationMixer(model); let animation = animations[1]; // animations[1]は歩く動作 let action = mixer.clipAction(animation) ; action.clampWhenFinished = true; action.play(); setTimeout(()=>{ mixer = new THREE.AnimationMixer(model); let animation = animations[0]; // animations[0]は停止している動作 let action = mixer.clipAction(animation) ; action.clampWhenFinished = true; action.play(); }, 250); } |