WebGL 2.0 と GLSL ES 3.0

GLSL は次なるステージへ

WebGL 2.0 では、GLSL についても大きな変更が掛かっています。

個人的な感覚では、WebGL のバージョンが上がったことによって、WebGL そのものの機能はもちろん強化されているのですが、だからといって WebGL の扱い方が根本的に変わったようには感じません。しかし、GLSL については話が違ってきます。

まず大前提として、WebGL 2.0 では、従来通りの GLSL のバージョンも そのまま利用することができる という点をしっかり把握しておきましょう。

WebGL 1.0 で利用されていた GLSL のバージョンは GLSL ES 1.0 でした。これは、WebGL 2.0 の場合も引き続き利用できるわけです。ただし、意図的に有効化するための記述を施すことにより、WebGL 2.0 からは GLSL ES 3.0 を利用することが可能になります。GLSL そのもののバージョンが上がったことによって、これまでとは全く違った記述方法を用いることになります。

今回はその GLSL ES 3.0 によって変化した部分を中心に、概要をしっかり押さえておきましょう。

GLSL ES 3.0 を有効化する

WebGL 2.0 を利用する場合でも、シェーダについては従来のバージョンがそのまま使えます。これを別の言い方で表すと 明示的に有効化 しない限り、シェーダは GLSL ES 1.0 の従来のバージョンが使われるとも言えます。

それでは、有効化したい場合にはどうすればいいのかというと、シェーダのコードの側に、ある一文を加えてやります。

より具体的には version directive を記述してやる必要があります。このバージョンディレクティブは、必ず GLSL ES 3.0 のシェーダコードの 最初の一行目 に記述するという点に注意しましょう。

バージョンディレクティブの記述例

#version 300 es
// 以下シェーダのコードを記述

WebGL のシェーダの記述には、よく script タグを用いる方法が使われますが、必ず一行目にこのディレクティブが記載されていることが GLSL ES 3.0 を使う上での前提条件になりますので注意しましょう。

たとえば次のように書いたものをそのまま使うと、たぶんエラーになります。

間違ったディレクティブの記述例

<script>
#version 300 es

// 以下シェーダのコードを記述
</script>

このような記述ではシェーダのコードの一行目に、まず最初に改行文字があることになります。これは、ディレクティブよりも前に別の文字があると判断されるため、構文エラーになります。

もしもタグの直後にディレクティブを書くのが嫌だとか、あるいは見づらいなどと感じる場合には、シェーダをコンパイルする前に改行コードを正規表現などで一括置換するようにしておけばいいですね。

なんにしても、まず一行目に正しくバージョンディレクティブを記述していないと、そもそも GLSL ES 3.0 を有効化することができず、単なる構文エラーとして先に進めなくなりますので気をつけましょう。

精度修飾子

GLSL ES 1.0 の頃から、頂点シェーダではデフォルトの精度として highp が保証されていました。しかし、フラグメントシェーダについては最低精度として mediump が保証されていたに留まっていました。

GLSL ES 3.0 では、標準で highp 精度に対応した形になっており、これまでに比べてフラグメントシェーダでも高精度な浮動小数点を扱いやすくなっています。

ただし、これは GLSL 全般、あるいはシェーダ全般に言えることですが、GPU が期待通りの性能を発揮してくれるかどうかはわかりません。場合によっては、精度修飾子によってエラーが起こるケースも絶対に無いとは言い切れないところがあります。

それでも、言語仕様として標準で highp がサポートされたことは喜ぶべきでしょう。

attribute と varying が消えた!?

先述のとおり、GLSL ES 3.0 は、これまでの 1.0 と比較して大きくシェーダの記述の仕方が変わっています。

そのなかでも、特に従来の GLSL に慣れている人が戸惑うと思われるのが attribute と varying の廃止 だと思います。これは使おうと思えば使えるとかそういうことではなく「完全な廃止」です。つまり仮に記述したとしても、これは構文エラーになります。

GLSL ES 3.0 では、これらの代わりに inout を使います。

これまでは頂点属性としての入力は attribute 修飾子付きの変数によって行なっていましたが、この attribute 修飾子の代わりに in 修飾子付きの変数を使います。これは要するに、シェーダに外部から入ってくるデータである、という意味で捉えればいいわけですね。

そしてこれと同様に、varying 修飾子付きの変数についても、頂点シェーダからフラグメントシェーダに出力する際には out を使い、フラグメントシェーダで入力として受け取る際には in を使うわけです。

これまでは attribute と varying というようにそれぞれ別々の名称になっていたものが、共通の inout によって表現されるようになったわけです。

