Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice

Turn URP SSAO on and off at runtime

Discussion in 'Universal Render Pipeline' started by Volcore, Mar 1, 2021.

  1. Volcore

    Volcore

    Joined:
    Jan 14, 2020
    Posts:
    24
    Hi,

    I've recently stumbled upon the new SSAO feature in URP. It would be perfect for our game, where we have a 3D menu which needs high quality, high resolution rendering and shadowing, and the combat part, where performance is everything.

    Hence, I'd like to be able to control the SSAO at runtime. Turn it on in certain scenes where visual quality is important, and then turn it off in general (ideally at no performance penalty).

    I've been trying to figure out how we can achieve this with the new SSAO feature. The implementation itself is hidden (internal class), and there doesn't seem to be an official way to modify the renderer features.

    All I really need is to be able to check and uncheck this little box at runtime:

    upload_2021-3-1_15-33-0.png

    Is that currently supported? Is it a good idea, or am I missing something fundamental here?

    We're using Unity 2020.5 and URP 10.3.1.

    Thanks!
     
  2. Sky77

    Sky77

    Joined:
    Jan 30, 2014
    Posts:
    171
    Good luck. We tried but we had no luck in figuring this out. We were able to disable some rendered features but not the SSAO effect.

    Dealing with Renderer features at runtime in generale is very cumbersome.

    You can easily swap render assets, but it’s not an ideal way to do it.
     
  3. hibbygames

    hibbygames

    Joined:
    Mar 19, 2015
    Posts:
    50
    Make a script with using UnityEngine.Rendering.Universal and public ScriptableRendererFeature ssao;

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.Rendering.Universal;
    5.  
    6. public class controlSSAO : MonoBehaviour
    7. {
    8.  
    9.     public ScriptableRendererFeature ssao;
    10.  
    11.     public void toggleSSAO(bool _bool)
    12.     {
    13.         ssao.SetActive(_bool);
    14.     }
    15. }
    Attach your SSAO thing to it in the inspector, it's inside the forward renderer
    ssoa.jpg
    then just turn it on or off with ssao.SetActive(true); or ssao.SetActive(false)
     
  4. SparrowGS

    SparrowGS

    Joined:
    Apr 6, 2017
    Posts:
    2,536
    Hey, I've tried doing so and I can't assign the renderer feature as an independent object, it's just a value inside the ForwardRendererData class.

    I ended up doing it like so:
    Code (CSharp):
    1. public ForwardRendererData renderer;
    2.  
    3. public void SetSSAO(bool state) {
    4.  
    5.     renderer.rendererFeatures[0].SetActive(state);
    6. }
    Unity 2020.3.26
     
    tmonestudio likes this.
  5. Corvostudio

    Corvostudio

    Joined:
    Jul 19, 2015
    Posts:
    14
    Hello, in our project we are able to disable the SSAO just like SparrowGS describes, however on the final build when at runtime, disabling the AO leaves a weird lighting in the scene basically everything gets very dark. This doesn't happen if the SSAO is not present in the render features when starting up the build, but it happens only when starting up a build with the SSAO present in render features, and then disabling it with renderfeatures[x].SetActive(false). Also the bug never happen in the editor.

    We are using Unity 2021.3.3LTS
     
    kminko and jolix like this.
  6. funkyCoty

    funkyCoty

    Joined:
    May 22, 2018
    Posts:
    738
    It's likely because of shader stripping. Try disabling URP's post process shader stripping. Your build times will increase, but SSAO being on/off shouldnt be stripped as a result.
     
    _geo__ likes this.
  7. RayanAziz11

    RayanAziz11

    Joined:
    Aug 4, 2021
    Posts:
    5
    Thanks, it helped me :)
     
  8. jeffomatic

    jeffomatic

    Joined:
    Jun 24, 2021
    Posts:
    15
    We're also experiencing severe global darkening when SSAO is disabled (2021.3.6f1). Unfortunately, changing the post-process shader stripping per funkyCoty's suggestion above doesn't seem to have any effect.

    There is another thread in this forum that also mentions a darkening effect when disabling SSAO, but as of this writing, there's no solution there either.

    UPDATE: We ended up making two renderer assets, one with the SSAO feature, and one that omits the SSAO feature. We used QualitySettings.SetQualityLevel() to switch between the two. On our first attempt, we copied the renderer asset and disabled the SSAO feature without removing it. This caused the darkening described above, which was resolved when we removed the feature entirely. Changing shader stripping settings didn't have any influence in our case.
     
    Last edited: Aug 8, 2022
    fra3point and SparrowGS like this.
  9. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,367
    To shed some of light on this I have tried turning SSAO off in URP with various levels of shader stripping. While turning off stripping "unused post processing shaders" helped somewhat with the colors it did (in my example) not help with shadows and other problems.

    Turning stripping off on all shaders (except for debug) solved the issue (though it comes at a cost).

    upload_2022-11-25_15-32-50.png

    I have also tried setting the "Intensity" of the SSAO render feature to zero (0f) at runtime. Sadly that just had the same result as turning it off (active = false). Usually that would be a good thing as the shader should not execute SSAO if not needed but in the case of missing shader variants due to stripping it is a disadvantage.

    If you are not looking for performance and only want to disable the SSAO due to aesthetic reasons then setting the "Intensity" to something close to zero works (I used 0.001f). Caveat: you still pay the performance cost.
     
    Last edited: Nov 25, 2022
    PraveenBalu likes this.
  10. fra3point

    fra3point

    Joined:
    Aug 20, 2012
    Posts:
    269
    Thank you! This workaround was the only one that worked in my case.

    I hope Unity fixes this in the near future... Meanwhile, I'll submit a bug report.
     
  11. Kronnect

    Kronnect

    Joined:
    Nov 16, 2014
    Posts:
    2,910
    SSAO should be linked to the volume system, otherwise it gets applied to all cameras which is not optimal in many cases.
     
    bluescrn likes this.
  12. SteenPetersen

    SteenPetersen

    Joined:
    Mar 13, 2016
    Posts:
    110
    The script posted by @SparrowGS is now deprecated in Unity 2021.3.22f1 I used this and it worked:


    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering.Universal;
    3.  
    4. namespace UI
    5. {
    6.     public class SSAOManager : MonoBehaviour
    7.     {
    8.         public UniversalRendererData renderer;
    9.         public void ToggleSSAO(bool state) {
    10.             renderer.rendererFeatures[0].SetActive(state);
    11.         }
    12.  
    13.     }
    14. }
     
  13. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,629
    I use the following code that works with Unity 2022.3 and URP 14.0.7 at the time of writing:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.Rendering.Universal;
    5. using System.Reflection;
    6.  
    7. public static class URPUtility
    8. {
    9.     static Dictionary<System.Type, Dictionary<string, FieldInfo>> s_FieldLUT = new Dictionary<System.Type, Dictionary<string, FieldInfo>>();
    10.     static Dictionary<System.Type, string> s_TypeNameLUT = new Dictionary<System.Type, string>();
    11.  
    12.     public static ScriptableRendererFeature ssao
    13.     {
    14.         get
    15.         {
    16.             const string kTypeName = "ScreenSpaceAmbientOcclusion";
    17.             return GetRendererFeature(kTypeName);
    18.         }
    19.     }
    20.  
    21.     public static bool ssaoEnabled
    22.     {
    23.         get
    24.         {
    25.             var v = ssao;
    26.             if (v != null)
    27.                 return v.isActive;
    28.             return false;
    29.         }
    30.         set
    31.         {
    32.             var v = ssao;
    33.             if (v != null)
    34.                 v.SetActive(value);
    35.         }
    36.     }
    37.  
    38.     public static float ssaoDirectLightingStrength
    39.     {
    40.         get => GetSettingsFloat(ssao, "m_Settings", "DirectLightingStrength");
    41.         set => SetSettingsFloat(ssao, "m_Settings", "DirectLightingStrength", value);
    42.     }
    43.  
    44.     public static float ssaoRadius
    45.     {
    46.         get => GetSettingsFloat(ssao, "m_Settings", "Radius");
    47.         set => SetSettingsFloat(ssao, "m_Settings", "Radius", value);
    48.     }
    49.  
    50.     public static float ssaoIntensity
    51.     {
    52.         get => GetSettingsFloat(ssao, "m_Settings", "Intensity");
    53.         set => SetSettingsFloat(ssao, "m_Settings", "Intensity", value);
    54.     }
    55.  
    56.     public static ScriptableRendererFeature decals
    57.     {
    58.         get
    59.         {
    60.             const string kTypeName = "DecalRendererFeature";
    61.             return GetRendererFeature(kTypeName);
    62.         }
    63.     }
    64.  
    65.     public static float decalsMaxDrawDistance
    66.     {
    67.         get => GetSettingsFloat(decals, "m_Settings", "maxDrawDistance");
    68.         set => SetSettingsFloat(decals, "m_Settings", "maxDrawDistance", value);
    69.     }
    70.  
    71.     static float GetSettingsFloat(ScriptableRendererFeature feature, string settingsName, string memberName)
    72.     {
    73.         if (feature == null)
    74.             return 0;
    75.  
    76.         var settingsMember = GetField(feature.GetType(), settingsName);
    77.         var settings = settingsMember.GetValue(feature);
    78.  
    79.         var directLightingStrengthMember = GetField(settings.GetType(), memberName);
    80.         return (float)directLightingStrengthMember.GetValue(settings);
    81.     }
    82.  
    83.     static void SetSettingsFloat(ScriptableRendererFeature feature, string settingsName, string memberName, float value)
    84.     {
    85.         if (feature == null)
    86.             return;
    87.  
    88.         var settingsMember = GetField(feature.GetType(), settingsName);
    89.         var settings = settingsMember.GetValue(feature);
    90.  
    91.         var directLightingStrengthMember = GetField(settings.GetType(), memberName);
    92.         directLightingStrengthMember.SetValue(settings, value);
    93.     }
    94.  
    95.     static FieldInfo GetField(System.Type type, string fieldName)
    96.     {
    97.         if (!s_FieldLUT.TryGetValue(type, out var fields))
    98.             s_FieldLUT[type] = fields = new Dictionary<string, FieldInfo>();
    99.  
    100.         if (!fields.TryGetValue(fieldName, out var field))
    101.             fields[fieldName] = field = type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
    102.  
    103.         return field;
    104.     }
    105.  
    106.     public static ScriptableRendererFeature GetRendererFeature(string typeName)
    107.     {
    108.         var asset = UniversalRenderPipeline.asset;
    109.         var type = asset.GetType();
    110.         var fieldInfo = GetField(type, "m_RendererDataList");
    111.         var renderDatas = (ScriptableRendererData[])fieldInfo.GetValue(asset);
    112.         if (renderDatas == null)
    113.             return null;
    114.  
    115.         foreach (var renderData in renderDatas)
    116.         {
    117.             foreach (var rendererFeature in renderData.rendererFeatures)
    118.             {
    119.                 if (rendererFeature == null)
    120.                     continue;
    121.  
    122.                 var featureType = rendererFeature.GetType();
    123.                 if (!s_TypeNameLUT.TryGetValue(featureType, out var name))
    124.                     s_TypeNameLUT[featureType] = name = featureType.Name;
    125.  
    126.                 if (name == typeName)
    127.                     return rendererFeature;
    128.             }
    129.         }
    130.  
    131.         return null;
    132.     }
    133.  
    134.     [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.AfterAssembliesLoaded)]
    135.     static void RuntimeInitializeOnLoadMethod()
    136.     {
    137.         s_FieldLUT = new Dictionary<System.Type, Dictionary<string, FieldInfo>>();
    138.         s_TypeNameLUT = new Dictionary<System.Type, string>();
    139.     }
    140. }
    141.  
    I'm aware that it's a lot of ugly reflection code. I hope Unity will expose a proper API at some point. In order to turn on/off SSAO you'd do:
    Code (CSharp):
    1. URPUtility.ssaoEnabled = true; // or false
     
    tukyo and Enabang like this.
  14. MAN_Schubert

    MAN_Schubert

    Joined:
    Mar 12, 2020
    Posts:
    8
    Hey Guys. Is it possible that we can modify the Radius + Intensity in Runtime too?

    We habe severe different Camera Distances in our project (cinemachine). Close up to a Person up to far away to see a city.

    What we would like to do is add a Script to a Gameobject, that in Runtime Triggers or Translates to different "Intensity/Radius" values, so we can blend between them.
     
  15. Kronnect

    Kronnect

    Joined:
    Nov 16, 2014
    Posts:
    2,910
    URP SSAO could (should?) be customized using the post processing volume system. That would make it easier to enable/disable or adjust its options according to the specific scene.
     
  16. Roggi_

    Roggi_

    Joined:
    May 14, 2014
    Posts:
    84
    Exactly. Who had the brilliant idea to move the screen space efect from a volume into a render feature?
     
    wwWwwwW1 likes this.
  17. two7two

    two7two

    Joined:
    Aug 3, 2023
    Posts:
    9
    hi, has anyone tried to disable SSAO in certain materials/shaders? I want my buildings and characters to leave SSAO on the ground, but at the same time not to take over.
     
  18. Alexsandro-Natsume

    Alexsandro-Natsume

    Joined:
    Apr 20, 2015
    Posts:
    24
    the guy who made SSAO as a render feature must have some brain damage or something ...
     
  19. funkyCoty

    funkyCoty

    Joined:
    May 22, 2018
    Posts:
    738
    Making it a RenderFeature is fine.. making all the fields "private" or "protected" is pure evil.
     
  20. Alexsandro-Natsume

    Alexsandro-Natsume

    Joined:
    Apr 20, 2015
    Posts:
    24
    this shows that the guys behind unity never made a game ...
     
  21. _geo__

    _geo__

    Joined:
    Feb 26, 2014
    Posts:
    1,367
    To me it shows a shift in how Unity approaches their API design in general. It's very noticable with UI Toolkit for example. Their default state is to make everything private and only release very few parts of the API to keep the "API surface", which they then have to support officially, small. Iirc some Unity employees even confirmed that this is their guiding principle now. From a nitpicker manager perspective this makes sense (less support = less costs).

    In reality this stifles innovation. We developers can not give proper feedback on an API that is not publicly available and it's tiresome to call out all the APIs that should be public in hopes of Unity "fixing" them. Especially if it would be immediately obvious that this API should be public if anyone within Unity ever cared to make even the smallest of sample projects with it. Unity seems not interested in improving at that front. Ending development of the Gigaya showed that the nitpicky managers won (this project has no direct ROI -> kill it).

    /Rant