ホビットの冒険

Mobile WebGL で中つ国に命を吹き込む

これまで、マルチメディアを多用したインタラクティブなウェブベース エクスペリエンスをモバイルやタブレットにもたらすことは困難でした。主な制約は、パフォーマンス、API の可用性、デバイス上の HTML5 オーディオの制限、シームレスなインライン動画再生の欠如でした。

今年の初め、Google は Google と Warner Bros. の仲間たちとともに、ホビットの新作映画『ホビットの荒野行動』のモバイル ファーストのウェブ エクスペリエンスを構築するプロジェクトを開始しました。マルチメディアを多用するモバイル Chrome 試験運用版の構築は、刺激的で困難な作業でした。

このエクスペリエンスは、WebGL とウェブ オーディオを利用できるようになった新しい Nexus デバイスの Chrome for Android 向けに最適化されています。ただし、ハードウェア アクセラレーションによる合成と CSS アニメーションにより、WebGL 以外のデバイスやブラウザでもエクスペリエンスの大部分にアクセスできます。

このゲームはすべて、中つ国とホビット映画に登場する場所と登場人物の地図をベースにしています。WebGL を活用することで、『ホビット』三部作の豊かな世界をドラマ化して探索し、ユーザーがエクスペリエンスを制御できるようにしました。

モバイル デバイスでの WebGL の使用に関する課題

まず、「モバイル デバイス」という用語は非常に広範です。デバイスの仕様は千差万別です。そのため、デベロッパーは、より簡単なエクスペリエンスでより多くのデバイスをサポートするか、今回の例のように、サポートされているデバイスを、よりリアルな 3D 世界を表示できるデバイスに限定するかを決定する必要があります。「中つ国への旅」では、Nexus デバイスと 5 つの人気の Android スマートフォンに焦点を当てました。

テストでは、これまでの WebGL プロジェクトの一部と同じように、three.js を使用しました。実装にあたり、Nexus 10 タブレットで適切に動作するトロールショー ゲームの初期バージョンを作成しました。デバイスで初期テストをいくつか行った後、低スペックのノートパソコンで通常使用するものとほぼ同じものを念頭に置いた最適化のリストを作成しました。

  • ローポリモデルを使用する
  • 低解像度のテクスチャを使用する
  • ジオメトリを結合して、描画呼び出しの回数をできるだけ減らします。
  • 素材と照明をシンプルにする
  • ポスト エフェクトを削除してアンチエイリアスをオフにする
  • JavaScript のパフォーマンスの最適化
  • WebGL キャンバスをハーフサイズでレンダリングし、CSS でスケールアップする

これらの最適化をゲームの最初の大まかなバージョンに適用した後、フレームレートは 30 FPS で安定し、満足のいくものとなりました。その時点での目標は、フレームレートに悪影響を与えることなく映像を改善することでした。さまざまな手法を試したところ、パフォーマンスに大きく影響する手法もあれば、期待ほど大きな効果が得られなかった手法もあります。

ローポリモデルを使用する

モデルから始めましょう。ローポリ モデルを使用すると、確実にダウンロード時間を短縮し、シーンの初期化に要する時間を増やすことができます。その結果、パフォーマンスに大きな影響を与えることなく、複雑さを大幅に増やすことができることがわかりました。このゲームで使用しているトロールモデルは約 5K 顔で、シーンは約 40K 顔で問題なく機能します。

トロールの森の 1 頭
トロールの森のトロールの一つ

エクスペリエンスの別の(まだリリースされていない)ロケーションでは、ポリゴンの削減によるパフォーマンスへの影響が大きくなりました。その場合、モバイル デバイスでは、デスクトップ用に読み込んだオブジェクトよりも低ポリゴンのオブジェクトを読み込みました。複数の 3D モデルを作成するには追加の作業が必要ですが、必ずしも必要であるとは限りません。着手するモデルがどれほど複雑かに大きく依存します。

多数の物体を含む大きなシーンで作業するときは、ジオメトリの分割方法を戦略的に行うようにしました。これにより、重要性の低いメッシュのオンとオフをすばやく切り替え、すべてのモバイル デバイスで機能する設定を見つけられるようになりました。その後、ジオメトリを実行時に JavaScript で結合して動的な最適化を行うか、本番前環境で統合してリクエストを保存するかを選択できます。

低解像度のテクスチャを使用する

