WebGL対応のライブラリThree.jsを爆速にする方法

前回のWebGL(Three.js)とStage3D(Away3D)の比較ですが、Mr.doobさんを始め国内外の多くの方からご指摘頂きWebGL(Three.js)版を最適化したところ、最終的にはFlash(Away3D)版と同じぐらいのパフォーマンスになりました。当初、最適化・検証不足で間違った情報を掲載してしまい申し訳ありませんでした。

さて、そのWebGL(Three.js)版を最適化した手法が有意義だったのでシェアしたいと思います。

デモの紹介

まずはこちらの2つのデモの再生を比較してみてください。WebGL対応のブラウザ(例:Google Chrome)でご覧ください。

▼最適化前

▼最適化後

どうでしょう?
圧倒的に後者のほうが滑らかに再生できているのではないかと思います。
後者のほうは配置している3Dのオブジェクト数が10倍近く多いにもかかわらずです。

※ちなみにFlash Stage3Dだとこの6倍以上の100万ポリゴンで60fpsを達成しました。

最適化の秘密

最適化の秘密を紹介したいと思います。Flash(Away3D)の最適化でも紹介したのと同様、GPUを用いるコンテンツにおいてドローコール(描画の命令)の回数はパフォーマンスに最も影響します。そのためドローコールを少なくすることが3Dコンテンツ制作の際に最も気に留めるべきポイントとなります。

大量の3Dオブジェクトを表示する場合は、3Dオブジェクトのジオメトリ(3D形状における頂点の座標群)を結合することによってGPUに対するドローコールを少なくすることができます。Three.jsでは、THREE.GeometryUtils.merge()メソッドでジオメトリを結合できます。コードは次のように記述します。

// 空のジオメトリを作成
var geometry = new THREE.Geometry();
// 立方体個別の要素を作成
var meshItem = new THREE.Mesh(new THREE.CubeGeometry(30, 30, 30, 1, 1, 1));

// Box
for (var i = 0; i < 10000; i++) {
    meshItem.position.x = 10000 * Math.random();
    meshItem.position.y = 10000 * Math.random();
    meshItem.position.z = 10000 * Math.random();

    // ジオメトリを結合
    THREE.GeometryUtils.merge(geometry, meshItem);
}

// マテリアルを作成
var material = new THREE.MeshBasicMaterial({color:"#F00"});

// 3D空間に追加
var mesh = new THREE.Mesh(geometry, material);
container.add(mesh);

3Dオブジェクトを一個一個3D空間に追加して表示するよりは、大量の立方体をまとめた巨大な3Dオブジェクトを1個だけ3D空間に追加したほうが負荷が少ないという感じです。前者はGPUに対して一個一個ドローコールが発生するのですが、後者だと1回にまとめて(厳密には転送できる頂点の個数で分割した回数だけ)ドローコールが発生します。

注意点

ジオメトリをまとめてしまうので、3Dオブジェクト(Meshのインスタンス)としては一つになります。そのため、個別にマテリアルを設定したりマウスイベントを設定するのはできなくなります(工夫次第ではできると思いますが、簡単にはできなくなります)。
そのため、同じような形状のジオメトリを大量に表示させたい場合にこのテクニックを使ってみるといいかもしれません。

また3Dモデルを用意するときも、出来る限りメッシュは分割せずに用意するといいでしょう。このあたりの最適化テクニックはFlash Stage3D勉強会で触れられていたので、そちらの資料も参考にしてみるといいでしょう。

Stage3D勉強会(第2回) – 発表資料 | ClockMaker Blog
Stage3D 勉強会 (第3回)のほうもよければご参加ください

追記(平成29年11月8日):最新版リビジョンr88でのジオメトリの結合方法をICS MEDIAに投稿しました。最新の情報は次の記事を参考ください。

 

投稿者 : 池田 泰延

BookMark

ブックマークはこちらからどうぞ。

このエントリーをはてなブックマークに追加

Comment/Trackback 3件