Toggle RenderFeatures or Change Renderers from script

Hi, I want to be able to toggle a render feature on my URP Renderer from inside a script, or to switch which renderer a camera is currently using from script, so I can control which effects are happening at runtime.

I saw we have UniversalAdditionalCameraData.SetRenderer(int). This would work great except I can’t see an easy way to get a list of the current renderers being used by the pipeline, so if I change my renderers on the pipline asset then SetRenderer() might be referencing a different renderer then I intended or one that doesn’t exist.

Is there a way to get a list of the renderers in the active pipeline so I can make sensible use of CameraData.SetRenderer or is there an easier way to do what I’m trying to do?

There’s no exposed way to do this atm, but it has been something a few users have asked already and we plan to add support to this considering the feedback received.

Another request has been to inject render passes by script.
I can’t give an ETA for this just yet but we plan to improve this.

3 Likes

Hi phil_lira

Are there any updates on this?

Is there a way to do this now?

Right now what I want to do is have the artist set the ScriptableRendererData in the inspector of a script

public ScriptableRendererData rendererData;

Then when entering the scene, I want to set the camera’s “Renderer” property to rendererData.

The SetRenderer function isn’t very useful at the moment, since there’s no way to get the index from the pipeline asset you’re looking for. This is all required for automatic setup of renderers, or specific cameras. Currently, you can get around all this by using reflection.

Example code

Checking if a ScriptableRenderPass is already in the render data list (requires a reference to the ScriptableRendererData in question somewhere)

BindingFlags bindings = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance;

            ScriptableRendererData[] m_rendererDataList = (ScriptableRendererData[])typeof(UniversalRenderPipelineAsset).GetField("m_RendererDataList", bindings).GetValue(UniversalRenderPipeline.asset);

            ScriptableRendererData pass = <Reference to it here>;

           bool isPresent = false;
for (int i = 0; i < m_rendererDataList.Length; i++)
            {
                if (m_rendererDataList[i] == pass) isPresent = true;
            }

return isPresent;

Adding it to the render pipeline:

private static void AddRendererToPipeline(ScriptableRendererData pass)
        {
            if (pass == null) return;

            BindingFlags bindings = System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance;

            ScriptableRendererData[] m_rendererDataList = (ScriptableRendererData[])typeof(UniversalRenderPipelineAsset).GetField("m_RendererDataList", bindings).GetValue(UniversalRenderPipeline.asset);
            List<ScriptableRendererData> rendererDataList = new List<ScriptableRendererData>();

            for (int i = 0; i < m_rendererDataList.Length; i++)
            {
                rendererDataList.Add(m_rendererDataList[i]);
            }
            rendererDataList.Add(pass);

            typeof(UniversalRenderPipelineAsset).GetField("m_RendererDataList", bindings).SetValue(UniversalRenderPipeline.asset, rendererDataList.ToArray());
        }

Applying it to a camera (also requires a reference to the ScriptableRendererData in question)

                    ScriptableRendererData[] rendererDataList = (ScriptableRendererData[])typeof(UniversalRenderPipelineAsset).GetField("m_RendererDataList", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(UniversalRenderPipeline.asset);

                    for (int i = 0; i < rendererDataList.Length; i++)
                    {
                        if (rendererDataList[i] == <your renderer>) universalAdditionalCameraData.SetRenderer(i);
                    }
1 Like

The easiest approach for now, personally, is to implement my own renderer that includes all the desired passes and is configurable from script to include and exclude passes on the fly.

2 Likes

I was also looking for this. I don’t know since when this works, but I created a behaviour like this:

using System.Collections;
using UnityEngine;
using UnityEngine.Rendering.Universal;

public class SwitchRendererFeature : MonoBehaviour
{
    public ScriptableRendererFeature feature;

    // Start is called before the first frame update
    void Start()
    {
        StartCoroutine(Blink());
    }

    IEnumerator Blink()
    {
        while(true) {
            feature.SetActive(!feature.isActive);
            yield return new WaitForSecondsRealtime(1);
        }
    }
}

I added it to a GameObject and dragged the renderer feature from the project (you can open the ForwardRenderer, see attached image) to the slot in the MonoBehaviour. Works like a charm.

9 Likes
            // Get current renderer setting's scriptable object
            var renderer = (GraphicsSettings.currentRenderPipeline as UniversalRenderPipelineAsset).GetRenderer(0);
            // Get **property** which contains list of 'rendererFeatures' from current renderer setting's scriptable object using reflection
            var property = typeof(ScriptableRenderer).GetProperty("rendererFeatures", BindingFlags.NonPublic | BindingFlags.Instance);
            // Get value from that property as List<ScriptableRendererFeature>
            List<ScriptableRendererFeature> features = property.GetValue(renderer) as List<ScriptableRendererFeature>;
            // Loop through each feature that exists in current renderer setting's scriptable object
            foreach (var feature in features)
            {
                // Find feature of type that you want (e.g. 'BlurKawaseURP')
                if (feature.GetType() == typeof(BlurKawaseURP))
                {
                     // Cast feature to access it's properties
                    (feature as BlurKawaseURP).Settings.downsample = 4;
                }
            }
2 Likes