モバイル デバイスでの読み込み時間を短縮するため、デスクトップのテクスチャの半分のサイズに、さまざまなテクスチャを読み込むことにしました。すべてのデバイスが最大 2048x2048px までのテクスチャ サイズを処理でき、ほとんどのデバイスが 4096x4096px を処理できることがわかりました。個々のテクスチャのテクスチャ ルックアップを GPU にアップロードすると、問題はないようです。テクスチャの合計サイズが GPU メモリに収まるようにして、テクスチャが常にアップ / ダウンロードされないようにする必要がありますが、ほとんどの場合、これはほとんどのウェブ エクスペリエンスでは大きな問題ではありません。ただし、描画呼び出しの回数を減らすには、テクスチャを組み合わせてできる限り少ないスプライト シートにすることが重要です。これはモバイル デバイスのパフォーマンスに大きな影響を与えます。

トロールの森に生息するトロールのテクスチャ
トロールの森のトロールのテクスチャ
(元のサイズ 512×512 ピクセル)

素材と照明をシンプルにする

素材の選択もパフォーマンスに大きく影響する可能性があるため、モバイルでは慎重に管理する必要があります。パフォーマンスの最適化には、3.js で MeshPhongMaterial(テクセルごとのライト計算)ではなく MeshLambertMaterial(頂点ごとのライトの計算)を使用しています。基本的には、ライティングの計算をできるだけ少なくするシンプルなシェーダーを使用しようとしました。

使用するマテリアルがシーンのパフォーマンスにどのように影響するかを確認するには、MeshBasicMaterial を使用してシーンのマテリアルをオーバーライドします。比較がしやすくなります。

scene.overrideMaterial = new THREE.MeshBasicMaterial({color:0x333333, wireframe:true});

JavaScript のパフォーマンスを最適化する

モバイルゲームを構築する際に GPU が最大の課題となるとは限りません。特に物理アニメーションやスケルトン アニメーションには、CPU に多くの時間が費やされます。シミュレーションによっては、このような負荷の高い計算を 2 フレームおきにしか実行できないことがあります。オブジェクト プーリング、ガベージ コレクション、オブジェクト作成に関しては、利用可能な JavaScript 最適化手法を使用することもできます。

新しいオブジェクトを作成する代わりに、事前に割り当てられたオブジェクトをループで更新することは、ゲーム中のガベージ コレクションの「中断」を回避するうえで重要なステップです。

たとえば、次のようなコードがあるとします。

var currentPos = new THREE.Vector3();

function gameLoop() {
  currentPos = new THREE.Vector3(0+offsetX,100,0);
}

このループの改善版では、ガベージ コレクションが必要となる新しいオブジェクトの作成を回避できます。

var originPos = new THREE.Vector3(0,100,0);
var currentPos = new THREE.Vector3();
function gameLoop() {
  currentPos.copy(originPos).x += offsetX;
  //or
  currentPos.set(originPos.x+offsetX,originPos.y,originPos.z);
}

可能な限り、イベント ハンドラではプロパティの更新のみを行い、requestAnimationFrame レンダリング ループにステージの更新を処理させます。

もう 1 つのヒントは、レイキャスティング演算の最適化や事前計算を行うことです。たとえば、静的なパス移動中にオブジェクトをメッシュにアタッチする必要がある場合は、1 回のループ中に位置を「記録」し、メッシュに対してレイキャストするのではなく、このデータからデータを読み取ることができます。または、Rivendell エクスペリエンスと同様に、レイキャストを使用して、よりシンプルなローポリの目に見えないメッシュでマウス操作を探します。ハイポリ メッシュでの衝突検出は非常に低速であるため、通常はゲームループでは回避する必要があります。

WebGL キャンバスをハーフサイズでレンダリングし、CSS でスケールアップする

WebGL キャンバスのサイズは、パフォーマンスを最適化するために調整できる最も効果的なパラメータの 1 つです。3D シーンの描画に使用するキャンバスが大きいほど、各フレームで描画する必要があるピクセル数が多くなります。もちろん、これはパフォーマンスに影響します。2,560×1,600 ピクセルの高密度ディスプレイを搭載した Nexus 10 は、低密度タブレットの 4 倍のピクセル数を搭載する必要があります。これをモバイル向けに最適化するためにトリックを使用します。この手法では、キャンバスのサイズを半分(50%)に設定し、ハードウェア アクセラレーション CSS 3D 変換を使用して目的のサイズ(100%)に拡大します。この方法の欠点は、画像がモザイク化され、細い線が問題になることがありますが、高解像度の画面ではそれほど悪くありません。パフォーマンスの向上をぜひご検討ください。

Nexus 10(16 FPS)でキャンバスを拡大縮小せずに 50%(33 FPS)に拡大した同じシーン
Nexus 10(16 FPS)でキャンバスを拡大縮小せずに 50%(33 FPS)に拡大した同じシーン

構成要素としてのオブジェクト

