Basically, I’m trying to change the values of the SSAO render feature at runtime (Intensity, radius, etc).
I’ve been looking for a solution for the past few hours and can’t find anything on this.
When trying to do something like this, as it would be with other render features: var aafeature = rendererData.rendererFeatures.OfType<ScreenSpaceAmbientOcclusion>().FirstOrDefault();
I get an error saying that ScreenSpaceAmbientOcclusion is inaccessible due to its protection level.
This seems like it should be a relatively simple thing, but it seems like Unity just doesn’t want us to…? Lol.
In any case, if anyone has any ideas, I would really appreciate the advice! Thanks!
This weekend I’ll write a sample. Basically, you need to change fields in ScreenSpaceAmbientOcclusionSettings. So use reflection to get the ScreenSpaceAmbientOcclusion object from rendererFeatures, then get the object from the m_Settings field, and then change field values.
At some point, it’d be easier just to modify the SRP to make it not internal. But really, you shouldn’t have to do that, Unity should not make things like that internal since it’s exposed in the editor.
I’m also interested in how this is done through reflection. I know I need to access the internal class ScreenSpaceAmbientOcclusionSettings… but I just can’t get to it.
I expected renderFeature*.GetType().GetProperties(BindingFlags.NonPublic) to return some kind of array with my settings, but it returns an empty list (of PropertyInfos). Any help appreciated.*
I know this might not be helpful to your situation, but I ended up purchasing HBAO Horizon Based Ambient Occlusion from the asset store. Much better results, faster, and easily scriptable.
Sorry it took so long to get back. did a quick mockup here:
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEngine.Rendering.Universal;
class SsaoConfigurator
{
private readonly object _ssaoSettings;
private readonly FieldInfo _fRadius;
public SsaoConfigurator()
{
static ScriptableRendererFeature findRenderFeature(Type type)
{
FieldInfo field = reflectField(typeof(ScriptableRenderer), "m_RendererFeatures");
ScriptableRenderer renderer = UniversalRenderPipeline.asset.scriptableRenderer;
var list = (List<ScriptableRendererFeature>) field.GetValue(renderer);
foreach(ScriptableRendererFeature feature in list)
if(feature.GetType() == type)
return feature;
throw new Exception($"Could not find instance of {type.AssemblyQualifiedName} in the renderer features list");
}
static FieldInfo reflectField(Type type, string name) =>
type.GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) ??
throw new Exception($"Could not reflect field [{type.AssemblyQualifiedName}].{name}");
Type tSsaoFeature = Type.GetType("UnityEngine.Rendering.Universal.ScreenSpaceAmbientOcclusion, Unity.RenderPipelines.Universal.Runtime", true);
FieldInfo fSettings = reflectField(tSsaoFeature, "m_Settings");
ScriptableRendererFeature ssaoFeature = findRenderFeature(tSsaoFeature);
_ssaoSettings = fSettings.GetValue(ssaoFeature) ?? throw new Exception("ssaoFeature.m_Settings was null");
_fRadius = reflectField(_ssaoSettings.GetType(), "Radius");
}
public float radius
{
get => (float) _fRadius.GetValue(_ssaoSettings);
set => _fRadius.SetValue(_ssaoSettings, value);
}
}
This allocates a bit on every frame it’s used because of the float boxing, but don’t think that’s avoidable. I don’t know the performance costs, but it shouldn’t be toooo much since the field handle is cached. Still jumping through about 25 hoops to get access to something that Unity should provide by default.
My tests were far from scientific, but my scenes don’t have that many objects in them, just a few high-poly meshes. The fps were just a little higher in HBAO. It also ended up being more convenient to use overall (as a Post Effect, not just a Render Feature).
The SsaoConfigurator I put above is a standalone class. If you want it in a MonoBehaviour, change line 13 to private void Awake() and change the fields to not readonly.