GLSL の flat 補間モード
今回のサンプルの実行結果
気軽に使える flat 修飾子
前回は GLSL ES 3.0 で追加された layout
修飾子について解説しました。
大きなメリットが無いというちょっと残念な新機能ですが、資料を追っているときなどに突然出てきても大丈夫なように、頭の片隅に入れておくようにしましょう。
さて今回のテーマですが、やはり GLSL ES 3.0 から追加された機能である flat 修飾子 を用いたフラット補間モードについてです。
WebGL 1.0 では、いわゆるフラットシェーディングを行うためには拡張機能を有効化し、その上でシェーダ内でベクトルの勾配を計算するというちょっと面倒なことをやってやる必要がありました。これは以前に、当サイトでも解説したことがあります。
このようなフラットなシェーディングは、ちょっとレトロな雰囲気の、独特な質感を表現できるという特徴があります。頂点データの生成から工夫して行ってやれば、無理にシェーダを使わなくてもこれを実現することは可能ですが、いずれにしても若干手間が掛かることには変わりないですね。
GLSL ES 3.0 から加わった flat
修飾子を使えば、比較的簡単にフラットシェーディングの実装を行うことができます。拡張機能も必要ありませんし、フラットシェーディング用の特殊な頂点データを用意する必要もありません。非常に簡単です。
これがどういった仕組みで行われているのか、次項から詳しく見ていきましょう。
シェーダ間での補間
ある程度 3D プログラミングに詳しい方であれば、WebGL などのグラフィックス API がどのようなプロセスで描画を行っているのかご存知かと思います。一般に、GPU 内部でグラフィックスが生成されていく過程のことをグラフィックスパイプラインとか、あるいはレンダリングパイプラインなどと呼びますが、今回の flat
修飾子を使った補間について理解するには、これらのパイプラインの流れが理解できているとスムーズにイメージしやすいと思います。
WebGL の場合、グラフィックスパイプラインにおいてシェーダが関連する箇所がふたつあります。
ひとつ目は、頂点シェーダが動作するステージで、ジオメトリステージなどと呼ばれることがあります。ステージというと大げさですが、要は頂点シェーダが頂点情報を元に 3D データを処理するタイミングです。
頂点シェーダでは、頂点の座標変換を行ったり、次のステージへとデータを出力するためのビルトイン変数などへのデータの代入が行われます。ここで言うビルトイン変数とは gl_Position
などのことですね。
その他にも、WebGL 1.0 で言うところの varying 変数を用いることで、これを次のステージへの橋渡し役として利用できます。GLSL ES 3.0 からは varying 修飾子が使えないので、頂点シェーダから out
修飾子で出力し、フラグメントシェーダで in
修飾子を使ってデータを受け取ります。
さて、それではここで考えてみましょう。
たとえば、以下の図に示すように、頂点が3つでポリゴンを形成している場面を考えます。このとき、矢印で示しているピクセル上でフラグメントシェーダが動作するとき、頂点が持っている情報はどのように渡ってくるのでしょうか。
頂点が存在しないフラグメント上での処理
頂点によって三角形などのプリミティブが生成されるとき、実際には頂点が存在しない場所にも面が張られます。面があるということは、そのピクセルに面を描画する必要があるわけですから、当然フラグメントシェーダが実行されますね。
頂点が3つしかないにもかかわらず、フラグメントシェーダは大抵の場合それよりも多い回数実行されることになるわけです。このとき、各フラグメントには、関連するそれぞれの頂点の情報が 線形補間 された状態で渡されます。
その証拠に、ポリゴンを形成する頂点に色を持たせて描画すれば、描かれるポリゴン上にはキレイに混ざりあった、補間された色が出力されます。これは色であれば見た目にもわかりやすいですが、なにも色だけが線形補間されるというわけではありません。それが法線であろうと、あるいはそれ以外のなにかしらのデータであろうと、要は頂点シェーダからフラグメントシェーダへのデータの移り変わりのタイミングで、あらゆるデータが補間処理の対象になるわけですね。
この補間処理に干渉することができるのが、今回のテーマである flat
修飾子です。
あらゆるフラグメントに対して行われる処理である線形補間に、シェーダ側で任意の指定が行えるようになったわけですね。
ちなみに、一切の指定をせずにフラグメントシェーダへ出力した値は、デフォルトの動作として smooth
モードになります。意図的に smooth
修飾子をシェーダ内に記述することもできますが、これは既定の動作であるために、単に省略されているだけだったんですね。
WebGL 1.0 及び GLSL ES 1.0 の場合は、この補間処理に干渉するための修飾子の指定が行なえません。強制的に smooth
モードになってしまうわけです。GLSL ES 3.0 なら、ここに flat
修飾子を利用することで、線形補間を行わないように指定してやることができるのです。
flat 修飾子付きの変数宣言
さて具体的にどのように flat
修飾子を使えばいいのか、実際の記述方法を見ていきましょう。
修飾子、というふうにあることから想像ができるかと思いますが、変数の宣言を行う際に flat
修飾子を用いると、その変数へ代入された値がどのように扱われるのかが変化します。
つまり、今回のような flat
修飾子を用いるケースでは、シェーダ全体に対して指定を行うというわけではなく、それぞれの変数ごとに個別に設定を行います。先述のとおり、記述を省略した場合には smooth
修飾子付きの変数として扱われることになりますので、これまでと同様に線形補間の対象となります。
flat 修飾子付きの変数宣言
// 通常の varying 変数宣言
out vec3 vPosition; // ← 頂点シェーダ
in vec3 vPosition; // ← フラグメントシェーダシェーダ
// flat 修飾子付きで varying 変数を宣言
flat out vec3 vPosition; // ← 頂点シェーダ
flat in vec3 vPosition; // ← フラグメントシェーダ
たったこれだけの記述で、これまで行われてきた線形補間の処理を行わないように指定することができます。頂点シェーダ側では flat out
で出力し、フラグメントシェーダ側では flat in
で受け取るわけです。
ひとつイメージするときに注意しないといけない点としては、たとえ線形補間を行わないとは言っても、頂点シェーダから出力した情報が完全にそのままの状態で出力されていくわけではないということを理解しておきましょう。
たとえば頂点が3つのポリゴンで、それぞれの頂点が違う色をしていたとします。これを描画したときに、頂点の色が補間されないのならば、ポリゴンの中が三色に塗り分けされるのかと言えばそういうことではありません。あくまでも フラグメントごとの補間がされない だけであって、ひとつのポリゴンの中身が複数の状態に別れてしまうわけではないのですね。
まとめ
さて、GLSL ES 3.0 から利用することができるようになった、補間に関する修飾子について見てきましたが、思いのほか簡単に使うことができるのでちょっと拍子抜けしたという感想を持った方もいたかもしれません。
フラット補間モードを使う場面としては、やはり一番多いのはフラットシェーディングの質感表現かと思いますが、シェーダを高速な演算機器であると考えれば、勝手に補間されてしまうと都合の悪いケースというのも考えられます。自分が実現したい処理に合わせて、適切な補間モードを選択しつつ、便利に活用していきましょう。
今回も実際にフラットモードを使ったサンプルを用意しています。
以下のサンプルでは、頂点シェーダでライティングの陰影計算を行い、その結果の色情報をフラットモードでフラグメントシェーダへ渡しています。さらに、頂点のテクスチャ座標もフラットモードを使っているため、テクスチャの参照結果も補間無しモードになっています。どのような結果になるのか、確かめてみてください。