【Unity】ソフトシャドウってハードシャドウよりどれだけ重いのか?シェーダを見て考える

影は処理負荷が大きいので、設定にはシビアになる必要があります。
この記事ではソフトシャドウとハードシャドウの処理負荷の違いを追ってみます。

Unity2018.4.0

ソフトシャドウ?ハードシャドウ?

現実の影に近い、エッジがグラデーションを描くような影をソフトシャドウといいます。

f:id:halya_11:20190616231014p:plain

これに対してエッジがはっきりした影をハードシャドウといいます。

f:id:halya_11:20190616230950p:plain

ソフトシャドウの方がハードシャドウよりも処理負荷が大きくなります。
では実際の処理にどのような違いがあるのでしょうか?

シェーダを見る

処理を追ったところ、シャドウマップをサンプリングする処理に違いがあることがわかりました。
この処理はUnityShadowLibrary.cgincにUnitySampleShadowmap()という関数で定義されています。

inline fixed UnitySampleShadowmap (float4 shadowCoord)
{
    #if defined (SHADOWS_SOFT)

        half shadow = 1;

        // No hardware comparison sampler (ie some mobile + xbox360) : simple 4 tap PCF
        #if !defined (SHADOWS_NATIVE)
            float3 coord = shadowCoord.xyz / shadowCoord.w;
            float4 shadowVals;
            shadowVals.x = SAMPLE_DEPTH_TEXTURE(_ShadowMapTexture, coord + _ShadowOffsets[0].xy);
            shadowVals.y = SAMPLE_DEPTH_TEXTURE(_ShadowMapTexture, coord + _ShadowOffsets[1].xy);
            shadowVals.z = SAMPLE_DEPTH_TEXTURE(_ShadowMapTexture, coord + _ShadowOffsets[2].xy);
            shadowVals.w = SAMPLE_DEPTH_TEXTURE(_ShadowMapTexture, coord + _ShadowOffsets[3].xy);
            half4 shadows = (shadowVals < coord.zzzz) ? _LightShadowData.rrrr : 1.0f;
            shadow = dot(shadows, 0.25f);
        #else
            // Mobile with comparison sampler : 4-tap linear comparison filter
            #if defined(SHADER_API_MOBILE)
                float3 coord = shadowCoord.xyz / shadowCoord.w;
                half4 shadows;
                shadows.x = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, coord + _ShadowOffsets[0]);
                shadows.y = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, coord + _ShadowOffsets[1]);
                shadows.z = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, coord + _ShadowOffsets[2]);
                shadows.w = UNITY_SAMPLE_SHADOW(_ShadowMapTexture, coord + _ShadowOffsets[3]);
                shadow = dot(shadows, 0.25f);
            // Everything else
            #else
                float3 coord = shadowCoord.xyz / shadowCoord.w;
                float3 receiverPlaneDepthBias = UnityGetReceiverPlaneDepthBias(coord, 1.0f);
                shadow = UnitySampleShadowmap_PCF3x3(float4(coord, 1), receiverPlaneDepthBias);
            #endif
        shadow = lerp(_LightShadowData.r, 1.0f, shadow);
        #endif
    #else
        // 1-tap shadows
        #if defined (SHADOWS_NATIVE)
            half shadow = UNITY_SAMPLE_SHADOW_PROJ(_ShadowMapTexture, shadowCoord);
            shadow = lerp(_LightShadowData.r, 1.0f, shadow);
        #else
            half shadow = SAMPLE_DEPTH_TEXTURE_PROJ(_ShadowMapTexture, UNITY_PROJ_COORD(shadowCoord)) < (shadowCoord.z / shadowCoord.w) ? _LightShadowData.r : 1.0;
        #endif

    #endif

    return shadow;
}

上記を見ると、ソフトシャドウの処理の方はシャドウマップを4回サンプリングしていることがわかります。
これに対してハードシャドウの処理は1回だけサンプリングしています。
要するに、ソフトシャドウは周囲の影の色をサンプリングして合成することで、エッジがぼけたような効果を得ているということになります。

シャドウマップのサンプリング回数が4倍になるので処理負荷にはそれなりに影響しそうです。

関連

light11.hatenadiary.com