平行ライトにCookieマスクを追加します

今回の主な話題:平行光にCookieマスクを追加します、ToLua tableの使用に時間がかかる問題の最適化、鏡面反射に関するいくつかの最適化、UnityでBVHを使用して動的Occlusion Culling プラグインを作成する、PlayableBehaviourの特定のクラス内の変数に関する問題。


Rendering

Q1: 私たち自分で書いたShader、普通な平行光は光と影の処理を受けられますが、アーティストがライトに1つのCookie マスクを追加したいため、結果モデルが暗くなってしまいました。

 

ここで _LightColor0.rgb を取ります。Cookieがあれば、ここではすべて000で真っ黒です。Cookieがない場合、取れるのはライトの色です。

これはなぜですが、もう1つのForwardAdd Passを追加しなければ処理できませんか?

アーティストが雰囲気(例えば森の中の光と闇の感覚)をレンダリング できる追加の光と影の変化が欲しいですが、DrawCallをdoubleにしにたくません。何か良い実現方法ありませんか?

次の図には、Cookieを追加していませんから平らで鈍いです。

現在の考えでは、霧を簡単化する方法しか参照できません。このところの地面はまだ平坦なので、この方法を使用できます。Meshパッチを地面と同じ高さに置き、Shaderのレンダリングレベルをこのように設置します。

Tags{ “Queue” = “Transparent+150” “IgnoreProjector” = “True” “RenderType” = “Transparent”}

これで考えると、少し変更したら、シンプルな曇りのエフェクトを行うこともできます。(混合方法はまだ変更する必要があるかもしれないです、アーティストが建物には多少の色の歪みがあると言われています。)

Shader "Studio1/Flow2"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _MashAlpha("シャドウの透明度",Range(0.00, 1.00)) = 0.60
        [KeywordEnum(OFF, ON)] _MoveUV("uv移動", Float) = 0
        _MoveSpeed("uv移動速度",Float) = 1
    }
    SubShader
    {
        Tags{ "Queue" = "Transparent+150" "IgnoreProjector" = "True" "RenderType" = "Transparent"   }
        LOD 100
        Blend SrcAlpha OneMinusSrcAlpha
        ZWrite Off
        ZTest Off
        Cull Back
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog
            #pragma multi_compile _MOVEUV_OFF _MOVEUV_ON
            #include "UnityCG.cginc"
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };
            sampler2D _MainTex;
            float4 _MainTex_ST;
            float _MashAlpha;
#ifdef _MOVEUV_ON
            float _MoveSpeed;
#endif
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }
            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed2 tep=i.uv;
                #ifdef _MOVEUV_ON
                //uvを時間によって変化させ
                tep.x +=_Time.y * _MoveSpeed;
                tep.y +=_Time.y * _MoveSpeed;
                #endif
                fixed4 col = tex2D(_MainTex,tep);
                col = fixed4(col.rgb, col.a*_MashAlpha);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}


Lua

Q2: 一般的に、Lua のtableをキャッシュして使用することがより良いと感じます。しかし、自分がプリントしてみたところ、Vector3のような小容量のtableに対して、キャッシュで値を変更するよりも新規のほうがはるかにコストが安いと感じました。これら2つの使い方の違いはパフォーマンスに大きな影響を与えますか?普段に気つける必要がありますか?

画像を添付します。(左側がキャッシュの消費時間でありますが、右側がNewの消費時間です。8倍の差があります。)

 

次のコードようにすれば良いです。

local VSet = Vector.Set
for i = 1, 100000000 do
VSet(c, a.x+b.x, a.y+b.y, a.z+b.z)
End

あなたのケースでは、Metatable を介した関数の呼び出しと関数の直接呼び出しを比較して、どちらが速いかを確認しました。主流のLuaはオブジェクトに向かって実現するため、obj:XXX()の方式を使用して関数を呼び出し、すべてMetatableを介して完成させます。このやり方は継承のサポートに非常に適していますが、Metatableの探しをもう 1 回行いますから、パフォーマンス 上は確かに悪くなります。Vector.Setをキャッシュすれば、これらの時間を節約できます。

local VNew = Vector.New

これも直接 Vector.New よりも高速で、フィールドの探し時間を一回節約しました。(VectorでNewフィールドを検索します。)

このような最適化は、関数呼び出しのある場所どちらでも使用できます。コストの高い場所でやったら大丈夫です、コード全体でこのような最適化を行う必要はありません。

 

これには、テスト方法の問題に関します。

1、テストの前と後には必ずGCする必要があります。これは、前回に蓄積されたメモリが後段階のテストで影響を受けないためです。

2、テストの規模は、ゲームメモリの規模と一致していなければなりません。大きすぎたり小さすぎたりすると、影響程度の反映に悪いです。

3、外部呼び出しを減らすために、外部実現をループに直接書き込むことができます。外部インターフェイスを使用する必要がある場合は、このタイプのインターフェイスのパフォーマンスを適切に評価する必要もあります。

