見出し画像

GLSL : don't use "if"

Shader記述時の注意点

* for文のループ回数を動的に出来ない。
* if文はあまり使わない方がいいらしい (処理負荷が高い)
* 基本bit演算は使えない。将来のために予約はされている。拡張を使えば使える。
* 配列も基本的には使わない方がいい

ifの分岐を避ける

> 3項演算子

vec3 BLACK = vec3(0.0);
vec3 WHITE = vec3(1.0);
vec3 color;

// こんなif文は、
if (x < 0.5) {
     color = WHITE;
} else {
     color = BLACK;
}
// こんな三項演算子に置き換える
color = (x < 0.5) ? WHITE : BLACK;

こんな感じでif/else構文を避けていくわけですが、この方法の欠点はベクトルの要素を使って一度に条件分岐することができない事です。
step関数とmix関数を使うとベクトルの要素ごとに条件分岐をしてその結果をベクトルに直接反映させる事ができます。

> stepの用法

// 0.0 if x < edge, else 1.0
T step(T edge, T x)
T step(float edge, T x)

cf:WebGL 1.0 API Quick Reference Card

例えば、

vec2 x = vec2(0.25, 0.75);
vec2 edge = vec2(0.5);
// .xは 0.25 < 0.5 なので、0.0
// .yは 0.75 >= 0.5 なので、1.0
vec2 y = step(edge, x); // vec2(0.0, 1.0)

条件分岐の結果が.xと.yで各々 異なっているのが分かります。

さらに、mix関数を組み合わせる事でベクトルでもif/else構文や三項演算子の代わりを果たす事ができます。

vec3 BLACK = vec3(0.0);

vec3 WHITE = vec3(1.0);
vec3 color;
vec3 x = vec3(0.4, 0.5, 0.6);
vec3 edge = vec2(0.5)
// .xは 0.4 < 0.5 なので、0.0
// .yは 0.5 >= 0.5 なので、1.0
// .zは 0.6 >= 0.5 なので、1.0
// 結果、vec3(1.0, 0.0, 0.0)で赤色になる
color = mix(WHITE, BLACK, step(edge, x));

> stepを使った分岐を分かり易く: 条件を満たす時に1.0

step(edge, x); // edge <= x なら 1.0
step(x, edge); // x <= edge なら 1.0
vec4(1.0) - step(edge, x); // x < edge なら 1.0 // vec4の場合
vec4(1.0) - step(x, edge); // edge < x なら 1.0 // vec4の場合

> stepの注意点

T step(float edge, T x)
はできても、
T step(T x, float edge)
はできないので注意です。

vec4 v4 = vec4(1.0, 0.75, 0.5, 0.25);
vec4 result; // 1つ目がベクトルなら2つ目もベクトルにしないとダメ
// result = step(v4, 0.5);
// ベクトルの次元を合わせればOK
result = step(v4, vec4(0.5));

> "=="と"!="

abs(sign(x - edge)); // x != edge なら 1.0
vec4(1.0) - abs(sign(x - edge)); // x == edge なら 1.0 // vec4の場合

参考URL

条件分岐のためにstep関数を使う時の考え方をまとめてみた
プログラマブルシェーダとGPUについて
WebGL 1.0 API Quick Reference Card
2017年度GLSLお役立ちシート
GLSLについてのメモ

もしよろしければ、サポートをお願いします! 頂いたサポートは、Creatorとしての活動費に充てさせて頂きます。