渋谷ほととぎす通信

エンジニア社長によるUnityとAIのブログ & エンジニアの生存戦略

【Unity】シェーダーが2.5倍早くなる方法

【Unity】シェーダーが2.5倍早くなる方法

こんにちは、エンジニアのオオバです。

お悩みさん
お悩みさん
  • PropertyToIDって何?
  • パフォーマンスがよくなる?
  • オオバ
    オオバ
    本記事ではこれらの悩みを解決します。

    Unityでオブジェクトを思ったとおりの見た目にするためにシェーダーを皆さん書いていると思います。

    複雑なシェーダーになればなるほどGPU負荷が上がっていきスパイク(カクつき)の原因になります。

    そこで本記事ではシェーダーのプロパティアクセスを2.5倍早くなるテクニックを紹介していきます。

    シェーダーをガリガリ書いている方はぜひ読んでみてください。

    👉DOTweenの教科書を読んでUnityアニメーションをプログラミングしてみよう!

    シェーダーのプロパティにアクセスとは?

    シェーダーはマテリアルを通してシェーダー内のパラメーターを更新することが可能です。

    • 数値
    • テクスチャ

    など。

    さまざまなシェーダー内のパラメータをC#を使って変更できます。

    // シェーダー内の_Propパラメーターに100を代入  
    material.SetFloat("_Prop", 100f);  
    

    たとえば上記のソースコードはマテリアルにセットされたシェーダー内の「_Prop」パラメーターに100を代入したということになります。

    文法の内訳は以下です。

    第1引数第2引数
    シェーダー内の変数名代入する値

    このようにシェーダーのパラメーターを変更して見た目を変更することはよく発生します。

    関数更新する値の種類
    SetInteger整数
    SetFloat浮動小数点
    SetTextureテクスチャ
    SetColor
    SetVectorVector4
    SetMatrix4x4行列
    SetFloatArrayfloat型配列

    「SetFloat」以外にも値をシェーダーのパラメータを変更する関数が実装されています。上記の表以外にもまだまだあります。

    ↓詳しくは公式リファレンスを確認してみてください。

    シェーダーパラメーターの文字列アクセスは重い

    ここからが本題ですが、前章で紹介したシェーダーのパラメーターアクセス方法は処理としては重いです。言い換えるともっと効率的なアクセス方法があります。

    それは 「PropertyToID」 です。

    PropertyToIDとはシェーダー内の変数名をIDに変換してIDでパラメーターにアクセスする手法です。

    具体的には次のコードでパラメーターを文字列からIDに変換します。

    int id = Shader.PropertyToID("_Prop");  
    

    Shaderのクラスメソッド PropertyToID を使うとint型の値が返ってきます。

    この値を使ってシェーダーパラメーターにアクセスするのです。IDを使ったシェーダーアクセス方法はこちら。

    material.SetFloat(id, 100f);  
    

    文字列の時と文法に変更はありません。 文字列が整数に置き換わっただけ です。

    文字列からIDに変更するだけでパフォーマンスが向上します。

    PropertyToIDはどのくらいパフォーマンスアップするのか

    では「PropertyToID」が実際どのくらいパフォーマンスを上げるのか確認していきましょう。

    確認方法は次の検証コードとUnity Profilerを使用して計測します。

    toggleを切り替えて文字列参照と整数参照を切り替えてプロファイルします。

    【Unity】シェーダーが2.5倍早くなる方法_0

    上の動画はUnity Profilerの画面です。toggleを切り替えると、明らかなパフォーマンスの違いが出ました。

    【Unity】シェーダーが2.5倍早くなる方法_1

    文字列指定ID指定
    4.47ms1.61ms

    ID指定の方が約2.5倍パフォーマンスが良いという結果になりました。

    プロファイラーでPropertyToID処理の中身を確認

    なぜPropertyToIDの方が処理速度が早くなるのか確認してみましょう。

    Unity Profilerで確認すると両者には処理内容に大きな違いが生まれていることが分かります。

    文字列でプロパティ指定

    【Unity】シェーダーが2.5倍早くなる方法_2

    IDでプロパティ指定

    【Unity】シェーダーが2.5倍早くなる方法_3

    文字列指定では内部的に 「PropertyToID」 を実行し、更にSetFloatが実行されています。その他ID指定と比べると処理項目が多いことが分かります。

    ※※Deepプロファイラで実行しないと関数名まで表示されないので注意です

    PropertyToIDで事前にキャッシュがオススメ

    PropertyToIDのパフォーマンスが高いことが分かりました。では実際どのようにPropertyToIDを使用したらよいか解説していきます。

    結論、事前キャッシュです。ゲーム起動時にキャッシュして使い回す方法がおすすめの方法です。なぜなら パラメーターのIDはゲーム実行中変更されないため です。

    public class Test : MonoBehaviour  
    {
        // シェーダーIDをキャッシュ  
        public static readonly int ColorId = Shader.PropertyToID("_Color");  
    }
    

    上記のコードのようにゲーム起動時にIDを取得しておくとゲーム実行中に無駄な処理が走らずに済むためオススメです。

    まとめ

    本記事ではPropertyToIDを使ったシェーダーのパフォーマンスアップについて解説してきました。

    シェーダーは複雑になればなるほど負荷が上がってきます。今回紹介したテクニックは小さな改善にはなりますが、パフォーマンスチューニングは小さい改善の積み重ねです。

    PropertyToIDは知ってて損のない手法ですし、デメリットがないためぜひ積極的に使用してもらえればと思います。

    PropetyToIDはVisual Effect GraphのイベントID取得にも使用されています。

    オススメ記事
    参考サイト