Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Join us on March 30, 2023, between 5 am & 1 pm EST, in the Performance Profiling Dev Blitz Day 2023 - Q&A forum and Discord where you can connect with our teams behind the Memory and CPU Profilers.
    Dismiss Notice

Feature Request URP adding a RenderFeature from script

Discussion in 'Universal Render Pipeline' started by andrea_i, May 28, 2021.

  1. andrea_i

    andrea_i

    Joined:
    Nov 18, 2012
    Posts:
    32
    Hi all,

    My tool (clayxels) needs a custom RenderFeature in order to render, I couldn't find the APIs to do that, so I had to hack my way around it like so:
    Code (CSharp):
    1. UnityEngine.Rendering.Universal.ScriptableRendererData forwardPipeData = AssetDatabase.LoadAssetAtPath<UnityEngine.Rendering.Universal.ScriptableRendererData>("Assets/Settings/ForwardRenderer.asset");
    2. List<UnityEngine.Rendering.Universal.ScriptableRendererFeature> passes = forwardPipeData.rendererFeatures;
    3. MyCustomRenderFeature newRenderFeature = ScriptableObject.CreateInstance<MyCustomRenderFeature>();
    4. passes.Add(newRenderFeature);
    5.  
    6. // update the passes list with this hack
    7. MethodInfo dynMethod = forwardPipeData.GetType().GetMethod("OnValidate", BindingFlags.NonPublic | BindingFlags.Instance);
    8. dynMethod.Invoke(forwardPipeData, new object[]{});
    The main problem with this hack (aside from the hack itself) is that when deploying the exe unity will fail to use my renderFeature unless I remove it and add it manually.
    Any short term or long term solution would be super appreciated!
     
    deus0 likes this.
  2. StaggartCreations

    StaggartCreations

    Joined:
    Feb 18, 2015
    Posts:
    1,965
    I'm using a similar function, I'm assuming you're doing this in the editor, not a build? An API would be nice, having to resort to reflection is kind of a red flag :p

    Render features are ScriptableObjects, so they have to be saved to disk in order to remain persistent. This part is missing in your function, it should add the MyCustomRenderFeature instance as a sub-asset to the renderer, get a reference to it, and assign that to the list instead.

    From: https://github.com/Unity-Technologi...l/Editor/ScriptableRendererDataEditor.cs#L180
    Code (CSharp):
    1. AssetDatabase.AddObjectToAsset(newRenderFeature, forwardPipeData);
    2. AssetDatabase.TryGetGUIDAndLocalFileIdentifier(newRenderFeature , out var guid, out long localId);
    This must be called before adding it to the list, otherwise you're not adding a reference to the sub-asset, but rather the "virtual" instance, which would result in "Type mismatch" showing on the field in the inspector (debug mode). And it'll go missing after saving the project.

    If it's an editor-only function, remember to call EditorUtility.SetDirty(forwardPipeData); so things get saved when needed.

    Bonus if you want to fetch the current default renderer, rather than using a hardcoded filepath!
    Code (CSharp):
    1. private static int GetDefaultRendererIndex(UniversalRenderPipelineAsset asset)
    2. {
    3.     return (int)typeof(UniversalRenderPipelineAsset).GetField("m_DefaultRendererIndex", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(asset);
    4. }
    5.  
    6. /// <summary>
    7. /// Gets the renderer from the current pipeline asset that's marked as default
    8. /// </summary>
    9. /// <returns></returns>
    10. public static ScriptableRendererData GetDefaultRenderer()
    11. {
    12.     if (UniversalRenderPipeline.asset)
    13.     {
    14.         ScriptableRendererData[] rendererDataList = (ScriptableRendererData[])typeof(UniversalRenderPipelineAsset)
    15.                 .GetField("m_RendererDataList", BindingFlags.NonPublic | BindingFlags.Instance)
    16.                 .GetValue(UniversalRenderPipeline.asset);
    17.         int defaultRendererIndex = GetDefaultRendererIndex(UniversalRenderPipeline.asset);
    18.  
    19.         return rendererDataList[defaultRendererIndex];
    20.     }
    21.     else
    22.     {
    23.         Debug.LogError("No Universal Render Pipeline is currently active.");
    24.         return null;
    25.     }
    26. }
     
    athenspire and deus0 like this.
  3. andrea_i

    andrea_i

    Joined:
    Nov 18, 2012
    Posts:
    32
    StaggartCreations, you saved my day (and my users on URP : )!
    I'll try that straight away, it's great to know there's a workaround while we wait for URP to have these APIs exposed one day, hopefully.