Hey, I have a question regarding Unity’s .shadowAttenuation attribute for additional lights within a custom render feature in Unity 6000.0.29f1 URP.
Below is a basic shader that showcases the problem I have within my set-up.
Shader "Hidden/Fullscreen/Problem"
{
SubShader
{
Tags
{
"RenderPipeline" = "UniversalPipeline"
}
Pass
{
Name "Problem"
Cull Off
ZWrite Off
ZTest Always
HLSLPROGRAM
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
#pragma multi_compile _ _FORWARD_PLUS
#pragma multi_compile_fragment _ _SHADOWS_SOFT _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
#pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS
#pragma multi_compile_fragment _ _LIGHT_COOKIES
#pragma vertex Vert
#pragma fragment Frag
float4 Frag(Varyings IN) : SV_Target
{
float2 uv = IN.texcoord;
float sampledDepth = SAMPLE_TEXTURE2D_X(_CameraDepthTexture, sampler_PointClamp, uv).r;
float3 posWS = ComputeWorldSpacePosition(uv, sampledDepth, UNITY_MATRIX_I_VP); // recreate world position of current pixel
float shadow = 0;
#if _FORWARD_PLUS
// Forward+ rendering path needs this data before the light loop
InputData inputData = (InputData)0;
inputData.normalizedScreenSpaceUV = uv;
inputData.positionWS = posWS;
#endif
LIGHT_LOOP_BEGIN(1)
Light additionalLight = GetAdditionalPerObjectLight(lightIndex, posWS);
shadow = AdditionalLightRealtimeShadow(lightIndex, posWS);
LIGHT_LOOP_END
return shadow;
}
ENDHLSL
}
}
}
In my scene I have one additional light with a box right next to it.
If the additional light is a point or spot light, the shadowAttenuation value is 1 within the whole range of the light.
That is obviously wrong as this means it shines through the box next to it. For an additional directional light, it is 0 everywhere.
Unity’s lit material behaves correctly with additional light shadows (within the same scene). While looking through the code for it, I found this method
AdditionalLightRealtimeShadow(lightIndex, posWS);
The method fixes the behavior for the spot light,
for point and directional lights their respective errors persist.
I have also tried the same set-up but as a shader for a material.
Shader "Unlit/Custom/Toon"
{
Properties
{
[MainTexture] _BaseMap("Texture", 2D) = "white" {}
}
SubShader
{
Pass
{
Name "ForwardToon"
HLSLPROGRAM
// Include required URP core shader library
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
#pragma multi_compile _ _FORWARD_PLUS
#pragma multi_compile_fragment _ _SHADOWS_SOFT _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
#pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS
#pragma multi_compile_fragment _ _LIGHT_COOKIES
struct Attributes
{
float3 positionOS : POSITION;
float4 uv : TEXCOORD0;
float3 normalOS : NORMAL;
};
struct Varyings
{
float2 uv : TEXCOORD0;
float3 positionWS : TEXCOORD1;
float3 normalWS : NORMAL;
float4 clipPos : SV_POSITION;
};
#pragma vertex Vert
#pragma fragment Frag
sampler2D _BaseMap;
float4 _BaseMap_ST;
// Vertex Shader
Varyings Vert(Attributes IN)
{
Varyings OUT;
OUT.positionWS = TransformObjectToWorld(IN.positionOS);
OUT.normalWS = TransformObjectToWorldNormal(IN.normalOS);
OUT.clipPos = TransformObjectToHClip(IN.positionOS);
OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);
return OUT;
}
float4 Frag(Varyings IN) : SV_Target
{
#if _FORWARD_PLUS
// Forward+ rendering path needs this data before the light loop
InputData inputData = (InputData)0;
inputData.normalizedScreenSpaceUV = IN.uv;
inputData.positionWS = IN.positionWS;
#endif
// Preparing color
float3 additionalLightsColor = float3(0.0, 0.0, 0.0);
LIGHT_LOOP_BEGIN(GetAdditionalLightsCount())
Light additionalLight = GetAdditionalPerObjectLight(lightIndex, IN.positionWS);
#if _LIGHT_COOKIES
additionalLight.color *= SampleAdditionalLightCookie(lightIndex, IN.positionWS);
#endif
#if USE_STRUCTURED_BUFFER_FOR_LIGHT_DATA
float4 additionalLightPos = _AdditionalLightsBuffer[lightIndex].position;
#else
float4 additionalLightPos = _AdditionalLightsPosition[lightIndex];
#endif
additionalLightsColor += (additionalLight.color * (additionalLight.shadowAttenuation * additionalLight.distanceAttenuation));
additionalLightsColor = additionalLight.shadowAttenuation;
LIGHT_LOOP_END
return float4(additionalLightsColor, 1.0);
}
ENDHLSL
}
UsePass "Legacy Shaders/VertexLit/SHADOWCASTER"
}
}
Here Unity’s .shadowAttenuation attribute is correct for point lights, but for spot lights it moves when moving the camera and doesn’t stay static. Additional directional lights also do not work.
Is there any other way to get the correct shadowAttenuation value (in both cases)?
I appreciate any help, thanks!