シェーダーで条件分岐は遅いらしいのでベクトル演算に置き換える(追記あり)

一般的にシェーダーでの条件分岐は遅いという話がある

今回作ったシェーダーではブレンドモードや使用するマスクのチャンネルをアプリから送信してシェーダー内で利用するということをした

これを条件分岐を使って素直に実装するとこんな感じになる(空で書いたので動くか不明)

fixed3 blendColor(fixed3 src, fixed4 dest, float blend, int mode) {
    float alpha = dest.a * blend;
    fixed3 blended_dest = dest.rgb * alpha;
    if (mode == 0) {
        return lerp(src, dest, alpha);
    } else if (mode == 1) {
        return src + blended_dest;
    } else if (mode == 2) {
        return src - blended_dest;
    } else if (mode == 3) {
        return src * blended_dest;
    }
    return src;
}

fixed useMask(fixed4 mask, int use_mask_number) {
    if (use_mask_number == 0) {
        return 1.0 - mask.r;
    } else if (use_mask_number == 1) {
        return 1.0 - mask.g;
    } else if (use_mask_number == 2) {
        return 1.0 - mask.b;
    } else if (use_mask_number == 3) {
        return 1.0 - mask.a;
    }
    return 0.0;
}

これの modeuse_mask_number を使用したいチャンネルが1.0のベクトルで渡すことで、if文を行列とベクトル演算に置き換えることができる

fixed3 blendColor(fixed3 src, fixed4 dest, float blend, float4 mode) {
    float alpha = dest.a * blend;
    fixed3 blended_dest = dest.rgb * alpha;
    // GLESでは非正方行列が使用できないので使わないところは0で埋める
    return mul(mode, fixed4x4(lerp(src, dest, alpha), 0,
                                src + blended_dest, 0,
                                src - blended_dest, 0,
                                src * blended_dest, 0)).rgb;
}

fixed useMask(fixed4 mask, float4 use_mask_vector) {
    return 1.0 - dot(mask, use_mask_vector);
}

本当に早くなっているかわからないけど、条件分岐は遅いという話を信じてベクトル演算の方を使用している

※5/24追記 シェーダーをアタッチしたオブジェクトを処理落ちするだけ複製してから、ifバージョンと上記の行列バージョンで比較してみたところ

ifバージョン: 16fps

matrixバージョン: 12fps

でifバージョンのほうが高速でした、遅いとは言ってもすべての計算をしてから行列演算をするよりかは速いようです...

書き方の違いはあれどglslでもhlslでも利用できるが、制限として4次元までしかこの方法は使用できない(float5やvec5なんてないよね?ないはず)