Search Unity

Feature Request [CUSTOM SOLUTION] Passing light's inspector data to shaders (and passing per-light custom data)

Discussion in 'Universal Render Pipeline' started by DevilBlackDeath, Jun 2, 2022.

  1. DevilBlackDeath

    DevilBlackDeath

    Joined:
    Mar 16, 2014
    Posts:
    14
    I formally changed the title and tag as a feature request for what I need (which might be useful for others using URP hopefully). I'm also "archiving" my original message so the top message reflects the feature request.

    I will combine feature requests in one, tell me if I should separate them into multiple threads ;)


    Main feature request

    Expose the color and intensity of lights as chosen in inspector to the shaders. I would say inspectorColor and intensity as member names would be ideal (maybe inspectorIntensity so shader coders don't go thinking they should multiply the intensity by the attenuation ?). Afaik this should be fairly "easy" as VisibleLight does return the Light component in its light member. Then all it would take is gather that and send it to the shader in the ForwardLights. (I assume this would also entail adding these membersas properties to the shader in Input.hlsl)[/SIZE]
    Secondary requests
    • Give the developers the ability to get a list of visible Light objects from the scene ordered the same as they are in the shaders so we can pass any amount of custom shader data (my shader doesn't go that far, but someone could one day want to use a color ramp in their light for a specific toon shading effect or maybe some lights could be used to appear differently on certain objects for puzzles, or so they could use math-based cookies based on functions like sin). Being able to do it separately for each pass might be even better ? I also assume this function would have to be used in OnPreRender to make sure the pipeline has made all necessary calculations for the current frame ?
    • Give the developers access to each pass' data as read-only. This actually automatically solves the first secondary request as we would be able to access RenderingData and all of its component and as such access to the visible lights list and so on (though a simpler solution for the first request would certainly be better as getting an already ordered list of light components that can be iterated through to send to the shader would be easier for less techie shader coders).
    Thanks in advance, I hope you will consider these requests with attention as I truly believe they can give a HUGE amount of freedom for URP shaders, which afaik is the intended pipeline for any non-photorealistic shading.

    Have a good day everyone :)

    OLD POST


    Hello everyone,

    I'm currently working on a cel-shading inspired shader for a game. The thing is, I absolutely need to be able to get the "raw" color and intensity of the additional lights (non-main).

    I'd want to pass that as global arrays to the shaders just like Unity does by default to expose data for its shaders (through arrays like "_AdditionalLightColors").

    My question is : what would be the best way to go about it ? Do I put an empty managing GameObject in all of my scenes, said GameObject updating this array on every updates ? Or is there a better way to do that ?

    I initially thought of doing that in the lights themselves so they update only when needed, but it would seem Unity themselves don't do it for their data. Also, it would create a lot of overhead to get the current light's index in the list of visible lights !

    Another problem is the limit of light per object is 8, but I don't even know how Unity handles that. Is each object by default responsible for finding its own visible lights ? It's very opaque and finding any information on it it's hard, but I'd like my arrays to merge seamlessly with Unity's coding as much as possible (mostly for ease of coding). Any help is appreaciated :)

    Thanks in advance !
     
    Last edited: Jun 14, 2022
  2. DevilBlackDeath

    DevilBlackDeath

    Joined:
    Mar 16, 2014
    Posts:
    14
    MOST INFORMATION IN THIS POST IS OBSOLETE

    I might have found a solution BUT any other solution is still appreciated, and if I could have confirmation from either an Unity dev or someone else it'd be great.

    After displaying the value of 'UnityEngine.Rendering.Universal.UniversalRenderPipeline.maxVisibleAdditionalLights' in the console, it would seem said value is 256. Fortunately that would seem to indicate the various _AdditionalLights... Arrays contain all lights information at all times, and shaders rely on GetPerObjectLightIndex() to find which lights are the ones affecting the currently shaded mesh ! Now the matter is more how are lights sorted in this array. I'd assume it's probably whatever order they're returned in when getting all Light GameObjects in the current scene but I'll have to try that out to confirm it !

    Hopefully this all makes sense, do ask for clarification if there's something unclear in what I said.

    Thanks in advance :)

    Edit : It would seem I'd be able to figure out the light order by accessing RenderingData, trying to figure out where this is declared or accessible for the URP. Although I guess I might as well actually request properly if color and intensity (as inputted in the editor's inspector of respective lights) could be exposed ? Meaning as new struct members for the Light struct (the one I assume is being used by InitializeLightConstants to send info to the shader). I'll keep doing my workaround as a temporary makeshift solution, but I think long-run, exposing these would be beneficial (as well as anything that can't be calculated from what already is exposed).

    Thanks again a lot in advance, and I'll keep updating here ! :)
     
    Last edited: Jun 7, 2022
  3. DevilBlackDeath

    DevilBlackDeath

    Joined:
    Mar 16, 2014
    Posts:
    14
    I've tried everything I've said and here are my conclusions : I cannot seem to access to anything from the render passes. Culling results, light and rendering datas... It also seems the lights are not ordered by the scene order, which makes sense, since they're returned to the pipeline by a function returning visible lights (which probably do some culling-related calculations, and return lights in the order it found them to be visible in).

    As such I formally changed the title and tag as a feature request for what I need (which might be useful for others using URP hopefully).

    I will combine feature requests in one, tell me if I should separate them into multiple threads ;)

    Main feature request
    Expose the color and intensity of lights as chosen in inspector to the shaders. I would say inspectorColor and intensity as member names would be ideal (maybe inspectorIntensity so shader coders don't go thinking they should multiply the intensity by the attenuation ?). Afaik this should be fairly "easy" as VisibleLight does return the Light component in its light member. Then all it would take is gather that and send it to the shader in the ForwardLights. (I assume this would also entail adding these membersas properties to the shader in Input.hlsl)

    Secondary requests
    • Give the developers the ability to get a list of visible Light objects from the scene ordered the same as they are in the shaders so we can pass any amount of custom shader data (my shader doesn't go that far, but someone could one day want to use a color ramp in their light for a specific toon shading effect or maybe some lights could be used to appear differently on certain objects for puzzles, or so they could use math-based cookies based on functions like sin). Being able to do it separately for each pass might be even better ? I also assume this function would have to be used in OnPreRender to make sure the pipeline has made all necessary calculations for the current frame ?
    • Give the developers access to each pass' data as read-only. This actually automatically solves the first secondary request as we would be able to access RenderingData and all of its component and as such access to the visible lights list and so on (though a simpler solution for the first request would certainly be better as getting an already ordered list of light components that can be iterated through to send to the shader would be easier for less techie shader coders).
    Thanks in advance, I hope you will consider these requests with attention as I truly believe they can give a HUGE amount of freedom for URP shaders, which afaik is the intended pipeline for any non-photorealistic shading.

    Have a good day everyone :)
     
    Last edited: Jun 8, 2022
  4. DevilBlackDeath

    DevilBlackDeath

    Joined:
    Mar 16, 2014
    Posts:
    14
    I'm bumping the thread after more researches. I've looked into getting the needed data from either the Pipeline Asset or the Pipeline Data, unfortunately I couldn't trace any publicly accessible variable that could lead me back to getting the Rendering Data or the list of visible lights and their ordering in said list in any way shape or form.

    If anyone can help with accessing that I'd be grateful though it looks increasingly likely that without involving an official change, there's likely no way to do that. Well other than rewriting and customizing some of the URP classes (I could do that, but my main issue is I won't be able to easily keep the URP up to date, without having to likely rewrite some or all of my classes with every update).
     
  5. DevilBlackDeath

    DevilBlackDeath

    Joined:
    Mar 16, 2014
    Posts:
    14
    Phew, finally did it ! So for everyone who would need the exact feature set I was looking for (getting light color and intensity as put in the inspector instead of just a premultiplied color) here is a RendererFeature I just whipped up :

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering;
    3. using UnityEngine.Rendering.Universal;
    4. using UnityEngine.Rendering.Universal.Internal;
    5.  
    6. public class RawLightsSettings : ScriptableRendererFeature
    7. {
    8.     class RawLightsPass : ScriptableRenderPass
    9.     {
    10.         Vector4[] m_additionalLightRawColors;
    11.         float[] m_additionalLightIntensities;
    12.  
    13.         int m_additionalLightsColor = Shader.PropertyToID("_AdditionalLightsInspectorColor");
    14.         int m_additionalLightsIntensity = Shader.PropertyToID("_AdditionalLightsInspectorIntensity");
    15.  
    16.         public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
    17.         {
    18.             base.Configure(cmd, cameraTextureDescriptor);
    19.  
    20.             m_additionalLightRawColors = new Vector4[UniversalRenderPipeline.maxVisibleAdditionalLights];
    21.             m_additionalLightIntensities = new float[UniversalRenderPipeline.maxVisibleAdditionalLights];
    22.         }
    23.  
    24.         public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    25.         {
    26.             var cmd = CommandBufferPool.Get();
    27.             var lightData = renderingData.lightData;
    28.             var lights = lightData.visibleLights;
    29.  
    30.             for (int i = 0, lightIter = 0; i<lights.Length && lightIter < UniversalRenderPipeline.maxVisibleAdditionalLights; i++) {
    31.                 VisibleLight light = lights[i];
    32.  
    33.                 if (lightData.mainLightIndex != i) {
    34.                     m_additionalLightRawColors[lightIter] = light.light.color;
    35.                     m_additionalLightIntensities[lightIter] = light.light.intensity;
    36.                     lightIter++;
    37.                 }
    38.             }
    39.  
    40.             for (int i = 0; i< m_additionalLightsColor && i < lights.Length; i++)
    41.                 Debug.Log(m_additionalLightRawColors[i].ToString());
    42.  
    43.             cmd.SetGlobalVectorArray(m_additionalLightsColor, m_additionalLightRawColors);
    44.             cmd.SetGlobalFloatArray(m_additionalLightsIntensity, m_additionalLightIntensities);
    45.  
    46.             context.ExecuteCommandBuffer(cmd);
    47.             CommandBufferPool.Release(cmd);
    48.         }
    49.     }
    50.  
    51.     RawLightsPass m_ScriptablePass;
    52.  
    53.     /// <inheritdoc/>
    54.     public override void Create()
    55.     {
    56.         m_ScriptablePass = new RawLightsPass();
    57.  
    58.         // Configures where the render pass should be injected.
    59.         m_ScriptablePass.renderPassEvent = RenderPassEvent.BeforeRenderingOpaques;
    60.     }
    61.  
    62.     // Here you can inject one or multiple render passes in the renderer.
    63.     // This method is called when setting up the renderer once per-camera.
    64.     public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    65.     {
    66.         renderer.EnqueuePass(m_ScriptablePass);
    67.     }
    68. }
    69.  
    If you want to use this you have to create a script with the name "RawLightsSettings". In it, erase everything and copy the code above. Once this is done go in your Universal Renderer Pipeline data asset (called "UniversalRenderPipelineAsset_Renderer" by default) and you can click on "Add Renderer Feature" where Raw Lights Settings will be listed, add it.

    In your shader you need to declare a global Vector4 array of size MAX_VISIBLE_LIGHTS (this is a global declared by Unity so you can use that name in your shader directly) named "_AdditionalLightsInspectorColor" and a global float array of the same size named _AdditionalLightsInspectorIntensity.

    You can then use those global array in your shader to retrieve the needed values.

    I know the explanation was fairly "noobish" but I wanted it to be accessible to people who are still fairly unfamiliar with shaders, or at least put in a way that they can easily Google each task and find examples to help them.


    Now I understand how renderer features and passes work, I would say this is the best way to add what I asked in an official capacity.

    The best way would be to have a feature that returns the list visibleLights and the mainLightIndex in some sort of struct. With this devs could do whatever they want with lights. They would be able to get the real light object from visibleLights and from there retrieve any Component containing custom settings if needed.
     
    dongxiao_unity and funkyCoty like this.