私たちは、ドル グルドゥール城の大きな迷路と終わりまで続く崖の谷を作り出すために、構成要素の 3D モデルを作り、その構成要素を再利用しました。オブジェクトを再利用することで、エクスペリエンスの途中ではなく開始時にオブジェクトをインスタンス化してアップロードできます。

ドルグルドゥアの迷路で使われる 3D の物体ビルディング ブロック。
ドルグルドゥアの迷路で使用される 3D オブジェクトの構成要素。

Rivendell にはいくつかの地面のセクションがあり、ユーザーのジャーニーの進行に合わせて常に Z 深度の位置を変更します。ユーザーがセクションを通過すると、それらは遠くに移動します。

ドルグルドゥア城では、ゲームごとに迷路を再生したいと考えていました。そのために、迷路を再生成するスクリプトを作成しました。

最初からストラクチャ全体を 1 つの大きなメッシュに統合すると、シーンが非常に大きくなり、パフォーマンスが低下します。この問題に対処するため、Google は構成要素が表示されるかどうかに応じて、構成要素を表示 / 非表示にすることにしました。当初から 2D raycaster スクリプトの使用について考えていましたが、最終的には組み込みの three.js 視錐台カリングを使用しました。プレーヤーが直面している「危険」に焦点を当てるために、raycaster スクリプトを再利用しました。

次に重要となるのはユーザー操作ですデスクトップではマウスとキーボードで入力し、モバイル デバイスではタップ、スワイプ、ピンチ、デバイスの向きなどを行います。

モバイルウェブ エクスペリエンスでのタップ操作の使用

タップのサポートを追加するのは難しくありません。このトピックに関する有益な記事があります。しかし、ちょっとした問題が複雑になることもあります。

タップとマウスの両方を使用できます。Chromebook Pixel などのタッチ対応ノートパソコンは、マウスとタッチの両方に対応しています。よくある間違いとして、デバイスでタップが有効になっているかどうかを確認してから、タッチイベント リスナーのみを追加し、マウスには何も追加しないというミスがあります。

イベント リスナーのレンダリングを更新しない。代わりにタッチイベントを変数に保存し、requestAnimationFrame レンダリング ループで反応します。これにより、パフォーマンスが向上し、競合するイベントが統合されます。イベント リスナーで新しいオブジェクトを作成する代わりに、オブジェクトを再利用するようにしてください。

これはマルチタッチであることを忘れないでください。event.touches はすべてのタッチの配列です。場合によっては、代わりに event.targetTouches や event.changedTouches を見て、関心のあるタッチに反応する方が興味深い場合もあります。タップとスワイプを分離するために、タップが動いたか(スワイプ)、まだ動いているか(タップ)を確認するまでの遅延を使用します。ピンチを把握するためには、2 つの初期接触間の距離と、それが時間の経過とともにどのように変化するかを測定します。

3D の世界では、カメラがマウス操作とスワイプ操作にどう反応するかを決定する必要があります。カメラの移動を追加する一般的な方法の一つは、マウスの動きを追うことです。これは、マウスの位置による直接制御、またはデルタ移動(位置変更)によって行うことができます。モバイル デバイスとパソコンのブラウザの動作は、必ずしも同じであるとは限りません。広範囲にわたってテストを実施し、各バージョンに適した機能を判断しました。

小さな画面やタッチスクリーンを扱う場合、ユーザーの指と UI インタラクションのグラフィックスが、表示したい内容の妨げになることがよくあります。これは、ネイティブ アプリの設計では慣れ親しんでいることですが、ウェブ エクスペリエンスではこれまで考えたことはありません。これはデザイナーや UX デザイナーにとって大きな課題です。

概要

このプロジェクトで得られた経験全体として、モバイルでの WebGL は、特に新しいハイエンド デバイスにおいて、非常に効果的に機能することがわかりました。パフォーマンスに関しては、ポリゴン数とテクスチャ サイズが主にダウンロード時間と初期化時間に影響しているようです。モバイルのパフォーマンスを最適化するうえで最も重要な要素は、マテリアル、シェーダー、WebGL キャンバスのサイズです。ただし、パフォーマンスに影響する要素はすべての要素の合計であるため、最適化するためにできることは何でもできます。

モバイル デバイスをターゲットにする場合は、タップ操作についてよく考える必要があります。ピクセルサイズだけでなく、画面の物理的なサイズも考慮する必要があります。状況を実際に確認するために、3D カメラを近づけなければならないケースもありました。

テストが開始され、順調な成果が上がっています。お楽しみいただければ幸いです。

お試しになる場合は、ぜひ中つ国への旅に挑戦してください。