最後に、最近私がやったこの方面のパフォーマンステストの結論についてお話しましょう。Vector の再利用は、New よりも高速で、メモリの割り当てとGCが導く時間コストも生成しません。


Rendering

Q3: 現在、ゲームにあるシーンの地面にリアルタイムの鏡面反射が使用されて、反射のコストが少し大きいで、面数のピーク値は200,000 を超えています。

FrameDebugger で確認したところ、一部の不要な部分も反映されていることがわかりました。これにより、DrawCall と面数が大幅に増加しました。たとえば、次のようになります。

1、キャラクターの輪郭

1人のキャラに6,000の面が持つことを仮定すれば、輪郭描画は6,000、反射キャラは6,000、反射輪郭描画も6,000で、すべて240,000の面が必要です。反射輪郭描画の部分はまったく見えず、完全に取り除くことができます。

2、モデル LOD、反射テクスチャ自体はあまり明確ではなく、低レベルのモデルLODも使用することもできます。

これら 2 つの問題に対応して、私は解決策を考えました。

1、反射テクスチャをレンダリングするOnWillRenderObject方法の中に、Shader.globalMaximumLODを10に強制的に変更させ、LOD100のShaderは輪郭が描画されていないし、計算も少ないです。レンダリングが完了する後から元に戻します。

2、反射テクスチャをレンダリングするOnWillRenderObject方法の中に、QualitySettings.lodBiasを0に強制的に変更させ、カメラに低レベルのモデルLODでレンダリング して、完了したから元に戻します。

現在、私の質問は、リアルタイム鏡面反射テクスチャはフレームごとにレンダリングする必要があります。つまり、フレームごとにShader.globalMaximumLODとQualitySettings.lodBiasの値を変更します。この操作は何か問題を引き起こしますか?

自分がパッケージして試しました。Profilerの中からShader.globalMaximumLODとQualitySettings.lodBiasを設置する用時間は、節約されたレンダリング用時間より多いことが分かりました。

では、反射オブジェクトのレベルを制御すること以外に(これはやるつもりですが、現在のプロジェクトの状況でいくつの難点を解決する必要があり)、他の最適化方法はありますか?

これが問題主に役立つかどうかはわかりません。Material.SetShaderPassEnabledは、反射カメラがレンダリング している時に輪郭を描画するPassをオフにします。パフォーマンスはわかりませんが、アイデアだけを提供します。

 

②  

後でReplacementShaderを使うという新しい方法を考えました。diffuseが1つのみあるのShader Replaceを使ったら、1は輪郭を描きませんで、計算量を減らすこともできます。2は不透明オブジェクトのみをレンダリングするため、特殊効果と3D UIのDrawCallを節約できます。


Culling

Q4: 私のプロジェクト内のオブジェクトはすべて動的ローディングするため、Unity自体のオクルージョンカリング機能は使用できません。動的オクルージョンによって提案されたInstantOCなどの一部のプラグインも使用されていますが、これらのプラグインはすべてのオブジェクトの光線検出に基づいていますから、プロジェクトのCPU計算には長い時間がかかります。

ですから、BVHを使用してUnityで動的オクルージョンカリング提出プラグインを作成したいです。何か良いアイデアはありますか? ありがとうございました。

自分でBVHを使ったら、もうオクルージョン関係でしょう?視線に基づいてより正確な計算を行う必要があり、コストも高いはずです。

問題主のシーンオブジェクト動的ローディングはただの動的ローディングですが、動的ローディングが完了した後に動けなくなります。あるいはシーンの主要オブジェクトがすべて動的であるのか、シーンがランダムに生成されるのかはわかりません。前者の場合、オクルージョン関係は事前にオフラインで計算することもできます。後者の場合、確かに動的にのみ計算できます。

この部分のコストは高いで、直接に暴力的な距離除​​去やLODほど良くないかもしれません。スクリーンショットを見ると、不透明なものが多いため、オクルージョンカリングは、主にDrawCallを減らし、CPU時間を節約するためのものです。除​​去がかかる時間と比較して決定ください。


Timeline

Q5: Timeline再生は変数の変更を記録できます。例では、PlayableBehaviour類にある、浮動点やベクトルなどの変数の変化を記録できます。現在は、カスタムPlayableBehaviourでカスタムconfig類の実例を維持することを実現したいです。しかし、それを使用すると、記録されたconfig実例にある変数が変更された場合、再生するとconfig実例は空き引用であります。正しい操作方法はなんですか?configの初期化タイミングが間違っているためですか?

Unityフォーラムでも質問を投稿しました。カスタムBehaviourの中には内部クラスを使用して変更を記録することはできません。構造を使用する必要があります。


UWA公式サイト:https://jp.uwa4d.com

UWA公式ブログ:https://blog.jp.uwa4d.com

UWA公式Q&Aコミュニティ(中国語注意)https://answer.uwa4d.com