Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Forward+ not working with custom shader

Discussion in 'Universal Render Pipeline' started by Lazy_Evaluation, Mar 21, 2023.

  1. Lazy_Evaluation

    Lazy_Evaluation

    Joined:
    Nov 16, 2019
    Posts:
    16
    Hello! I'm building a game with a toon graphic style which requires many lights to be displayed, more than 8 per object (so I can't use neither deferred nor Forward rendering). I updated Unity to 2022.2.10f1 and URP to 14.0.6, and set the rendering setting to Forward+. I had to adapt my hlsl code to make it work with Forward+, and I modified NedMakesGame's Lighting.hlsl script (from a toon tutorial video) to make it work with Forward+:
    Code (CSharp):
    1. #ifndef CUSTOM_LIGHTING_INCLUDED
    2. #define CUSTOM_LIGHTING_INCLUDED
    3.  
    4.  
    5. float GetLightIntensity(float3 color)
    6. {
    7.     return max(color.r, max(color.g, color.b));
    8. }
    9.  
    10. void CalculateMainLight_float(float3 WorldPos, out float3 Direction, out float3 Color, out half DistanceAtten, out half ShadowAtten, out float Intensity)
    11. {
    12. #ifdef SHADERGRAPH_PREVIEW
    13.     Direction = half3(0.5, 0.5, 0);
    14.     Color = 1;
    15.     Intensity = 0;
    16.     DistanceAtten = 1;
    17.     ShadowAtten = 1;
    18. #else
    19. #if SHADOWS_SCREEN
    20.     half4 clipPos = TransformWorldToHClip(WorldPos);
    21.     half4 shadowCoord = ComputeScreenPos(clipPos);
    22. #else
    23.     half4 shadowCoord = TransformWorldToShadowCoord(WorldPos);
    24. #endif
    25.     Light mainLight = GetMainLight(shadowCoord);
    26.     Direction = mainLight.direction;
    27.     Color = mainLight.color;
    28.     DistanceAtten = mainLight.distanceAttenuation;
    29.     ShadowAtten = mainLight.shadowAttenuation;
    30.     Intensity = GetLightIntensity(mainLight.color);
    31. #endif
    32. }
    33.  
    34.  
    35. #ifndef SHADERGRAPH_PREVIEW
    36. // This function gets additional light data and calculates realtime shadows
    37. Light GetAdditionalLightForToon(int pixelLightIndex, float3 worldPosition)
    38. {
    39.     // Convert the pixel light index to the light data index
    40.     int perObjectLightIndex = GetPerObjectLightIndex(pixelLightIndex);
    41.     // Call the URP additional light algorithm. This will not calculate shadows, since we don't pass a shadow mask value
    42.     Light light = GetAdditionalPerObjectLight(perObjectLightIndex, worldPosition);
    43.     // Manually set the shadow attenuation by calculating realtime shadows
    44.     light.shadowAttenuation = AdditionalLightRealtimeShadow(perObjectLightIndex, worldPosition);
    45.     return light;
    46. }
    47. #endif
    48. void AddAdditionalLights_float(float Smoothness, float3 WorldPosition, float3 WorldNormal, float3 WorldView,
    49.     float MainDiffuse, float MainSpecular, float3 MainColor, float3 ScreenPosition,
    50.     out float Diffuse, out float Specular, out float3 Color)
    51. {
    52.  
    53.     float mainIntensity = GetLightIntensity(MainColor);
    54.     Diffuse = 0;
    55.     Specular = 0;
    56.     Color = MainColor;
    57.  
    58.  
    59.     float totalDiffuse = 0; // Aggiunto per accumulare i valori di thisDiffuse
    60.  
    61. #ifndef SHADERGRAPH_PREVIEW
    62.     InputData inputData = (InputData) 0;
    63.     float highestDiffuse = Diffuse;
    64.     float maxIntensity = 0;
    65.     float intensity = 0;
    66.     uint meshRenderingLayers = GetMeshRenderingLayer();
    67.  
    68.     inputData.normalizedScreenSpaceUV = ScreenPosition;
    69.     inputData.positionWS = WorldPosition;
    70.     uint lightsCount = GetAdditionalLightsCount();
    71.     LIGHT_LOOP_BEGIN(lightsCount)
    72.     Light light = GetAdditionalLight(lightIndex, WorldPosition);
    73.     #ifdef _LIGHT_LAYERS
    74.         if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
    75. #endif
    76.     {
    77.         half NdotL = saturate(dot(WorldNormal, light.direction));
    78.         half atten = light.distanceAttenuation * light.shadowAttenuation * GetLightIntensity(light.color);
    79.         half thisDiffuse = atten * NdotL;
    80.         half thisSpecular = LightingSpecular(thisDiffuse, light.direction, WorldNormal, WorldView, 1, Smoothness);
    81.         Diffuse += thisDiffuse;
    82.         Specular += thisSpecular;
    83.         intensity = GetLightIntensity(light.color);
    84.  
    85.         if (thisDiffuse > 0)
    86.         {
    87.             if (intensity > maxIntensity)
    88.             {
    89.                 maxIntensity = intensity;
    90.                 Color = light.color;
    91.             }
    92.         }
    93.     }
    94.         LIGHT_LOOP_END
    95. #endif
    96.     }
    97. #endif
    98.  
    As you can see I'm using the LIGHT_LOOP system and I changed the GetAdditionalPerObjectLight function, and it works. When there are few objects and lights.
    If I have many objects and many lights, the game crashes with this error:

    This is a big problem and it should mean that the GPU is somehow under great stress, but switching to Forward or Deferred rendering, with the same amount of objects and lights in the same scene, doesn't make the game crash. So I find a bit weird that a feature that has been literally implemented to handle more lights can't even handle as many lights as Forward or Deferred do. I guess I'm doing something wrong with the code...
    In small scenes with few objects, the Forward+ works correctly even with 20/30 lights, as it should. The scene where the game crashes has thousand of objects (not all of them are nearby a light, I'd say that there are about 30-40 objects inside the lights ranges) and about 4-8 lights.
    Could somebody help? My GPU drivers are updated and I'm using a RX480 4GB card.
     
    Last edited: Mar 21, 2023
  2. wwWwwwW1

    wwWwwwW1

    Joined:
    Oct 31, 2021
    Posts:
    761
    Will this issue still happen if switching to URP's default shader rather than custom shader graph?

    If yes, I think you can submit a bug report with a reproduceable project.
     
  3. Lazy_Evaluation

    Lazy_Evaluation

    Joined:
    Nov 16, 2019
    Posts:
    16
    No, it doesn't.
    Digging more, I managed to find what could be the problem: the LIGHT_LOOP might not be supported on Forward+.
    I changed the code to have this:

    Code (CSharp):
    1. #if USE_FORWARD_PLUS
    2.     for(uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++)
    3.     {
    4.     Light light = GetAdditionalLight(lightIndex, WorldPosition);
    5.         half NdotL = max(0, dot(WorldNormal, light.direction));
    6.         half atten = light.distanceAttenuation * light.shadowAttenuation * GetLightIntensity(light.color);
    7.         half thisDiffuse = atten * NdotL;
    8.         thisSpecular += LightingSpecular(thisDiffuse, light.direction, WorldNormal, WorldView, 1, Smoothness);
    9.         Diffuse += thisDiffuse;
    10.  
    11.         Color = (thisDiffuse > 0 && GetLightIntensity(light.color) > maxIntensity) ? light.color : Color;
    12.         maxIntensity = (thisDiffuse > 0 && GetLightIntensity(light.color) > maxIntensity) ? GetLightIntensity(light.color) : maxIntensity;
    13.     }
    14. #endif
    Now, the game doesn't crash. But the problem is that there's no light shown, because the loop takes a minimum between MAX_VISIBLE_LIGHTS and URP_FP_DIRECTIONAL_LIGHTS_COUNT, which should be 1 with only one directional light. If I, for example, set a static number instead of URP_FP_DIRECTIONAL_LIGHTS_COUNT, the lights are shown correctly and the game doesn't crash, but for obvious reasons everything is a big buggy because the number will most likely be greater than the lights count in my scene, resulting in the lights staying visible even when they are not enabled.

    The solution to this problem seems now to be finding the total additional lights count, so I can place it in the loop condition. GetAdditionalLightsCount(); doesn't work with Forward+. Any alternative? Also looked for URP_FP_LIGHTS_COUNT but it doesn't exist :-(
     
  4. wwWwwwW1

    wwWwwwW1

    Joined:
    Oct 31, 2021
    Posts:
    761
    Then it should be a custom shader issue, you can refer to how Lit shader calculates lighting in Forward+ in "URP-Package/ShaderLibrary/Lighting.hlsl" (link is for 2023.2a).

    Example from the link:
    Code (CSharp):
    1.     #if defined(_ADDITIONAL_LIGHTS)
    2.     uint pixelLightCount = GetAdditionalLightsCount();
    3.  
    4.     #if USE_FORWARD_PLUS
    5.     for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++)
    6.     {
    7.         FORWARD_PLUS_SUBTRACTIVE_LIGHT_CHECK
    8.  
    9.         Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor);
    10.  
    11. #ifdef _LIGHT_LAYERS
    12.         if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
    13. #endif
    14.         {
    15.             // Do custom lighting here.
    16.         }
    17.     }
    18.     #endif
    19.  
    20.     LIGHT_LOOP_BEGIN(pixelLightCount)
    21.         Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor);
    22.  
    23. #ifdef _LIGHT_LAYERS
    24.         if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
    25. #endif
    26.         {
    27.             // Repeat the same custom lighting here.
    28.         }
    29.     LIGHT_LOOP_END
    30.     #endif
    It's quite confusing and I'm also looking forward to the SRP shader documentation improvements.
     
  5. Lazy_Evaluation

    Lazy_Evaluation

    Joined:
    Nov 16, 2019
    Posts:
    16

    It's the code which I've taken as an example for my shader. The only difference that I do is that I don't use the four argument GetAdditionalLight method because I can't have InputData and SurfaceData completely loaded without passing about 20 variables in my shader graph and spending hours setting them up, creating a huge spaghetti code. The rest, is pretty much the same. The LIGHT_LOOP works, in small scenes, but in the game it crashes with a D3D11 error as another post (without solution) in the forum reports. The for loop, doesn't crash, but until I find a way to count all the lights in a scene I can't use it.
     
  6. wwWwwwW1

    wwWwwwW1

    Joined:
    Oct 31, 2021
    Posts:
    761
    You can replace it with the one that doesn't handle shadows.

    What I usually do to solve an unknown shader problem (for me) is to make it work first (maybe from an example) and then clean it up until I found out where the problem is.

    You can also try reporting this as a bug and they should point out what went wrong later as long as it's confirmed by QA.
     
  7. ZbigniewBrzozowski

    ZbigniewBrzozowski

    Joined:
    Jan 26, 2019
    Posts:
    4
    Hello. Did you find solution to your problem? I'm noob when it comes to coding shaders - could you please give me some tutorials to match Unity Toon Shader with Forward+ ? I already research NedMakesGame channel thanks to you. Thank you.
     
  8. Lazy_Evaluation

    Lazy_Evaluation

    Joined:
    Nov 16, 2019
    Posts:
    16
    Yes! First, you have to add _FORWARD_PLUS as a boolean keyword, like every other special keyword used in the shader. Be sure to use it in every custom shader that you're using! upload_2023-4-15_11-46-14.png

    Second, use the LIGHT_LOOP_BEGIN and LIGHT_LOOP_END to loop between all the lights. My C# code is this:
    Code (CSharp):
    1.  
    2. #ifndef CUSTOM_LIGHTING_INCLUDED
    3. #define CUSTOM_LIGHTING_INCLUDED
    4.  
    5.  
    6. float max3(float a, float b, float c)
    7. {
    8.     return max(a, max(b, c));
    9. }
    10.  
    11. float GetLightIntensity(float3 color)
    12. {
    13.     return max3(color.r, color.g, color.b);
    14. }
    15.  
    16.  
    17.  
    18. void CalculateMainLight_float(float3 WorldPos, out float3 Direction, out float3 Color, out half DistanceAtten, out half ShadowAtten, out float Intensity)
    19. {
    20. #ifdef SHADERGRAPH_PREVIEW
    21.     Direction = half3(0.5, 0.5, 0);
    22.     Color = 1;
    23.     Intensity = 0;
    24.     DistanceAtten = 1;
    25.     ShadowAtten = 1;
    26. #else
    27. #if SHADOWS_SCREEN
    28.     half4 clipPos = TransformWorldToHClip(WorldPos);
    29.     half4 shadowCoord = ComputeScreenPos(clipPos);
    30. #else
    31.     half4 shadowCoord = TransformWorldToShadowCoord(WorldPos);
    32. #endif
    33.     Light mainLight = GetMainLight(shadowCoord);
    34.     Direction = mainLight.direction;
    35.     Color = mainLight.color;
    36.     DistanceAtten = mainLight.distanceAttenuation;
    37.     ShadowAtten = mainLight.shadowAttenuation;
    38.     Intensity = GetLightIntensity(mainLight.color);
    39. #endif
    40. }
    41.  
    42.  
    43. void AddAdditionalLights_float(float Smoothness, float3 WorldPosition, float3 WorldNormal, float3 WorldView,
    44.     float MainDiffuse, float MainSpecular, float3 MainColor, float3 ScreenPosition,
    45.     out float Diffuse, out float Specular, out float3 Color)
    46. {
    47.     float mainIntensity = GetLightIntensity(MainColor);
    48.     Diffuse = 0;
    49.     Specular = 0;
    50.     Color = MainColor;
    51.  
    52. #ifndef SHADERGRAPH_PREVIEW
    53.     InputData inputData = (InputData) 0;
    54.     SurfaceData surfaceData;
    55.     AmbientOcclusionFactor aoFactor = CreateAmbientOcclusionFactor(inputData, surfaceData);
    56.     half thisSpecular = 0;
    57.     half maxIntensity = 0;
    58.     uint meshRenderingLayers = GetMeshRenderingLayer();
    59.  
    60.     inputData.normalizedScreenSpaceUV = ScreenPosition;
    61.     inputData.positionWS = WorldPosition;
    62.     uint lightsCount = GetAdditionalLightsCount();
    63.     half4 shadowMask = CalculateShadowMask(inputData);
    64.     uint _AdditionalLightsDirectionalCount = 128;
    65.  
    66. #if USE_FORWARD_PLUS
    67.     for(uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++)
    68.     {
    69.     Light light = GetAdditionalLight(lightIndex, WorldPosition);
    70.         half NdotL = max(0, dot(WorldNormal, light.direction));
    71.         half atten = light.distanceAttenuation * light.shadowAttenuation * GetLightIntensity(light.color);
    72.         half thisDiffuse = atten * NdotL;
    73.         thisSpecular += LightingSpecular(thisDiffuse, light.direction, WorldNormal, WorldView, 1, Smoothness);
    74.         Diffuse += thisDiffuse;
    75.  
    76.         Color = (thisDiffuse > 0 && GetLightIntensity(light.color) > maxIntensity) ? light.color : Color;
    77.         maxIntensity = (thisDiffuse > 0 && GetLightIntensity(light.color) > maxIntensity) ? GetLightIntensity(light.color) : maxIntensity;
    78.     }
    79. #endif
    80.    
    81.    
    82.     LIGHT_LOOP_BEGIN(lightsCount)
    83.  
    84.     Light light = GetAdditionalLight(lightIndex, WorldPosition);
    85.         half NdotL = max(0, dot(WorldNormal, light.direction));
    86.         half atten = light.distanceAttenuation * light.shadowAttenuation * GetLightIntensity(light.color);
    87.         half thisDiffuse = atten * NdotL;
    88.         thisSpecular += LightingSpecular(thisDiffuse, light.direction, WorldNormal, WorldView, 1, Smoothness);
    89.         Diffuse += thisDiffuse;
    90.  
    91.         Color = (thisDiffuse > 0 && GetLightIntensity(light.color) > maxIntensity) ? light.color : Color;
    92.         maxIntensity = (thisDiffuse > 0 && GetLightIntensity(light.color) > maxIntensity) ? GetLightIntensity(light.color) : maxIntensity;
    93.     LIGHT_LOOP_END
    94.  
    95.     Specular = thisSpecular;
    96. #endif
    97. }
    98. #endif
    It's still based on NedMakesGames code. It still uses all the subshaders and most of the tutorial's graph structure. You can see the product of my shader here: https://twitter.com/senfinecogames/status/1637766562378661888?s=20
     
  9. nasos_333

    nasos_333

    Joined:
    Feb 13, 2013
    Posts:
    13,288
    Could the light count be passed from script and do a normal for loop ? I think that would be safer.
     
  10. kripto289

    kripto289

    Joined:
    Feb 21, 2013
    Posts:
    501
    What this stupid code doing?

    Code (CSharp):
    1.  
    2. for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++)
    3. {
    4.                 Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor);
    5.  
    this should never work because lightIndex is always 1 (with 1 dir light) and all other lights just ignored.

    I'm trying to implement forward+, but looks like it cant work by design.

    PS I also like this

    cmd.SetGlobalVector("_FPParams0", math.float4(m_ZBinScale, m_ZBinOffset, m_LightCount, m_DirectionalLightCount));
    #define URP_FP_PROBES_BEGIN ((uint)_FPParams0.z)

    Again wtf, why "m_LightCount" named as "URP_FP_PROBES_BEGIN" ????
     
    nasos_333 likes this.
  11. Lazy_Evaluation

    Lazy_Evaluation

    Joined:
    Nov 16, 2019
    Posts:
    16
    No, Forward Plus requires the LIGHT_LOOP_BEGIN. It's a special loop made to loop all the lights.


    Well, first of all chill down, we're here to help or get help in a 100% friendly way. I can understand that this is an uncommon problem with few resources online, I miself spent one/two months making this shader work. Months have passed and I don't really remember perfectly how my shader works - it does, and my Forward+ shader works flawelssy, handling hundreds of lights.
    As far as I can remeber, that loop is either used to get multiple additional lights, or is something that must be used anyway (even with one directional light) to get the main light with the Forward+ setting on. I guess it's the second option because you can find "that stupid code" inside Unity's code:

    https://github.com/Unity-Technologi...pelines.universal/ShaderLibrary/Lighting.hlsl

    This shader should also work as an example for you. There are a few more online iirc, but I'm not sure.

    Forward+ is not easy to implement. Remember to add all the custom keywords:
    upload_2023-11-20_10-34-35.png


    Do never forget the Forward + keyword:
    upload_2023-11-20_10-35-0.png

    Those keywords all need to have the same Node settings (multicompile, global, all, and the two booleans down unchecked).

    I've not worked with probes so I can't help you with that last code. :-(
     
    Reanimate_L and nasos_333 like this.
  12. nasos_333

    nasos_333

    Joined:
    Feb 13, 2013
    Posts:
    13,288
    Thanks for the details, is very useful :)
     
    Lazy_Evaluation likes this.
  13. kripto289

    kripto289

    Joined:
    Feb 21, 2013
    Posts:
    501
    You misunderstood me. I addressed this to the unity developer who wrote the code.
    I'm writing volumetric light and use the code from the
    https://github.com/Unity-Technologi...pelines.universal/ShaderLibrary/Lighting.hlsl
    https://github.com/Unity-Technologi...s.universal/ShaderLibrary/RealtimeLights.hlsl

    As I said before, this unity code (lighting.hlsl) doesn't make sense.

    Code (CSharp):
    1.  
    2.  
    3. Light mainLight = GetMainLight(inputData, shadowMask, aoFactor);
    4.  
    5.     MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, aoFactor);
    6.  
    7.     inputData.bakedGI *= surfaceData.albedo;
    8.  
    9.     LightingData lightingData = CreateLightingData(inputData, surfaceData);
    10. #ifdef _LIGHT_LAYERS
    11.     if (IsMatchingLightLayer(mainLight.layerMask, meshRenderingLayers))
    12. #endif
    13.     {
    14.         lightingData.mainLightColor += CalculateBlinnPhong(mainLight, inputData, surfaceData);
    15.     }
    16.  
    17.     #if defined(_ADDITIONAL_LIGHTS)
    18.     uint pixelLightCount = GetAdditionalLightsCount();
    19.  
    20.     #if USE_FORWARD_PLUS
    21.     for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++)
    22.     {
    23.         FORWARD_PLUS_SUBTRACTIVE_LIGHT_CHECK
    24.  
    25.         Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor);
    26. #ifdef _LIGHT_LAYERS
    27.         if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
    28. #endif
    29.         {
    30.             lightingData.additionalLightsColor += CalculateBlinnPhong(light, inputData, surfaceData);
    31.         }
    32.     }
    33.     #endif
    34.  
    35.     LIGHT_LOOP_BEGIN(pixelLightCount)
    36.         Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor);
    37. #ifdef _LIGHT_LAYERS
    38.         if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
    39. #endif
    40.         {
    41.             lightingData.additionalLightsColor += CalculateBlinnPhong(light, inputData, surfaceData);
    42.         }
    43.     LIGHT_LOOP_END
    44.     #endif
    45.  
    46.     #if defined(_ADDITIONAL_LIGHTS_VERTEX)
    47.     lightingData.vertexLightingColor += inputData.vertexLighting * surfaceData.albedo;
    48.     #endif
    49.  
    50.  
    1) dir light computed using " Light mainLight = GetMainLight(inputData, shadowMask, aoFactor);"
    since direct light affects all objects in the scene, it makes no sense to use forward+ for dir lights.

    2) forward+ used only with the macro " #if defined(_ADDITIONAL_LIGHTS)"
    so "URP_FP_DIRECTIONAL_LIGHTS_COUNT" doesn't make sense in this case.

    3) URP_FP_DIRECTIONAL_LIGHTS_COUNT always 1 (or 0), while "URP_FP_PROBES_BEGIN" works correctly.
    Not sure why it names as "URP_FP_PROBES_BEGIN", because its visible additional light counts (m_LightCount).
    cmd.SetGlobalVector("_FPParams0", math.float4(m_ZBinScale, m_ZBinOffset, m_LightCount, m_DirectionalLightCount));

    4) code looks strange, because it compute additional lightings twice
    Code (CSharp):
    1.  
    2.  
    3. #if USE_FORWARD_PLUS
    4. //additionalCode
    5. #endif
    6.  
    7. uint pixelLightCount = GetAdditionalLightsCount();
    8. LIGHT_LOOP_BEGIN(pixelLightCount)
    9. LIGHT_LOOP_END
    although GetAdditionalLightsCount return 0 if forward+ enabled (and compiller just ignore it), but this code raises questions anyway.

    5) "LIGHT_LOOP_BEGIN" can handle "forward_plus" macro, but it's never called in forward+.

    Code (CSharp):
    1. #if USE_FORWARD_PLUS
    2.     #define LIGHT_LOOP_BEGIN(lightCount) { \
    3.     uint lightIndex; \
    4.     ClusterIterator _urp_internal_clusterIterator = ClusterInit(inputData.normalizedScreenSpaceUV, inputData.positionWS, 0); \
    5.     [loop] while (ClusterNext(_urp_internal_clusterIterator, lightIndex)) { \
    6.         lightIndex += URP_FP_DIRECTIONAL_LIGHTS_COUNT; \
    7.         FORWARD_PLUS_SUBTRACTIVE_LIGHT_CHECK
    8.     #define LIGHT_LOOP_END } }
    9. #else
    10.     #define LIGHT_LOOP_BEGIN(lightCount) \
    11.     for (uint lightIndex = 0u; lightIndex < lightCount; ++lightIndex) {
    12.     #define LIGHT_LOOP_END }
    13. #endif
    This macro raises questions again.

    6) GetAdditionalLightsCount(); doesnt work with custompass (or postprocessing), because AdditionalLightsCount is set on per-object basis by unity's rendering code.
    And there is no documentation why this doesnt work properly.


    This foward+ code raises too much questions
     
    Last edited: Nov 20, 2023