環境
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 } } }