ただし、uniform 修飾子付きの変数についてはこれまでどおりの記述方法を用います。これだって入力じゃないか! と思う人もいるかと思いますが、uniform 修飾子付きの変数には inout といった修飾子は使えませんので、間違えないようにしましょう。

gl_FragColor と gl_FragData も廃止!

廃止されたのは、attribute と varying だけではありません。

なんと GLSL ES 3.0 では gl_FragColor も使えません。びっくりですね。

代わりに何を使うのかというと、やっぱり out を使います。要するに、出力するものなのだから varying などと同様に扱うようにしよう、ということですね。

あまり GLSL や WebGL に詳しくないと、そもそも gl_FragData なんてあったっけ? と思う方もいらっしゃるかもしれません。この gl_FragData は MRT と呼ばれる複数のバッファへの同時出力の際に使われていたもので、出力する値を配列のように複数受け取ることができました。しかし、GLSL ES 3.0 では gl_FragColor 同様に使えなくなっていますので注意しましょう。

MRT のような同時出力の場合でも、やはり同様に out を使ってやる必要があるのですが、このあたりは後述する layout が関連してきます。

layout 修飾子

GLSL ES 3.0 では、attribute などが消滅した代わりに in などを利用しますが、同時に入力時のロケーションレイアウトを GLSL 内で明示的に指定できるようになりました。

WebGL 1.0 の頃から存在する API のメソッドに gl.getAttribLocation などがありますが、ここに出てくるロケーションの番号を、GLSL で任意に指定できるようになったのですね。

これは次回以降、別の機会にしっかりと使い方を解説する予定ですが、簡単に記述方法の例だけ載せておきます。GLSL に次のような layout 修飾子を用いた記述を行うことで、ロケーションを GLSL 側から指定できます。

layout を使ったロケーションの指定例

#version 300 es
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec4 color;

なお、この layout ですが、利用しなくても GLSL ES 3.0 対応のシェーダとして通常どおりコンパイルを通すことはできます。これはまたいずれ、しっかりと違いなどについて言及するようにしますので、ここではそういうものがあるのだなということだけ理解しておきましょう。

texture 系の変化

さて、まだまだ変更点はあります。

WebGL 2.0 について言及した以前のテキストでも書きましたが、今回のバージョンアップではテクスチャ周辺が大きく変化しています。これは GLSL ES 3.0 についても同様で、かなりの数の変更点があります。

それらの全てを漏れ無く理解しておく必要はひとまずありませんが、テクスチャのフォーマットはもちろん、ビルトイン関数についてもかなり種類が増えています。そんななかでも特に注意すべきなのは、これまで当たり前のように使っていた texture2D 関数が 廃止 されていることでしょう。

従来は、2D 系とキューブマップ系で、テクスチャ参照用の関数が別れていました。GLSL ES 3.0 では、これがひとつに統合されています。つまり、2D テクスチャであってもキューブテクスチャであっても、共通の関数で参照を行うことが可能になっているのですね。

より具体的には、テクスチャの参照にはそのものずばりの名前を持つ texture 関数を使います。この関数は与えられた sampler の種類に応じて、自動的に参照方式を変更してくれる関数となっています。

ちょっと注意しなければならないのは、当サイトのサンプルなどでもそうなのですが、uniform 変数の名前などに texture というキーワードを使ってしまっている場合でしょうか。この場合、名前が競合することになるので注意が必要です。

まとめ

さて、かなりざっくりとした概要だけの説明でしたが、それでも結構盛りだくさんな内容でしたね。

WebGL 2.0 の機能を完全に使いこなすためには、GLSL についても、やはり ES 3.0 のほうを使っていく必要があります。WebGL 2.0 が利用できるのであれば、同時に GLSL ES 3.0 についても利用できることが確約されるので、積極的に新しい記法に慣れていくことを個人的にはおすすめします。

従来どおりの GLSL ES 1.0 相当のシェーダもそのまま使えるので、無理にこれまでに実装したものを移植する必要はないでしょう。その点は安心して大丈夫です。ただ、これから新しく作っていくものについては、可能な限り、WebGL 2.0 を利用する場合には GLSL ES 3.0 を使うべきでしょう。

今回は動作するサンプルとして、従来の GLSL ES 1.0 を使って WebGL 2.0 で描画するものと、GLSL ES 3.0 を使って描画するものの二種類を用意しました。

実行結果については全く同じになるはずですが、コードを見てみると、シェーダの記述方法が違っていることがわかるはずです。ぜひ実際に実行して、両者が正しく動いていることを確かめてみてください。

entry

PR

press Z key