Search Unity

Question Additional Light Data Fails at Certain View Angles

Discussion in 'Shaders' started by swardson, Jan 16, 2023.

  1. swardson

    swardson

    Joined:
    Mar 22, 2013
    Posts:
    4
    Hey all,

    I have a problem getting additional lights working for my volumetric fog shader. I have it working great with directional lights, but the additional light stuff seems to have some issues.

    How the system currently works is that I have a function that draws a sphere using ray marching at a point and with a certain radius. This was working great, so I began looking through the URP source code to find how additional lights are handled. I found the functions GetAdditionalLightsCount() and GetAdditionalLight() among several others that seemed promising.

    After running some tests though, I found an issue where I would only be able to get the light data at certain camera angles (See attached GIF). LightIssue.gif LightIssue.gif LightIssue.gif LightIssue.gif LightIssue.gif

    I have no idea why this is happening and I am starting to lose my mind lol.

    Thanks so much for any help. :)

    here is my code (ignore the class names, I haven't updated them yet... I was preoccupied with the issue lol):

    Code (HLSL):
    1. Shader "Hidden/Worlds End/VolumeSpotLights"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.     }
    7.     SubShader
    8.     {
    9.         // No culling or depth
    10.         Cull Off ZWrite Off ZTest Always
    11.  
    12.         Pass
    13.         {
    14.             HLSLPROGRAM
    15.  
    16.             #pragma vertex vert
    17.             #pragma fragment frag
    18.  
    19.             #pragma multi_compile _  _MAIN_LIGHT_SHADOWS_CASCADE
    20.  
    21.             #pragma prefer_hlslcc gles
    22.             #pragma exclude_renderers d3d11_9x
    23.  
    24.             #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
    25.             #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
    26.  
    27.             struct appdata
    28.             {
    29.                 float4 vertex : POSITION;
    30.                 float2 uv : TEXCOORD0;
    31.             };
    32.  
    33.             struct v2f
    34.             {
    35.                 float4 vertex : SV_POSITION;
    36.                 float2 uv : TEXCOORD0;
    37.             };
    38.  
    39.             struct SpotLight
    40.             {
    41.                 float3 pos;
    42.                 float3 dir;
    43.                 float4 color;
    44.             };
    45.  
    46.             sampler2D _MainTex;
    47.  
    48.             float3 _Pos;
    49.  
    50.             float _Intensity;
    51.             float _Scattering;
    52.             float _Steps;
    53.             float _JitterVolumetric;
    54.             float _MaxDistance;
    55.  
    56.             float GetDepth(float2 uv)
    57.             {
    58.                 #if UNITY_REVERSED_Z
    59.                     float depth = SampleSceneDepth(uv);
    60.                 #else
    61.                     // Adjust z to match NDC for OpenGL
    62.                     float depth = lerp(UNITY_NEAR_CLIP_VALUE, 1, SampleSceneDepth(uv));
    63.                 #endif
    64.  
    65.                 return depth;
    66.             }
    67.  
    68.             //Unity already has a function that can reconstruct world space position from depth
    69.             float3 GetWorldPos(float2 uv)
    70.             {
    71.                 float depth = GetDepth(uv);
    72.                 return ComputeWorldSpacePosition(uv, depth, UNITY_MATRIX_I_VP);
    73.             }
    74.  
    75.             float random( float2 p )
    76.             {
    77.                 return frac(sin(dot(p, float2(41, 289)))*45758.5453 )-0.5;
    78.             }
    79.  
    80.             float random01( float2 p )
    81.             {
    82.                 return frac(sin(dot(p, float2(41, 289)))*45758.5453 );
    83.             }
    84.            
    85.             //from Ronja https://www.ronja-tutorials.com/post/047-invlerp_remap/
    86.             float invLerp(float from, float to, float value)
    87.             {
    88.                 return (value - from) / (to - from);
    89.             }
    90.  
    91.             float remap(float origFrom, float origTo, float targetFrom, float targetTo, float value)
    92.             {
    93.                 float rel = invLerp(origFrom, origTo, value);
    94.                 return lerp(targetFrom, targetTo, rel);
    95.             }
    96.  
    97.             float2 raySphere(float3 sphereCentre, float sphereRadius, float3 rayOrigin, float3 rayDir)
    98.             {
    99.                 float3 offset = rayOrigin - sphereCentre;
    100.                 float a = 1; // Set to dot(rayDir, rayDir) if rayDir might not be normalized
    101.                 float b = 2 * dot(offset, rayDir);
    102.                 float c = dot (offset, offset) - sphereRadius * sphereRadius;
    103.                 float d = b * b - 4 * a * c; // Discriminant from quadratic formula
    104.  
    105.                 // Number of intersections: 0 when d < 0; 1 when d = 0; 2 when d > 0
    106.                 if (d > 0) {
    107.                     float s = sqrt(d);
    108.                     float dstToSphereNear = max(0, (-b - s) / (2 * a));
    109.                     float dstToSphereFar = (-b + s) / (2 * a);
    110.  
    111.                     // Ignore intersections that occur behind the ray
    112.                     if (dstToSphereFar >= 0) {
    113.                         return float2(dstToSphereNear, dstToSphereFar - dstToSphereNear);
    114.                     }
    115.                 }
    116.                 // Ray did not intersect sphere
    117.                 return float2(1.#INF, 0);
    118.             }
    119.  
    120.             float ShadowAtten(int lightIndex, float3 samplePoint, half3 lightDir)
    121.             {
    122.                 return AdditionalLightRealtimeShadow(lightIndex, TransformWorldToShadowCoord(samplePoint), lightDir);
    123.             }
    124.  
    125.  
    126.             float pointLightFogAtten(in float2 hitData, float3 lightPos, float lightRange, float3 samplePos, float intensity)
    127.             {
    128.                 float dstToLight = length(samplePos - lightPos);
    129.  
    130.                 // float accumFog = 1;
    131.  
    132.                 if(dstToLight > lightRange)
    133.                 {
    134.                     dstToLight = lightRange;
    135.                 }
    136.                 float accumFog = exp(-dstToLight / (lightRange / 10)) * intensity;
    137.                
    138.                 return accumFog;
    139.             }
    140.  
    141.             float fogMarch(in float2 uv, in float3 spherePos, int index)
    142.             {
    143.                 float3 worldPos = GetWorldPos(uv);
    144.  
    145.                 //we find out our ray info, that depends on the distance to the camera
    146.                 float3 startPosition = _WorldSpaceCameraPos;
    147.                 float3 rayVector = worldPos - startPosition;
    148.                 float3 rayDirection =  normalize(rayVector);
    149.                 float rayLength = length(rayVector);
    150.  
    151.                 //Depth
    152.                 float nonlin_depth = SampleSceneDepth(uv);
    153.                 float depth = LinearEyeDepth(nonlin_depth, _ZBufferParams.y) * rayLength;
    154.  
    155.                 //Sphere info
    156.                 float2 hitData = raySphere(spherePos, _Scattering, startPosition, rayDirection);
    157.                 float dstToSphere = hitData.x;
    158.                 float dstInsideSphere = hitData.y;
    159.  
    160.                 float stepLength = dstInsideSphere / _Steps;
    161.                 float dstLimit = min(depth - dstToSphere, dstInsideSphere);
    162.                
    163.                 float randomOffset = random01(uv) * stepLength * _JitterVolumetric / 100;
    164.  
    165.                 float dstTravelled = randomOffset;
    166.  
    167.                 float accumFog = 0;
    168.                 while(dstTravelled < dstLimit)
    169.                 {
    170.                     float3 samplePoint = startPosition + rayDirection * (dstToSphere + dstTravelled);
    171.  
    172.                     float shadowAtten = ShadowAtten(index, samplePoint, 0);
    173.                    
    174.                     if(shadowAtten > 0)
    175.                     {
    176.                         accumFog += pointLightFogAtten(hitData, spherePos, _Scattering, samplePoint, _Intensity);
    177.                     }
    178.                    
    179.                     dstTravelled += stepLength;
    180.                 }
    181.  
    182.                 //we need the average value, so we divide between the amount of samples
    183.                 accumFog /= _Steps;
    184.  
    185.                 return accumFog;
    186.             }
    187.  
    188.  
    189.             v2f vert (appdata v)
    190.             {
    191.                 v2f o;
    192.                 o.vertex = TransformWorldToHClip(v.vertex.xyz);
    193.                 o.uv = v.uv;
    194.                 return o;
    195.             }
    196.  
    197.             float4 frag (v2f i) : SV_Target
    198.             {
    199.                 float worldPos = GetWorldPos(i.uv);
    200.                 float4 col = tex2D(_MainTex, i.uv);
    201.  
    202.                 float fog = 0;
    203.                 half3 color = half3(0, 0, 0);
    204.                 uint lightCount = GetAdditionalLightsCount();
    205.                 for(uint l = 0; l < lightCount; l++)
    206.                 {
    207.                     Light light = GetAdditionalLight(l, worldPos, 1);
    208.                     color += light.color;
    209.                     fog += fogMarch(i.uv, _Pos, l);
    210.                 }
    211.  
    212.                 return col + (fog * float4(color.r, color.g, color.b, 1));
    213.             }
    214.             ENDHLSL
    215.         }
    216.     }
    217. }
    218.  
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering;
    3. using UnityEngine.Rendering.Universal;
    4.  
    5. public class VolumeSpotLights : ScriptableRendererFeature
    6. {
    7.     public enum DownSample
    8.     {
    9.         off = 1,
    10.         half = 2,
    11.         third = 3,
    12.         quarter = 4
    13.     };
    14.  
    15.     [System.Serializable]
    16.     public class Settings
    17.     {
    18.         public DownSample downsampling = DownSample.off;
    19.  
    20.         [Space(10)]
    21.         [Header("Apperance")]
    22.         public Color tint = Color.white;
    23.         public float intensity = 1;
    24.         public float scattering = 0;
    25.  
    26.         public Vector3 position;
    27.  
    28.         [Space(10)]
    29.         [Header("Performance")]
    30.         [Min(0)] public float steps = 24;
    31.         public float maxDistance = 75;
    32.         [Min(0)] public float jitter = 250;
    33.  
    34.         [Space(10)]
    35.         [Header("Initialization")]
    36.         public RenderPassEvent renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
    37.     }
    38.  
    39.     public Settings settings = new Settings();
    40.  
    41.     class Pass : ScriptableRenderPass
    42.     {
    43.         public Settings settings;
    44.         private RenderTargetIdentifier source;
    45.         RenderTargetHandle tempTexture;
    46.         // RenderTargetHandle lowResDepthRT;
    47.  
    48.         Material material;
    49.  
    50.         private string profilerTag;
    51.  
    52.         public void Setup(RenderTargetIdentifier source)
    53.         {
    54.             this.source = source;
    55.         }
    56.  
    57.         public Pass(string profilerTag)
    58.         {
    59.             this.profilerTag = profilerTag;
    60.         }
    61.  
    62.         public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
    63.         {
    64.             var original = cameraTextureDescriptor;
    65.             int divider = (int)settings.downsampling;
    66.  
    67.             if (Camera.current != null) //This is necessary so it uses the proper resolution in the scene window
    68.             {
    69.                 cameraTextureDescriptor.width = (int)Camera.current.pixelRect.width / divider;
    70.                 cameraTextureDescriptor.height = (int)Camera.current.pixelRect.height / divider;
    71.                 original.width = (int)Camera.current.pixelRect.width;
    72.                 original.height = (int)Camera.current.pixelRect.height;
    73.             }
    74.             else //regular game window
    75.             {
    76.                 cameraTextureDescriptor.width /= divider;
    77.                 cameraTextureDescriptor.height /= divider;
    78.             }
    79.  
    80.             //R8 has noticeable banding
    81.             cameraTextureDescriptor.colorFormat = RenderTextureFormat.ARGB32;
    82.  
    83.             //we dont need to resolve AA in every single Blit
    84.             cameraTextureDescriptor.msaaSamples = 1;
    85.  
    86.             //we need to assing a different id for every render texture
    87.             // lowResDepthRT.id = 1;
    88.  
    89.             cmd.GetTemporaryRT(tempTexture.id, cameraTextureDescriptor);
    90.             ConfigureTarget(tempTexture.Identifier());
    91.  
    92.             // cmd.GetTemporaryRT(lowResDepthRT.id, cameraTextureDescriptor);
    93.             // ConfigureTarget(lowResDepthRT.Identifier());
    94.            
    95.             ConfigureClear(ClearFlag.All, Color.black);
    96.         }
    97.  
    98.         public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    99.         {
    100.             CommandBuffer cmd = CommandBufferPool.Get(profilerTag);
    101.             cmd.Clear();
    102.  
    103.             //it is very important that if something fails our code still calls
    104.             //CommandBufferPool.Release(cmd) or we will have a HUGE memory leak
    105.             try
    106.             {
    107.                 material = new Material(Shader.Find("Hidden/Worlds End/VolumeSpotLights"));
    108.                
    109.                 material.SetVector("_Pos", settings.position);
    110.  
    111.                 material.SetFloat("_Scattering", settings.scattering);
    112.                 material.SetFloat("_Steps", settings.steps);
    113.                 material.SetFloat("_JitterVolumetric", settings.jitter);
    114.                 material.SetFloat("_MaxDistance", settings.maxDistance);
    115.                 material.SetFloat("_Intensity", settings.intensity);
    116.                 // material.SetFloat("_GaussSamples", settings.gaussBlur.samples);
    117.                 // material.SetFloat("_GaussAmount", settings.gaussBlur.amount);
    118.                 // material.SetColor("_Tint", settings.tint);
    119.  
    120.                 //raymarch
    121.                 cmd.Blit(source, tempTexture.Identifier(), material, 0);
    122.  
    123.                 //bilateral blu X, we use the lowresdepth render texture for other things too, it is just a name
    124.                 // cmd.Blit(tempTexture.Identifier(), lowResDepthRT.Identifier(), material, 1);
    125.  
    126.                 //bilateral blur Y
    127.                 // cmd.Blit(lowResDepthRT.Identifier(), tempTexture.Identifier(), material, 2);
    128.  
    129.                 //save it in a global texture
    130.                 // cmd.SetGlobalTexture("_volumetricTexture", tempTexture.Identifier());
    131.                
    132.                 //downsample depth
    133.                 // cmd.Blit(source, lowResDepthRT.Identifier(), material, 4);
    134.                 // cmd.SetGlobalTexture("_LowResDepth", lowResDepthRT.Identifier());
    135.  
    136.                 //upsample and composite
    137.                 // cmd.Blit(source, temptexture3.Identifier(), material, 3);
    138.                 cmd.Blit(tempTexture.Identifier(), source);
    139.  
    140.                 context.ExecuteCommandBuffer(cmd);
    141.             }
    142.             catch
    143.             {
    144.                 Debug.LogError("Error");
    145.             }
    146.  
    147.             cmd.Clear();
    148.             CommandBufferPool.Release(cmd);
    149.         }
    150.     }
    151.  
    152.     Pass pass;
    153.     RenderTargetHandle renderTextureHandle;
    154.     public override void Create()
    155.     {
    156.         pass = new Pass("Volumetric Spot Light");
    157.         name = "Volumetric Spot Light";
    158.         pass.settings = settings;
    159.         pass.renderPassEvent = settings.renderPassEvent;
    160.     }
    161.  
    162.     public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    163.     {
    164.         var cameraColorTargetIdent = renderer.cameraColorTarget;
    165.         pass.Setup(cameraColorTargetIdent);
    166.         renderer.EnqueuePass(pass);
    167.     }
    168. }
    169.  
    170.  
    171.  
     
  2. swardson

    swardson

    Joined:
    Mar 22, 2013
    Posts:
    4
    Also, that is only with point lights, spotlights don't work at all, and directional lights work perfectly.
     
  3. swardson

    swardson

    Joined:
    Mar 22, 2013
    Posts:
    4
    After further testing in a blank project with none of the ray march stuff, the additional light stuff fails completely with all of the light types.