three.jsで使うテクスチャーをPreloadJSで一括ロードしよう！

![ceb40bdb-685a-d434-db94-e4e3b23ff887.png](https://qiita-image-store.s3.amazonaws.com/0/42248/c430ead7-3169-2a9b-9ed3-6e270beead78.png)

この記事は、<a href="http://qiita.com/advent-calendar/2016/threejs" target="_blank">Three.js Advent Calendar 2016</a> 11日目の記事です。


three.jsでコンテンツを作る上でさけては通れないテクスチャーのロード処理。実は**[PreloadJS](http://www.createjs.com/preloadjs)を併用することで劇的に効率良くテクスチャーが扱えるようになることをご存知でしょうか？**今回はそんなマル秘テクニックを解説します。

# TextureLoaderは一個のロードしかできない :smiling_imp:

three.jsに元々`TextureLoader`というローダーが備わっています。次のコードは`hoge.jpg`を読み込むためのコードです。

```js
// テクスチャーのロード
var loader = new THREE.TextureLoader();
var texture = loader.load('hoge.jpg');

// マテリアルに貼り付け
var material = new THREE.MeshBasicMaterial( { 
    map: texture
});
```

`TextureLoader`の`load()`メソッドを使用して一つの画像を読み込んでいます。しかしこのままでは**複数の画像を読み込みたい場合、。一個一個の画像に対して`load()`メソッドを呼ぶ必要があります。**ほとんどのプロジェクトの場合、テクスチャーは複数読み込む必要があるため`TextureLoader`では非常に面倒です。しかし**PreloadJSを併用することでこの問題が解決します。**


# PreloadJSとは :thinking:

PreloadJSとは[CreateJS](http://www.createjs.com)が提供している**画像や音楽ファイルを読み込むためのモジュールです。**PreloadJSを使用することで複数のアセットの読み込みが可能になり、ローディング表示などの実装にも役立ちます。**PreloadJSはCreateJSを使用せずとも単体で使用することができます。**

three.jsとPrelaodJSを併用した簡単なデモを作成しました。[GitHub](https://github.com/sawa-zen/threejs_preloadjs_demo/tree/gh-pages)にソースコードも置いていますので参考ください。

<a href="https://sawa-zen.github.io/threejs_preloadjs_demo/" target="_blank">
![Untitled.gif](https://qiita-image-store.s3.amazonaws.com/0/42248/8bc8f7ae-f779-a17e-8a68-43911762073b.gif)
</a>

- <a href="https://sawa-zen.github.io/threejs_preloadjs_demo/" target="_blank">Three.js & PreloadJS Demo</a>
- <a href="https://github.com/sawa-zen/threejs_preloadjs_demo/tree/gh-pages" target="_blank">GitHub</a>

ここからPreloadJSを使用したテクスチャーロードの実装方法を解説しますがドキュメントを併せて見ておくと分かりやすいです。

- [PreloadJS API Documentation](http://www.createjs.com/docs/preloadjs/modules/PreloadJS.html)


# PreloadJSでのテクスチャーロード実装 :wink:

PreloadJSを使ったテクスチャーロード方法を解説します。まずはPreloadJSをHTMLで読み込みます。

```html
<!-- PreloadJSの読み込み -->
<script src="https://code.createjs.com/preloadjs-0.6.2.min.js"></script>
```

PreloadJSを使用する準備が出来たのでJavaScriptを書いていきます。次のコードは画像を読み込んでthree.jsで使えるテクスチャーに変換するまでのコードです。

```js
// 読み込むテクスチャーリスト
var manifest = [
    { id: 'hoge', src: './texture/hoge.png'},
    { id: 'huga', src: './texture/huga.png'}
];

// ロードキューを作成
var loadQueue = new createjs.LoadQueue();

// ロード完了を監視
loadQueue.on('complete', function() {
    // loadQueueからロードした画像データを取得
    var image = loadQueue.getResult('hoge');
    // three.jsで使えるテクスチャーに変換
    var texture = new THREE.Texture(image);
    // 【重要】更新を許可
    texture.needsUpdate = true;
});

// テクスチャーのロードを開始
loadQueue.loadManifest(manifest);
```

コードを順に解説していきます。

## 1. マニフェストデータを用意

読み込みたい画像の一覧データを配列で用意します。

```js
// 読み込むテクスチャーリスト
var manifest = [
    { id: 'hoge', src: './texture/hoge.png'},
    { id: 'huga', src: './texture/huga.png'}
];
```

`id`に設定した文字列は読み込み結果から画像を取得する際に使用します。分かりやすい`id`を設定しましょう。`src`には読み込みたい画像のパスを設定します。**PreloadJSではこのような読み込みリストデータを<ruby>Manifest<rt>マニフェスト</rt><ruby>と呼びます。**


## 2. LoadQueueを生成

**`LoadQueue`はローダーと思ってもらって差し支えありません。**`LoadQueue`を生成し、これを利用して画像のロードを行っていきます。

```js
// ロードキューを作成
var loadQueue = new createjs.LoadQueue();
```


## 3. ロード完了イベントのリスナーを登録

ロード完了後の処理を追加します。全てのアセットのロードが完了すると`complete`イベントが発火されるので`on()`メソッドでイベントリスナーを登録して監視します。

```js
// ロード完了を監視
loadQueue.on('complete', function() {
    // loadQueueからロードした画像データを取得
    var image = loadQueue.getResult('hoge');
    // three.jsで使えるテクスチャーに変換
    var texture = new THREE.Texture(image);
    // 【重要】更新を許可
    texture.needsUpdate = true;
});
```

ロード完了後は**`getResult()`メソッドにマニフェストで設定したIDを引数として渡すことで読み込んだ画像データを取得できます。**

```js
// loadQueueからロードした画像データを取得
var image = loadQueue.getResult('hoge');
```

この時点での`image`の中身は`<image />`のようなタグ情報になっているのでこのままではテクスチャテクスチャーとして使用できません。この画像データを**`THREE.Texture`のインスタンス生成時に引数として渡すことで初めてthree.jsで扱えるテクスチャーオブジェクトに変換されます。**

```js
// three.jsで使えるテクスチャーに変換
var texture = new THREE.Texture(image);
```

**`needsUpdate`を`true`に設定することを必ず忘れないようにしてください！！**この設定を忘れるとテキスチャーが反映されず黒いMeshが表示されてしまいます。

```js
// 【重要】更新を許可
texture.needsUpdate = true;
```

## 4. ロード開始

最後に`loadManifest()`メソッドを使ってマニフェストで指定した画像の読み込みを開始します。

```js
// テクスチャーのロードを開始
loadQueue.loadManifest(manifest);
```

# 使い方

読み込んだテクスチャーはそのままマテリアルの生成時にプロパティーとして渡すことで使用できます。

```js
var material = new THREE.MeshPhongMaterial({
  map: texture
});
```

尚、上記ではTHREE.Textureで説明しましたが**THREE.CubeTextureでも同じように読み込んだ画像から生成できます。SkyBoxでも同じ様に使えるので便利です。**

```js
// キューブテクスチャー
var texture = new THREE.CubeTexture([
  loadQueue.getResult('px'),
  loadQueue.getResult('nx'),
  loadQueue.getResult('py'),
  loadQueue.getResult('ny'),
  loadQueue.getResult('pz'),
  loadQueue.getResult('nz')
]);
texture.needsUpdate = true;
```


# まとめ :bow: 

今回紹介した方法を用いれば、**テクスチャが読み込み終わっていないのにモデルが表示されてしまうなどの問題も解決できます**。さらに、PreloadJSはテクスチャーだけでなく音楽ファイルも読み込めるのでthree.jsのローダーは使用せずにアセットのロードは**PreloadJSで一元管理したほうがプロジェクト内の見通しが良くなると思います。**アセットの読み込み周りでお困りであれば一度試してみてはいかがでしょうか。