以前、three.jsとpanolens.jsを利用して360度画像をWebに埋め込むという記事を書きました。
今回は、このthree.jsだけを使って、3DCG作成無料ソフトBlenderで制作したモデルとアニメーションをWeb上で再現したいと思います!
~やりたい事~
ローカル上で、3Dモデルを再現させる。
環境
OS:Windows10
エディタ:VSCode
JavaScriptライブラリ:three.js v0.139.2
事前準備
Blenderからオブジェクトを書き出す
three.jsが公式に「glTF」の利用を推奨しておりますので、Blenderでの書き出しはglTFでエクスポートしてください。
three.js公式: Loading 3D models(https://threejs.org/docs/index.html#manual/en/introduction/Loading-3D-models)
glTFでの書き出し方法は、以前に書かせていただいたARの記事の中で触れておりますので、そちらをご参照ください。
※記事中の「2.Blender/モデルの保存」をご参照ください。
Blenderで物理演算を使用している場合
物理演算を使用している場合は、シミュレーション結果を固定するために、ベイクを行ってください。
ベイクを行わずに書き出すと、アニメーションキーフレームは空のままとなるため、物理演算のモーションのみ機能しません。
ベイクの方法は、こちらの記事をご参照ください。
three.jsのダウンロード
github若しくは、公式サイトのDownloadボタンからライブラリ一式をダウンロードしてください。
three.js公式サイト:https://threejs.org/
three.js github:https://github.com/mrdoob/three.js/
three.jsのダウンロードに関しては、「360度写真のWeb上再現」の記事で触れておりますので、gitコマンドが分からない際にはご参照ください。
※記事中の「2.必要なJavaScriptライブラリをインストールする」の中の「1.three.js」をご参照ください。
※下記記事中では、panolens.jsの依存関係によりthree.jsのversionを限定しておりますが、今回はthree.jsのみの利用となりますので、最新版のダウンロードでOKです。
HTML/CSS
まず、HTMLを作る前に、three.jsを動かすためのjsファイルを作成します。
名前は他ファイルと干渉しなければ何でもOKです。
今回は、controll.jsとしました。
今回、実装実験が目的でございますので、CSSもHTML内に書きこみます。
(正式にコーディングしていく際は、CSSを外だしにして下さい。)
three.jsの読み込み部分については、詳しく次章で触れますので、ご参照ください。
three.jsの読み込みにはちょっとした躓きポイントがあります。
HTMLのコード全体はこのようになります。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3Dmodel</title>
<script src="3djs/three.min.js"></script>
<script src="3djs/OrbitControls.js"></script>
<script src="3djs/RoomEnvironment.js"></script>
<script src="3djs/GLTFLoader.js"></script>
<script src="3djs/controll.js"></script>
<style>
html,
body {
margin: 0;
background-color: aquamarine;
}
#canvas {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div>
<canvas id="canvas"></canvas>
</div>
</body>
</html>
three.jsのライブラリを読み込む
前章のHTMLでは、必要なJavaScriptファイルをフォルダにまとめておりますが、コーディングを進めていくと新たに必要なファイルが出て来る可能性があります。
実装確認ができるまでの間は、ダウンロードしたフォルダ階層そのままに読み込んでいくことをお勧め致します。
それでは、githubからダウンロードしたフォルダ階層をそのままに、必要なjsファイルを読み込みます。
まずは、読み込み部分のコード全体を記させていただきます。
<script src="three.js-master/build/three.min.js"></script>
<script src="three.js-master/examples/js/controls/OrbitControls.js"></script>
<script src="three.js-master/examples/js/environments/RoomEnvironment.js"></script>
<script src="three.js-master/examples/js/loaders/GLTFLoader.js"></script>
それぞれ、読み込んだファイルはこのような機能を持っています。
three.min.js→心臓部分
OrbitControls.js→カメラの動きを制御する
OrbitControls公式リファレンス:https://threejs.org/docs/#examples/en/controls/OrbitControls
RoomEnvironment.js→環境光を制御する
GLTFLoader.js→glTFをロードする
GLTFLoader公式リファレンス:https://threejs.org/docs/#examples/en/loaders/GLTFLoader
ライブラリ読み込みで躓く点
three.js公式ページには、このようにjsmフォルダ内のjsファイルをimportするようにコード例で書かれています。
// GLTFLoader.jsのインポート例
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
このコード例のように、jsmファイルをscript src=で読み込むと、SyntaxErrorが出ます。
// コード例のファイルを読み込み
<script src="three.js-master/examples/jsm/loaders/GLTFLoader.js"></script>
Uncaught SyntaxError: Cannot use import statement outside a module
公式サイトを読んでみると、以下の内容が書かれております。
ソース:https://threejs.org/docs/index.html#manual/en/introduction/Installation
- ESのモジュールに依存している。
- Webpackなどバンドルツールを使用してプロジェクトに必要なパッケージを1つのJavaScriptファイルに結合する。
コード例の記述はES6モジュールを利用していることが分かりました。
JavaScriptのモジュールは、<script type=”module”>でHTML内に書く事ができます。
が、フォルダ構造を維持しなければモジュールが参照できなかったり、勉強不足なところもあり、今回はローカルで必要なファイルだけ抜き出して、実装を行うこととしました。
Three.jsが推奨しているES6での記述、実装方法につきましては、CrossRoadさまのブログで分かりやすく説明して下さっておりますので、リンクを貼らせていただきます。
https://www.crossroad-tech.com/entry/threejs-es6
それでは、<script src=””>で読み込んでも動くよう、モジュールを利用したjsmフォルダではなくjsフォルダのファイルを読み込みます。
jsフォルダは、 three.js-master/examples/js の階層にあります。
必要なjsファイルをコピーして、任意のフォルダにペーストしてもOKです。
以下のJavaScript読み込みでは、「3djs」フォルダを作りそこに必要なjsファイルをまとめました。
フォルダ名「js」じゃないの?!というツッコミはなしでお願いします(笑)
<script src="3djs/three.min.js"></script>
<script src="3djs/OrbitControls.js"></script>
<script src="3djs/RoomEnvironment.js"></script>
<script src="3djs/GLTFLoader.js"></script>
以下、モジュールエラーについて参考にさせていただいたサイトです。
●エラーについての質問&回答をしているサイト
エラー「 Uncaught SyntaxError: Cannot use import statement outside a module 」
https://stackoverflow.com/questions/61191061/uncaught-syntaxerror-cannot-use-import-statement-outside-a-module
https://grabthiscode.com/javascript/uncaught-syntaxerror-cannot-use-import-statement-outside-a-module-three-js
エラー「Uncaught TypeError: Failed to resolve module specifier “three”. Relative references must start with either “/”, “./”, or “…/”.」
https://discourse.threejs.org/t/error-relative-references-must-start-with-either-or/13573
●JavaScriptモジュールについて
https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Modules#a_background_on_modules
controll.jsにthree.jsを動かすための記述をしていく
コーディングのベースにさせていただいた記事をご紹介させていただきます。
Qiita「Blenderで作成した3DモデルをThree.jsでブラウザに表示する」:
https://qiita.com/nannany_hey/items/c92d9f05588c751077b1
three.js example animation/keyframes:
https://github.com/mrdoob/three.js/blob/master/examples/webgl_animation_keyframes.html
それでは、controll.jsのコード全体はこちらです。
window.addEventListener('DOMContentLoaded', init);
function init() {
// レンダラーを作成
const renderer = new THREE.WebGLRenderer({
canvas: document.querySelector('#canvas')
});
// ウィンドウサイズ設定
width = window.innerWidth;
height = window.innerHeight;
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height);
// シーンを作成
const pmremGenerator = new THREE.PMREMGenerator(renderer);
const scene = new THREE.Scene();
// 環境光
const roomEnviroment = new THREE.RoomEnvironment();
scene.background = new THREE.Color(0xbfe3dd);
scene.environment = pmremGenerator.fromScene(roomEnviroment, 0.04).texture;
// animation
let mixer;
let clock = new THREE.Clock();
const actions = [];
// カメラを作成
camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000);
camera.position.set(0, 1000, -1500);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.update();
// 3Dモデルを読み込み
const loader = new THREE.GLTFLoader();
// VScodeのLive Serverを使用している為
const url = 'http://127.0.0.1:5500/3d/model/destroy.glb';
let model = null;
loader.load(
url,
function (gltf) {
model = gltf.scene;
model.scale.set(100, 100, 100);
model.position.set(0, 0, 0);
scene.add(model);
//ANIMATION
mixer = new THREE.AnimationMixer(model);
// 複数のアクションすべてを再生
gltf.animations.forEach(animation => {
actions.push(mixer.clipAction(animation).play());
})
animate();
},
undefined, function (e) {
console.log(e);
}
);
renderer.gammaOutput = true;
renderer.gammaFactor = 2.2;
// animation
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
mixer.update(delta);
controls.update();
renderer.render(scene, camera);
}
}
コードの中身についての説明は、参考にさせていただいた記事やthree.js公式サイトで詳しく書かれておりますので、割愛させていただきます。
(記事にして下さる方々がいらっしゃるので、こうやって実装が出来ています!感謝です><)
<参考記事>
Qiita「Blenderで作成した3DモデルをThree.jsでブラウザに表示する」:
https://qiita.com/nannany_hey/items/c92d9f05588c751077b1
AKABANE TECH BLOGさま「Webで3Dモデルを扱いたい」:
https://www.fundely.co.jp/blog/tech/2019/10/09/180017/
MOX-MOTIONさま「Blenderでアニメーション出力」:
https://mox-motion.com/blog/webgl05-2/
Pent@creation BLOGさま「Three.jsでglTFアニメーション」:
https://www.pentacreation.com/blog/2019/10/191016.html
yumikokhさまGithub「switch-blender-animation」:
https://github.com/yumikokh/switch-blender-animation/blob/master/script.js
また、three.jsの公式ページにはexamplesに多くのコード例が掲載されております。
再現したいパターンに照らし合わせて、コード例を参照するとコーディングがスムーズにできました!
three.js example:https://threejs.org/examples/#webgl_animation_keyframes
※3Dが表示されているウィンドウの右下にある<>アイコンをクリックするとgithubのソースコードにリンクがされています。
three.jsのソースコードを読みながら必要なjsファイルを追加したり、記述を追筆していくと便利です!
アニメーションの部分だけ少し触れます。
今回の3Dモデルは、物理演算をベイクしており、大量のアニメーションを含んでいるデータとなっております。アニメーションは、配列で格納されるため、forEachで格納しているすべてのアニメーションを実行させるコードを記述しております。
// 複数のアクションがある場合
gltf.animations.forEach(animation => {
actions.push(mixer.clipAction(animation).play());
})
例えば、アニメーションがひとつしかない場合の記述はこのようになります。
// 単数のアクション
mixer.clipAction(gltf.animations[0]).play();
もちろん、配列の要素を指定することで、アニメーションの実行を制御する事もできます。
Webブラウザーで確認をする
Webブラウザーで確認するときは、ローカルサーバーをたちあげます。
今回は、手っ取り早く扱える、VSCodeのLive Serverを利用しました。
※3dモデルを読み込むURLもLiveServerに合わせています。
Live Serverの使い方につきましては、以下の記事をご参照ください。
※記事中の「4.画像が出ない時の対処法」の中の「1.VSCodeの拡張機能「Live Server」をご参照ください。
最後に
実際にサーバーにアップロードしたのですが、3Dファイルが重く読み込みに48秒もかかるという・・・・
Three.js公式サイトを読むと、DRACOLoader.jsで3Dメッシュを圧縮できると書かれていました。これで少しは軽くなるのかなと思いながらも力尽きました(笑)
DRACOLoaderについて:https://threejs.org/docs/#examples/en/loaders/DRACOLoader
物理演算についても、今回はBlenderの中で物理演算を施し、アニメーションキーフレームに焼き込みを行いましたが、three.jsと掛け合わして再現ができる3D物理エンジンのライブラリもあるようです。
three.jsは色々と試せて面白いですね!
<3D物理エンジンのライブラリー>
ammo.js:https://github.com/kripken/ammo.js
oimo.js:https://github.com/lo-th/Oimo.js
cannon.js:https://github.com/schteppe/cannon.js
physi.js:https://github.com/chandlerprall/Physijs
ブラウザー上で再現できたBlenderモデル+アニメーション↓
最後までお読み下さり有難うございましたm(__)m