Unityシェーダーの基礎知識。Shaderでif文を使ってはいけない理由。
if文の代わりとなるstep、min、max関数。
関連ページ:
参考URL:
Shaderは1ピクセルずつ処理を実行するので、同じ関数を1フレームごとに何千何万と繰り返し通過する。
このため処理に時間がかかるif文を使ったコードが非推奨となっていて、特殊な代替え処理をよく使う。
ShaderのHLSL言語がC言語の派生と言われながらも、初見では全くそうは見えないのもここに一因がある。
step関数
C#ユーザーが見慣れないHLSLの関数は、基本的にはC#の記述で置き換えることができる。
例えばstep(a, b)という関数は、aがb以下なら1を返し、それ以外は0を返す。
float
a
=
0.2f;
float
b
=
0.4f;
//resultには1が代入される
float
result
=
step(a,
b);
このstep関数をC#に置き換えるとこうなる。
private float Step(float a, float b)
{
if (a <= b)
{
return 1;
}
else
{
return 0;
}
}
step関数はC#に置き換えることが出来るので、先にC#で実装を考えてからHLSLに置き換える実装も可能。
置き換える際にちょっと厄介な場面もある。たとえば上のコードの「<=」という条件式が「<」であった場合。
private float Step(float a, float b)
{
if (a < b)
{
return 1;
}
else
{
return 0;
}
}
step(a, b)というコードでは、aとbが同じ値だと1を返してしまう。
この場合周りくどいが、下のようなコードにすると同じ意味になる。
//aとbが同じ値なら0を、aがbより小さいなら0を、aがbより大きいなら1を返す
1
-
step(b,
a);
min関数、max関数
min関数は、2つの渡された引数のうち小さい方を返す。
max関数は、2つの渡された引数のうち大きい方を返す。両方ともシンプルな関数。
float
a
=
0.2f;
float
b
=
0.4f;
//resultMinには0.2が代入される
float
resultMin
=
min(a,
b);
//resultMaxには0.4が代入される
float
resultMax
=
max(a,
b);
step、min、maxでif文の代わりを作る
stepとmin、max関数を併用すると、C#の複雑な条件式もある程度置き換えて実装できる。
これはどんな数値であろうと0を掛けると0になってしまう常識と、どんな数値に1を掛けたり割ったりしても値が変化しない常識を利用する。
例えば下のようなC#の関数があったとする。
//divisionが0以下ならoriginalの値をそのまま返す。
//それ以外はorginalの値にdivisionを割った値を返し、
//またdivisionの最大値は1000とする。
private
float
Calc(float
original,
float
division)
{
if(division
<=
0)
{
return
original
}
division
=
division
>
1000?
1000
:
division;
return
original
/
division;
}
これをShaderのHLSL言語に訳すとこうなる。
float
calc(float
original,
float
division)
{
//divisionが0以下なら0を返し、それ以外は1を返す
float
avoidZero
=
1
-
step(division,
0);
//もしavoidZeroが0なら1を返し、1ならdivisionの値をそのまま返す
float
fixDivision
=
max(1
-
avoidZero,
division
*
avoidZero);
//fixDivisionと1000を比較し、小さい方を返す
fixDivision
=
min(fixDivision,
1000);
//originalの値にfixDivisionを割って返す
return
original
/
fixDivision;
}
0
0