-100p

-10p

+10p

+100p

シェーダー基礎、if文の代わりとなるShader関数

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

-100p

-10p

+10p

+100p