前回、Babylon.jsをES6以降の仕様で書く方法をまとめました。
引き続きES6に慣れるため、Babylon.jsと似ているthree.jsについても書き方を調べてみました。ただ、three.jsを初めて使ったためか、うまく動かず情報を探すことができなかったため、今回整理した内容をまとめてみました。
使用したthree.jsのバージョン (npm package ) : 0.116.1
1. ES6でthree.jsのコードを書く手順
今回のソースコードは以下のリポジトリにまとめました。
GitHub - flushpot1125/threejs-es6
1.1 フォルダ作成、npm init
まずはソースコードを格納するためのフォルダを作り、npmコマンドを実行します。
$ npm init --y
このコマンドは前回の記事と同様です。
1.2 webpackでビルド、サーバ環境を構築する
webpackは、複数のjavascriptファイルをまとめて1つにする仕組みです。また、webpack-dev-serverを使うことで、簡易的なWebサーバをすぐに作ることができます。
$ npm install webpack webpack-cli webpack-dev-server --save-dev
インストール後、webpack.config.jsを作って設定内容を書きます。
書き方は前回同様、以下の記事を参考にさせていただいています。
この設定ファイルにより、自分で書いたソースコード (src/index.js)を起点として、1つにまとまった.jsファイル (app.js) がindex.htmlと同じ場所に生成されます。
1.3 three.jsのモジュールをインストールして最低限のコードを書く
インストールする手順はこれだけです。
$ npm install three
今回は、webpack.config.jsの記述に沿って、src/index.jsとindex.htmlを作ります。このindex.jsにthree.jsのコードを書きます。
app.jsは実体としては存在せず、webpackによってサーバ起動時に動的に生成されます。ターミナルから npm start
を実施して、http://localhost:4000
にアクセスすると、このように表示されます。
1.4 .obj、.mtl、テクスチャを読み込んで表示させる処理を追加してみる
先ほどまでで最低限の環境構築はできています。ただ、この後.objファイルを読み込む処理を追加したところ、思ったよりも詰まったので.obj、.mtl、テクスチャファイルを読み込んで表示させる方法を追記しておきます。
このようなコードを書きます。index.htmlは変更なしです。
また、コードに書いたディレクトリ構成に沿って、assets/importobjtest/に.obj、.mtl、テクスチャファイルをおきます。
ターミナルから npm start
を実施して、http://localhost:4000
にアクセスすると、このように表示されます。
2. three.jsで書くときのtips一覧
2.1 three.jsのモジュールの多くは、examples/jsmフォルダ以下を指定する
Importable Examples
The core of three.js is focused on the most important components of a 3D engine. Many other components like loaders or controls are part of the examples directory. three.js ensures that these files are kept in sync with the core but users have to import them separately if they are required for a project. You can find in the examples/jsm directory an ES6 module version for almost all example files. If you install three.js via npm, you can import them like so:
引用元:Import via modules - three.js
ここに書かれている通り、コアな処理はTHREEを宣言しています。
import * as THREE from 'three';
そのため、 new THREE.Scene()
のように、THREEをつけることで使えます。しかし、THREEで宣言しても出てこない場合があります。たとえば、.objファイルを読み込むためのOBJLoaderをTHREE.OBJLoaderとするとエラーが出ます。
Uncaught TypeError: three__WEBPACK_IMPORTED_MODULE_0__.OBJLoader is not a constructor at init (index.js:47)
正しくは.jsまで指定してこのように書きます。
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
2.2 importを使うときは、名称を{}でくくる
以下のように書くとエラーが出ます。
//間違い import OBJLoader from 'three/examples/jsm/loaders/OBJLoader.js';
ncaught TypeError: three_examples_jsm_loaders_OBJLoader_js__WEBPACK_IMPORTED_MODULE_3__.default is not a constructor at init (index.js:47)
あるいはこのようなエラーが出ることもあります。
./src/index.js 47:24-33 "export 'default' (imported as 'OBJLoader') was not found in 'three/examples/jsm/loaders/OBJLoader.js'
念のためですが、正しい書き方は下記です。
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
私の場合、Visual Studio Codeの文法チェックではエラーが出なかったのでしばらく気づけませんでした。
2.3 objモデルの読み込みエラーが出ていないのに3Dモデルが表示されない
カメラの位置が3Dモデルから離れている可能性があります。たとえば、three.js Editorを使ってモデルを表示してみると、3Dモデルに設定された位置がわかります。
ここで、カメラの位置より大幅にずれている、あるいはモデルが大きすぎる、小さすぎるなどがわかるので、その場合はモデルを修正するか、three.jsのコード側(たとえばカメラの位置やfovの値)を変更することで対応できます。
なお、three.js Editorは、以下よりアクセスできるWebベースのGUIエディタです。
2.4 マウス操作用のOrbitControlを使うと、OrbitControls.js:1110 Uncaught TypeError: Cannot read property 'addEventListener' of undefinedが出る
このように書くとエラーになります。
let controls = new OrbitControls(camera);
これは、three.jsのあるバージョン(どこからかは調べきれてません)から、マウスコントロール対象のcanvasも指定することになったためです。
正しくはこう書きます。
以下のようにcanvasを指定しても動きます。
なお、マウス操作につかえるTrackballControlsを使うときも、第2引数にcanvasの情報(書き方は上記どちらでも良さそう)が必要です。
これを書かないと、同じようにエラーが出ます。
Uncaught TypeError: Cannot read property 'addEventListener' of undefined at new TrackballControls (app.js:61073) at init (app.js:64293)
3. おわりに
今回調べてわかったのですが、three.jsはバージョンによって変更が激しいようです。そのため、Web上に多数の情報があるのですが、その通りに書いても動かないことがありました。
既知の方には基本的な話が多いのですが、これからWebXR関係のアプリ開発を始める方の参考になれば幸いです。