Modifying PostProcessing Effect Parameters at Runtime in Unity

Published

Working on my dungeon crawler game, Dungeons of Azoya, I started adding juice.

In case you don’t know, juice is the name given to all the little effects that give life to your game. In my case, I wanted to add an effect to my Vignette Post-Processing effect: when the player gets hit, the Vignette would cover a larger part of the screen and turn red, before turning back to its base color and size. Pretty standard effect, common in FPS games.

It is actually pretty simple to get access to PP parameters, but let me start with 2 warnings:

  • I used this in Unity 2021.2.7f1, with URP 12.1.2. The process for HDRP is almost the same, but you would have to change the using directives to use HDRP instead of URP. For older versions of URP/HDRP/LWRP you might need to switch to UnityEngine.Experimental.Rendering instead. The documentation is honestly quite lacking regarding scripting access to PostProcess effects.
  • I lost quite a bit of time to a “random” problem. I use Visual Studio Code for my work, and for some reason VS Code was not able to find the assembly UnityEngine.Rendering, which contains the code related to Post Processing Volumes, and neither could Unity. Deleting Library and all csproj files didn’t solve the issue. The fix was to switch to VS and regenerate project files. This fixed the issue and I was even able to switch back to VS Code afterwards. I have seen a post on the Unity forum about someone having the exact same issue and solution so this is definitely good to know as it is not an obvious solution.

So, how do we do this? We actually have 2 solutions. The first one is to have multiple PostProcess volumes and change their weights appropriately depending on the context. Considering I only want to modify a single effect and also take a look at PP scripting, I’ll be accessing the Vignette effect and changing its intensity directly. All other PP effects and parameters should have a similar method of access and modification.

In my case, I’ll be assuming that there is a single preexisting VolumeProfile that is being used in this scene, and I want to modify Vignette intensity.

Post Process effects are actually Scriptable Objects, but you can’t set them directly as fields in a monobehaviour for some reason, so we’ll have to get the post process volume in our monobehaviour. Considering we already know there is a single one, we’ll be setting it as a public property in VignetteController, a monobehaviour dedicated to modifying the Vignette effect.

How do we get the vignette effect? The VolumeProfile class exposes a components property which returns all PP effects contained in the volume. We’ll need to iterate on them to find the one we want (in our case: mVolumeProfile.components[i].name == “Vignette”).

Once we have found the effect we can change its parameters. There is a slight problem though in that the numeric parameters of PP Effects are not just floats, but instances of ClampedFloatParameter. We can’t juste set a float to vignette.intensity, we need to set it to vignette.intensity.value. You also can’t create a new ClampedFloatParameter since this seems to break the link with the PP process (the value is updated, but there is no change in the display for some reason).

Once you have the reference to this parameter, you are then free to do as you will with it. In the code below, we get access to the Vignette effect and change its intensity to 1f in Start.

That’s all you need to modify your PP effects at runtime! I’ll be sharing a finished version of the code with a coroutine that replicates the process described in the 2nd paragraph in my Unity Simple Utils repository on github.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using UnityEngine.Rendering.Universal;
using UnityEngine.Rendering;

public class VignetteController : MonoBehaviour
{
    public VolumeProfile mVolumeProfile;
    public Vignette mVignette;

    void Start()
    {
      // get the vignette effect
      for (int i = 0; i < mVolumeProfile.components.Count; i++)
      {
        if (mVolumeProfile.components[i].name == "Vignette")
        {
            mVignette = (Vignette)mVolumeProfile.components[i];
        }
      }

      // get the intensity parameter and all other parameters to be used. 
      // PP parameters are usually an instance of ClampedParameter
      // don't create a new ClampedFloatParameter, get the reference to the existing one
      ClampedFloatParameter intensity = mVignette.intensity; 
      intensity.value = 1f;

    }

}