WebGL 2.0 と WebGL 1.0
WebGL の次なる世代へ
WebGL の初版となる 1.0 が正式勧告されたのは 2011 年のことでした。
そして、2016 年に開催された SIGGRAPH では WebGL 2.0 の conformance test が NVIDIA のドライバで all pass になったことが発表されました。
いよいよ本格的に WebGL 2.0 を使える状態が近づいてきており、もう数年のうちに WebGL 2.0 が PC だけでなくモバイル端末でも普通に使える状態になると予想できます。ウェブにおける圧倒的な表現力を誇る WebGL は、新たなステージへと歩を進めようとしています。
しかし、WebGL のバージョンが上がることによって起こる変化とはどのようなものなのでしょうか。
本記事では、WebGL 2.0 における代表的な feature をご紹介したいと思います。
OpenGL ES との関係
WebGL はそもそも「OpenGL ES のバインディングである」という記述をよく見かけますね。これはもう少し砕けた言い方をすると、OpenGL ES 相当のグラフィックス API を、JavaScript の API として実行できるのが WebGL であるという意味になります。
OpenGL ES はモバイル向けの軽量な OpenGL 実装ですが、その機能をそのままブラウザ上で実行できる形にしたのが WebGL なのですね。そして、WebGL 1.0 は OpenGL ES の 2.0 がベースになっています。一方で WebGL 2.0 の場合は、OpenGL ES 3.0 がベースになっています。つまり、WebGL における 1.0 から 2.0 へのバージョンアップでは、OpenGL ES での 2.0 から 3.0 への変更点がほぼ同様に引き継がれた形になります。
また、このバージョンアップに伴い、OpenGL 用のシェーダ記述言語である GLSL についても変更が加えられます。
より具体的には、WebGL 1.0 のときに利用されていた GLSL ES 1.0 から、GLSL ES 3.0 へとバージョンが上がります。なんで途中の 2.0 が抜けているのかという疑問は誰しも抱くかと思いますが、OpenGL 系では必ずしもバージョン番号が連番になっていない場合も多いのであまり気にする必要はないでしょう。単純に、新しくなった GLSL が使えるようになったと認識していれば問題ありません。
OpenGL ES の 3 系では、3.0、3.1、3.2 でかなり機能が違っています。ジオメトリシェーダやコンピュートシェーダなどの新しいシェーダについては、残念ながら 3.0 系ではサポートされていません。言い換えると、WebGL 2.0 においても、これまでどおり頂点シェーダとフラグメントシェーダの二種類のシェーダのみがサポートされます。GLSL は ES 3.0 になりバージョンが新しくなるわけですが、だからといって、新しいシェーダを使えるようになるわけではないので、その点は紛らわしいため注意が必要です。
バージョン関連の相関
- WebGL 1.0 → OpenGL ES 2.0 相当
- WebGL 1.0 → GLSL ES 1.0 を利用
- WebGL 2.0 → OpenGL ES 3.0 相当
- WebGL 2.0 → GLSL ES 3.0 を利用
- WebGL 2.0 → シェーダの種類は変更なし
WebGL 2.0 と WebGL 1.0 の共存
WebGL 2.0 が使えるようになったと言われて、もしかすると WebGL 1.0 で作られたこれまでの成果が無駄になるのではないか? と感じる人がいるかもしれません。
しかしこれについては心配する必要はありません。
従来通り、ブラウザ上では WebGL 1.0 の機能を引き続き利用することができます。むしろ、WebGL 2.0 は明示的に有効化しない限り利用されることがないため、これまでに実装した WebGL 1.0 系の API を利用しているコンテンツの実行に対してなんの影響もありません。
また、ここからが少し紛らわしいのですが、WebGL 2.0 を有効化した状態でその機能を使っている場合であっても、GLSL は必ずしも ES 3.0 を使う必要は無い ということに留意しましょう。
これは GLSL ES 3.0 について説明する項で詳しく触れますが、GLSL ES 1.0 についてもこれまでどおりに利用できます。というよりむしろ、こちらもやはり明示的に有効化しない限り使われません。ですから、WebGL 2.0 を使っているからといって絶対に GLSL ES 3.0 を使わなければならないのかというと、そういうわけではないのですね。
ただし、WebGL 2.0 の機能のなかには、GLSL ES 3.0 を使っていないと正しく機能しないものがあります。これについては各項目についてそれぞれに影響の有無が異なるためにひとくくりにはできません。
簡単にまとめると、WebGL 2.0 は有効化しない限り強制的に切り替わってしまうようなことはなく、これまでどおり WebGL 1.0 を利用した開発を引き続き行うことができます。ですから当面は、無理に WebGL 2.0 へ移行する必要は無いと言えます。
そして、WebGL 2.0 を利用している場合でも、GLSL ES 1.0 は引き続き利用できる状態であり、むしろこちらに関しても、意図的に有効化しない限りは従来通りの GLSL ES 1.0 相当が使われます。ただし、WebGL 2.0 で追加された機能を完全に使いこなしたければ、必然、GLSL ES 3.0 を使っていく必要が遠からずやってきます。個人的には、WebGL 2.0 を使うことに決めたのなら、いっそ GLSL ES 3.0 を用いた記述スタイルに完全にシフトし、その記法や雰囲気に慣れるべきです。そうすることで、いざ必要になったときにスムーズに移行することができるはずだからです。
ただし、学習コストなどを考えると一概にただ新しいものを使えばいいというわけではないでしょう。そのあたりは、自身の学習スタイルに合わせて適宜選択していきましょう。
さて、それでは次項より、具体的に新しく追加された機能についてひとつひとつ見ていきます。
MRT(Multiple render targets)
WebGL 1.0 では拡張機能扱いだった MRT が標準化されました。これにより、遅延レンダリングなどのよりハイレベルなシェーディングテクニックが既定で行えるようになりました。
MRT はその名のとおり、複数のレンダリングターゲットに同時に出力を行うことができる技術です。これまでは、たとえばフレームバッファに対して法線や深度の情報を書き出す際、その都度、個別に描画命令を発行し gl_FragColor
に法線や深度を出力してやる必要がありました。しかし MRT を用いる場合は、たった一度のドローコール(gl.drawArrays
などの描画命令)で複数のバッファへの一括同時出力が行なえます。
参考: wgld.org | WebGL: MRT(Multiple render targets) |
VAO(Vertex Array Object)
こちらも拡張機能からの格上げとなります。名前が VBO(Vertex Buffer Object) と似ていますが、全くの別物です。
従来、VBO や IBO といった頂点に関するデータを格納したバッファは、個別にひとつひとつバインド処理を行ってやる必要がありました。ですから、たとえば複数の形状を描きたい場合、モデルデータを変更するたびに、大量のバインド処理を行ってやる必要があったのですね。
VAO はこれらの頂点に関するバッファ類をまとめてひとつのオブジェクトとして扱うことができる概念です。複雑で冗長なバインド処理を簡潔に記述できるようになる VAO は、デメリットが無く、使わない理由がありません。これまでは拡張機能扱いでしたが、これがデフォルトで有効化されるわけですから積極的に使っていきたいところです。
参考: wgld.org | WebGL: VAO(vertex array object) |
インスタンシング(instanced arrays)
こちらも拡張機能から基本機能への格上げを果たしたもののひとつです。
複数の異なる状態を適用しながら大量のオブジェクトを描画することができるインスタンス描画は、これまで以上に物量感溢れるシーンを構築するのに役立つでしょう。
これもやはり、今までは拡張機能扱いだったので若干使いにくい部分がありましたが、標準機能へと格上げされたことにより、気兼ねなく使えるようになりました。
参考: wgld.org | WebGL: インスタンシング(instanced arrays) |
Transform feedback
こちらはこれまでの WebGL 1.0 にはなかった機能です。
Transform feedback は、CPU を介さずに、GPU から直接バッファへの出力が行える機能です。と、そんなふうに言われてもあまりピンとこないという人が多いかもしれませんが、これは非常に強力な機能です。
たとえば、これまで GPU によって処理された値を再利用したければ、カラーバッファに値を出力してテクスチャとして参照しなおすしか方法がありませんでした。しかし Transform feedback では、VBO などのバッファに対して直接シェーダからの書き出しが行えます。つまり CPU 側(JavaScript)で VBO の中身を更新するのではなく、シェーダだけでバッファの中身をガリガリと書き換えていくことができるのですね。
WebGL 2.0 ではコンピュートシェーダこそ使えませんが、直接バッファの中身をシェーダで書き換えていくことができるようになったことで、これまで以上に GPGPU 的な処理が行いやすくなったと言えるでしょう。
UBO(Uniform buffer object)
UBO は、uniform 変数を構造体のようにひとつのかたまりとして処理できる機能。
これまでの WebGL 1.0 系では、同じ構造を持つ uniform 変数群であっても、別々のシェーダでそれらを共有することはできませんでした。UBO を用いることで、複数のシェーダに対して全く同じ内容の値を uniform 変数として送り込むことができるようになります。
ただし、名前からも想像できるかと思いますが、こちらは VBO などと同じようにバッファオブジェクトとして振る舞うため、バインドなどの処理を行ってやる必要があり、必ずしも記述が簡素化されるとは限りません。その性質を正しく理解したうえで使っていく必要があるでしょう。
Sampler object
WebGL や OpenGL を扱ったことがある人であれば、GLSL などで「sampler」という記述を見たことがあるでしょう。
非常にざっくりとした理解として、sampler とはテクスチャのことだと理解している方がいるかもしれませんが、ここで言う Sampler object は、テクスチャそのものを指しているわけではありません。
sampler とは、簡単に言うとテクスチャをどのようにサンプリングするかという意味に相当するものです。そして、Sampler object とはそのサンプリングルールをひとつのオブジェクトとして独立させることで、テクスチャを参照する際のサンプリング方法を扱いやすくしたものですね。
テクスチャ関連の変更
WebGL 2.0 において最も大規模な変更となるのがテクスチャ関連です。
あまりにも影響範囲が大きいので個別には書きませんが、かなりの数の大小の変更があります。
たとえば浮動小数点テクスチャなどは既定でサポートされます。また、テクスチャのサイズとして 2 の累乗サイズのみだった大きさの縛りがなくなります。
全体的に、まず対応するテクスチャフォーマットがかなり増えます。3D テクスチャや、2D テクスチャ配列、さらには R 成分のみのテクスチャなど、これまでの WebGL 1.0 では存在すらしなかった多くのテクスチャフォーマットが既定で利用できるようになっています。
このあたりは GLSL の記述にもかなり関わってくる部分ですので、一度にその全容を把握するのは難しいかと思います。ただ、これまで以上に柔軟に、さまざまなテクスチャを使ったテクニックが利用できるようになるのは間違いないので、ひとつひとつ確実に、用途に応じて利用していきましょう。
バッファ間のやりとり
WebGL 2.0 では、バッファ間で値をコピーするような機能もいくつか追加されています。
フレームバッファの内容を転送したり、VBO の内容をコピーしたりといったような、バッファレベルでデータをやりとりする方法が追加されています。
Query object
WebGL 2.0 ではクエリという概念が新しく出てきます。
これは最初はイメージするのが難しいかと思いますが、なにかしらのクエリを発行し、その結果を動的に取得しながら処理を行うといったことができるようになります。
たとえばオクルージョンカリングなどの技術を実現するのに欠かせない概念です。
まとめ
さて、かなり代表的な部分に限定して紹介してきましたが、いかがでしたでしょうか。上記で紹介した以外にも、細かいところでは様々な変更があり、WebGL 1.0 では難しかったようなことが比較的あっさり実現できるようになっているのはとても大きいですね。
ただ、最初のほうにも書きましたが、従来の WebGL 1.0 の機能が使えなくなるというわけでもなく、また逆に WebGL 2.0 を使うことによって難易度が跳ね上がる部分も出てくるかと思いますので、単純に新しい方を使えば盤石というわけではないので気をつけましょう。
また、ここではあまり深く言及しませんでしたが、WebGL 2.0 の変更もさることながら、GLSL ES 3.0 における変更点はかなり項目も多く、理解するのが難しそうに見える点が多いです。こちらは別稿に詳細を譲りますが、やはり、焦らず少しずつ、新しい機能を確実にものにしていくという姿勢が大事なのかもしれません。
自分のペースで構いませんので、じっくり腰を据えて取り組んでいきましょう。