フラグメントシェーダー芸

 フラグメントシェーダーの小技を集めてみました。

円を描く

discardの効果

枠を描く

 中を抜けば枠になる。

角を落とす

 テクスチャの角を落とすこともできる。

太さのある線、みたび

 フラグメントシェーダーで円が描けるのだから、いわゆるラウンドキャップの直線(角が丸いやつ)が描けるのではないか?

裏表のある話

 ポリゴンの表と裏で表示を変えることができる。

gl.pers
  • VanillaMat4.pers(クラスメソッド版)を呼び出す
  • whthis.canvasから自動で埋めてくれる
表の決まり方
  • gl.TRIANGLESの場合、の絵のように三角形の頂点を結び、それを視点から見た場合に反時計回りに見えるなら、ポリゴンの表が見えている
  • ということは、時計回りなら裏
  • gl.TRIANGLE_STRIPの場合、最初の三角形はgl.TRIANGLESと同じになるが、ふたつ目の三角形は逆になる
  • ひとつ目の三角形の最後の辺がふたつ目の三角形の最初の辺になるが、通常、残りの頂点はこの辺を挟んで反対側になる
  • 3D空間で棒状にするの絵のように、この辺をたどる方向は同じだから、残りの頂点へ行き着く方向はお互いに逆回りになる
  • このため、gl.TRIANGLE_STRIPの場合、最初の三角形は反時計回りに見える場合が表、次の三角形は時計回りに見える場合が表、その次の三角形は反時計回りに見える場合が表、のように、表に見える回転方向が交互に入れ替わる
  • 今までの説明では出てきていないが、縮退三角形を使う場合に気をつける必要がある
表(おもて)かどうか調べる
  • フラグメントシェーダーの組み込み変数gl_FrontFacingで分かる
  • trueならば表が見えている、falseならば裏が見えている
  • ここでは表ならテクスチャを表示、裏なら固定色で塗りつぶし
  • マウス(タッチ)で回せるようにしてあるので、めくってみるとよい
  • 普通はテクスチャが裏返しで見える
  • 裏にテクスチャを貼ることもできる
ifの代わりにmix
  • 一般的にシェーダープログラムではifは避けるべきと言われている
  • gl_FrontFacingfloatにキャストすると0か1になるため、mix関数の第3引数に渡せば、表が見えている時に第2引数の値が、裏が見えている時に第1引数の値が得られる
  • なぜifを避けるか?
    • シェーダーは複数の頂点やフラグメント(ピクセル)を同時に(並列で)計算している(1024個とか)
    • 同時に計算できる数だけシェーダーユニットがあり、基本的にレジスタは独立しているがプログラムとプログラムカウンタは全ユニットで共通
    • あるユニットでは条件がtrueだが、隣のユニットではfalseといった場合を考えると、実際にはジャンプできないことが分かる
    • ではどうするかというと、then節の後にelse節を並べて書き、ジャンプせずに全部の命令を条件付き実行する
    • if節の結果で(ユニットごとに)どちらの節を実際に実行するかが決まり、もう一方は空読み(いわゆるNOP)になる
    • if文をネストした場合、外側のif節のフラグを保存しておく必要があり、このフラグは他の制御構造と共用のため、制御構造のネスト深さに制限がある(シェーダー黎明期はアセンブラの仕様も公開されていた、これはGLではなくてMSのHLSLだけれど)
    • このため、then節とelse節にちょっとだけ異なる巨大なブロックを書いてしまうと、ふたつのブロックのうち片方はNOP扱いで全部実行するハメになる
    • なるべく処理を共通にすればthen節とelse節が小さくなるので、総実行ステップが減る
    • ステップ数が減った分だけパフォーマンスが上がる
    • 表は複雑な描画、裏は単純な塗りつぶしなら、シェーダー内でgl_FrontFacingで分岐して1回実行するよりも、これまた説明していないが、カリングを設定して裏と表を別々のシェーダーで描画した方が性能が出るんじゃなかろうか(やってないからじっさいのところはわかんない)
    • 昔の話だから今の常識は違うかもしれないし、コンパイラが最適化してくれることもあるかもしれない

※次回はいよいよ北はこっち!の予定


09 Sep 2024: applyaffectに変更

20 Aug 2024: 本編

15 Aug 2024: 予告編

Copyright (C) 2024 akamoz.jp