フェイクスポットライト

環境

Unity2022.2.2f1

概要

フェイクスポットライトの実装テストです。

法線を考慮していません。

デカール処理がOFFの場合ならもっとシンプルになりますが、面倒なので分けていません。

円錐の頂点部分が原点、底がZ方向で円の中心部分が0.0,0.0,1.0、半径が0.5のメッシュを使用しています。

コード

Shader "Custom/SSSpotDecalLight"
{
    Properties{
        [HDR]_Color("Color", Color) = (1, 1, 1, 1)
        [KeywordEnum(Off, On)] _VolumeEnabled("Volume Enabled", Float) = 1
        [KeywordEnum(Off, On)] _DecalEnabled("Decal Enabled", Float) = 1
        _MainTex ("MainTex", 2D) = "white" {}
        _Rim ("Rim", Range(0.0, 1.0)) = 1.0
    }

    CGINCLUDE
    #pragma enable_d3d11_debug_symbols
    #pragma shader_feature _ _VOLUMEENABLED_ON
    #pragma shader_feature _ _DECALENABLED_ON
    #include "UnityCG.cginc"

    float4 _Color;
    // Volume
    sampler2D _MainTex;
    float4 _MainTex_ST;
    fixed _Rim;
    // Decal
    sampler2D _CameraDepthNormalsTexture;
    sampler2D _CameraDepthTexture;

    struct appdata
    {
        float4 vertex : POSITION;
        float3 normal : NORMAL;
        float3 texcoord : TEXCOORD0;
    };

    struct v2f
    {
        float4 positionCS :SV_POSITION;
        // Volume
        float4 color : COLOR;
        float2 texcoord : TEXCOORD0;
        // Decal
        float4 positionSS : TEXCOORD1;
        float3 positionWS : TEXCOORD2;
        float dotRadius : TEXCOORD3;
        float3 lightDirVS : TEXCOORD4;
    };
    ENDCG

    SubShader
    {
        Tags { "RenderType" = "Transparent" "Queue" = "Transparent" }
        Pass
        {
            ZWrite Off
            Cull Off
            Lighting Off
            Blend SrcAlpha One

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            v2f vert(appdata v)
            {
                v2f o;
                o.positionCS = UnityObjectToClipPos(v.vertex);

                // Decal
                o.positionSS = ComputeScreenPos(o.positionCS);
                o.positionWS = mul(unity_ObjectToWorld, v.vertex).xyz;

                float3 vec = UNITY_MATRIX_M._m02_m12_m22 + UNITY_MATRIX_M._m00_m10_m20 * 0.5;
                o.dotRadius = dot(normalize(vec), normalize(UNITY_MATRIX_M._m02_m12_m22));
                o.lightDirVS = mul(UNITY_MATRIX_V, UNITY_MATRIX_M._m02_m12_m22);

                // Volume
                float3 viewDirOS = normalize(ObjSpaceViewDir(v.vertex));
                float NdotE = abs(dot(v.normal, viewDirOS));
                o.color.rgb = smoothstep(1 - _Rim, 1.0, NdotE) *_Color.rgb;
                o.color.a = 1.0 - (saturate(v.vertex.z - 0.5) / 0.5);

                o.texcoord = TRANSFORM_TEX(v.texcoord,_MainTex);

                return o;
            }

            half4 frag(v2f i) : Color
            {
                half4 col = fixed4(0,0,0,_Color.a);
                // Decal
                const float far = _ProjectionParams.z;
                float2 positionSS = i.positionSS.xy / i.positionSS.w;
                float depth;
                float3 normalVS;
                float4 depthAndNormal = tex2D(_CameraDepthNormalsTexture, positionSS);
                DecodeDepthNormal(depthAndNormal, depth, normalVS);
                float eyeDepth = depth * far;
                eyeDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, positionSS.xy));
                float3 cameraForwardDir = -UNITY_MATRIX_V._m20_m21_m22;
                float3 viewDirWS = normalize(UnityWorldSpaceViewDir(i.positionWS));
                float3 positionWS = ((eyeDepth * viewDirWS * (1.0 / dot(cameraForwardDir, viewDirWS))) + _WorldSpaceCameraPos);

                float3 vecWS;
                float power;
                float lightRadius = length(UNITY_MATRIX_M._m00_m10_m20) * 0.5;
                half3 rgb = tex2D(_MainTex, i.texcoord);
#if defined(_VOLUMEENABLED_ON)
                // Volume
                vecWS = positionWS - i.positionWS;
                power = saturate(length(vecWS) / lightRadius);
                half3 volumeRgb = rgb * i.color.rgb * i.color.a * power;
                col.rgb += volumeRgb.rgb;
#endif

#if defined(_DECALENABLED_ON)
                float lenLightDirWS = length(UNITY_MATRIX_M._m02_m12_m22);
                vecWS = positionWS - UNITY_MATRIX_M._m03_m13_m23;
                float lenVecWS = length(vecWS);
                power = lenVecWS > lenLightDirWS ? 0 : saturate(1.0 - (lenVecWS - (lenLightDirWS - lightRadius)) / lightRadius);
                float d = dot(normalize(vecWS), normalize(UNITY_MATRIX_M._m02_m12_m22));
                power *= saturate(d - i.dotRadius) / (1.0 - i.dotRadius);

                float NdotL = saturate(dot(normalVS, normalize(-i.lightDirVS)));
                power *= NdotL;

                half3 decalRgb = smoothstep(1 - _Rim, 1.0, power) * power *_Color.rgb;
                col.rgb += decalRgb.rgb;
#endif
                return col;
            }
            ENDCG
        }
    }
}

参考

フェイクポイントライト - テキトープログラム( ..)φメモ