5000人の中からキャラを見つけ出せ!Point Spriteを活用したJRAスペシャルコンテンツ
懐かしさと共に思わず心まで埋没しそう
今回は、カヤック所属のエンジニア、たじーさんから寄稿をいただきました!
なんと、あの JRA の公式コンテンツが WebGL で実装されているという話です。しかもコンテンツはちょっと懐かしさ漂う、あの作品をテーマに取り入れたものになっていて、物量感がとても爽快な作品となっています。
今回も、単なるコンテンツの紹介や宣伝ではなく、その実装の裏話も事細かく語ってくださいました。
正直言って私なんかではとても思いつかないようなびっくりする方法を使って実装されているんだなあということが、以下の寄稿記事を読むとわかると思います。これは単にコンテンツをプレイしていただけでは、恐らく気が付かなかった実装の工夫だと思います。
生の実装者の貴重な体験談、ぜひご覧になっていただければと思います。
はじめに
JRA第60回 有馬記念のスペシャルコンテンツが12月22日に公開されました。
このコンテンツでは、WebGLによって描画された約5000人のキャラクターの中から指定された3人を見つけ出すという、なかなか難易度の高いゲームとなっています。
これだけ大量のキャラを同時に描画し、さらにアニメーションさせるために、WebGLのPoint Spriteという技術を活用しています。
Point Spriteとは、WebGLによって描画される頂点の1つ1つに対してテクスチャをマッピングさせる技術です。
このことによって、ビルボードにテクスチャをマッピングする場合よりも圧倒的に高速に大量のキャラを描画することができるのです。
とはいえ、Point Spriteの実装を幅広いOSや端末に対応させるのは一筋縄ではいかず、主にGPUのスペックに応じていくつかの問題にぶつかりました。
今回はその中から、特に大きな問題となった2つと、それに対する解決アプローチについて書かせていただこうと思います。
gl_PointSizeの上限値問題
Point Spriteでは、1つの頂点に対してキャラをマッピングしていくので、頂点自体の大きさを調整することでキャラの大きさ・近さを表現する必要があります。
しかし、ここで問題となるのは、この頂点サイズの上限値が端末環境によって大きく異なる ということです。
調査した端末の中では最小で64、最大で8191まで開きがありました。
つまり、頂点サイズの上限値が大きい端末の場合では、
ここまでカメラを寄せてキャラを大きく見せても問題ないのですが、上限値の小さい端末ではこんなに大きくキャラを描画することができません。
従って、カメラが近づくほどに遠近感が崩壊してしまう結果となります。
これに対する解決策として、頂点サイズの上限値が小さい端末では、1人のキャラを複数個の頂点によって描画する という風にしました。
具体的には、
(1) アクセスされた端末の頂点サイズ上限値をJavaScriptで取得する
頂点サイズの上限値を取得するコード.js
var gl = document.createElement('canvas').getContext('webgl') ||
document.createElement('canvas').getContext('experimental-webgl');
var point_size_max = gl.getParameter(gl.ALIASED_POINT_SIZE_RANGE)[1];
(2) 取得した上限値をもとに、各キャラを描画する頂点数を決める 今回は、上の画像のように1つの頂点で1人のキャラをそのまま描画するパターンに加えて、
- 4つの頂点を使ってキャラを描画するパターン
- 9つの頂点を使ってキャラを描画するパターン
を制御できるプログラムを書き、頂点サイズ上限値によって出し分けました。
当然、1人のキャラに費やす頂点数が増えるほど、処理の負荷も高くなってしまうのですが、FPSが下がってくるとキャラの人数自体を減らすというプログラムも同時に書いたことで、最低限のフレームレートは保てる工夫もしています。
gl_PointCoord.y の向きが定められていない問題
頂点内の座標を取得するためのGLSL変数としてgl_PointCoordというのがあります。
ほとんどの端末では、このgl_PointCoordのy軸は上から下への方向によって値が振られていますが、ごく一部の端末では下から上への方向で値が振られているものがありました。
y軸方向が逆になっている端末で同じプログラムを実行すると、キャラが全て逆さまに描画されてしまう事態となります。これは直さなければなりません。
ところが厄介なことに、WebGLではこのy軸方向がどちらに向いているのかを直接取得することができません。(OpenGLの場合、GL_POINT_SPRITE_COORD_ORIGINという変数によって取得することができます)
従って、何か別の方法でアクセスされた端末のy軸方向を検知する方法を考えました。
その結果、ゲーム画面の実行前に、gl_PointCoord.yの値をそのまま画像に書き出してみることでy軸方向を検知する という対策をとることにしました。
例えば、gl_PointCoord.yのy軸方向が下向きの場合、以下のような画像が書き出されます。gl_PointCoord.yの値が下に行くほど大きくなる(0 -> 255)ためです。
同様に、y軸方向が上向きの場合は画像の上部分ほど明るい画像が書き出されます。
この書き出された画像の画素値を取得し、明るい部分は上部か下部かを検出することで、実質的にgl_PointCoord.yのy軸方向を取得することができました。
ただしこれはハック的な方法なので、本来であればOpenGLと同様にGL_POINT_SPRITE_COORD_ORIGIN変数がWebGLでも使用可能になることが望まれます。
最後に
Point Spriteは、簡単な計算を高速に実行できるというGPUの特性をよく活かした面白い技術です。
GLSLの知識や、上記のようにGPUごとの対応策も考える必要がありますが、うまく利用すればインパクトのある画を生み出すことができます。
今回のJRAスペシャルコンテンツは、12月28日 までの期間限定コンテンツとなりますので、是非お早めに体験ください!