WebGL+Retina の落とし穴!? Retina ディスプレイで WebGL のレンダリング結果を正しく表示する tips
今後は対応すべき場面が増えそうな高解像度設定
今回ご紹介するのは、Apple 社の製品で使われている Retina ディスプレイなどの、高解像度のディスプレイを搭載した端末用の tips です。
スマートフォンを中心に高解像度化がどんどん進んでいる現代。
今後はこういった高解像度端末への対応が必要になる場面もきっと増えていくと思います。高解像度ディスプレイでの WebGL の扱いについて検証してみます。
※本記事の執筆には @Stocker_jp さんに多大なるアドバイスをいただきました。ありがとうございます!
高解像度ディスプレイに潜む WebGL に科せられた罠とは
iPhone や最新の iMac など、最近の Apple 社の製品の多くに Retina ディスプレイと呼ばれる高解像度ディスプレイが採用されています。
本来なら 1 ピクセルとして表示される色の情報が、Retina ディスプレイなどの場合引き延ばされて 2 x 2 など、本来よりも多くのピクセルを用いたサイズで表示されます。これにより、スケーラブルなフォントなどのデータのレンダリングにおいては、非常に高精細で美しいレンダリングを行うことが可能になります。
左が Retina、右が素の解像度の場合。
ただし、WebGL を扱う上でも欠かすことのできない Canvas エレメントなどの場合、この引き延ばされて表示されるということが逆に仇になってしまう場合があります。
本来のピクセルが引き延ばされる過程で、どうしてもぼやけたような印象の描画結果になってしまうのですね。
せっかくの高解像度ディスプレイなのに、逆にレンダリング結果がぼんやりとした低品質なものになってしまうのはあまりにもったいない。どうにかしてこれに対応したいと考えるのが普通でしょう。
これに正しく対応するには、エレメントの大きさを ディスプレイの解像度に合わせたサイズに拡大 したうえで、見た目上は本来の大きさで表示されるように スタイルを適用 してやります。
1 ピクセルが倍の 2 x 2 に引き延ばされてしまうのなら、実際の大きさと同じサイズにしてしまおうというわけです。
エレメントの大きさ ⇒ 拡大する
スタイルの当て方 ⇒ 本来の大きさになるように縮める
WebGL の場合には、WebGL コンテキストオブジェクトを取得する段階で、内部的なフレームバッファの解像度が Canvas エレメントと同じサイズで生成されますので、その前までに Canvas のサイズを適切な大きさに修正するようにしておくとなにかとスムーズです。※もちろん viewport の設定でも対応できます。
しかし、ここでひとつ問題があります。
Retina ディスプレイではない通常の解像度だった場合に、もし Canvas が無条件で倍の大きさになってしまっては困ってしまいますよね。
javascript 側で Retina などの高解像度ディスプレイなのかどうかを、リアルタイムに判別してやらなくてはいけないわけです。もちろん、そのための方法はきちんと用意されています。
javascript による動的な解像度情報の取得
javascript で動的に解像度情報を得るには、window.devicePixelRatio
を利用します。
たとえば本来の 1 ピクセルが 2 x 2 に引き延ばされてしまうケースでは、devicePixelRatio
の戻り値が 2
になります。この数値をもとに Canvas の大きさを修正すればいいわけですね。
devicePixelRatio を用いた設定の記述例
var dpr = window.devicePixelRatio || 1;
canvas.width = [本来の横幅] * dpr;
canvas.height = [本来の縦幅] * dpr;
このような処理を行うと、Canvas のサイズは解像度に応じて 実サイズと同じか、より大きくなる ことがわかりますね。
並行して、CSS でスタイルを当てる際には 本来の大きさに見えるように 指定を行ってやります。
こうすることで、CSS の効果で画面上では本来の大きさの Canvas が表示されつつも、内部的な Canvas の大きさは解像度に応じた本来よりも大きいものとなり、結果、レンダリングされる結果がぼやけてしまうことなく正しく表示されるようになるというわけですね。
@Stocker_jp さんにキャプチャしてもらった画像を見ると、本来のアンチエイリアスのレベルで正しくレンダリングされているのが確認できます。※等倍画像
負荷増加に注意!
当り前といえば当たり前ですが、このような措置を行うと内部的には Canvas を大きくした分だけ描画の負荷が増えます。
もともとの Canvas のサイズが仮に 512px だとします。
これを devicePixelRatio
の戻り値にのっとって仮に二倍にしたとすれば、当然 1024px の Canvas に対する描画負荷に相当する、余分な負荷が掛かることになります。
また、Android 端末などの場合、devicePixelRatio
の戻り値として 3
を返してくる端末もあるようですし、なかなか闇の香りが漂う感じになっています。
WebGL をフルスクリーンで描画するようなタイプのデモで、もしスクリーンサイズが三倍とかになるととんでもない負荷増になることが想像できます。このあたりはよくよく考えて実装する必要があるでしょう。
結局のところ、プラットフォームとしてどこまでを許容するのかが肝になるでしょう。なんでもかんでも高解像度対応するというよりは、ケースバイケースで必要に応じて実装するのがいいのではないでしょうか。
参考リンク:
HandlingHighDPI - WebGL Public Wiki ※Khronos 純正 Wiki
Enable High DPI display support. by jherrm · Pull Request #7 · gpjt/webgl-lessons · GitHub ※比較画像あり