- Home /
How do you make Post Processing Stack Depth of Field auto-focus on an object?
How do I make it focus on whatever is the nearest object to the camera.
Answer by Twin-Stick · Oct 31, 2017 at 08:24 AM
Hey! All you need to do is have a script on the camera, get a reference to the post processing profile and calculate the distance from the camera and the target you want to focus on.
Nice and simple :) Here is something I'm using...
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.PostProcessing;
public class KeepInFocus : MonoBehaviour {
// Array of targets
public Transform[] focusTargets;
// Current target
public float focusTargetID;
// Cache profile
PostProcessingProfile postProfile;
// Adjustable aperture - used in animations within Timeline
[Range(0.1f,20f)] public float aperture;
void Start () {
// Load the post processing profile
postProfile = GetComponent<PostProcessingBehaviour>().profile;
}
void Update () {
// Get distance from camera and target
float dist = Vector3.Distance(transform.position, focusTargets[Mathf.FloorToInt(focusTargetID)].position);
// Get reference to the DoF settings
var dof = postProfile.depthOfField.settings;
// Set variables
dof.focusDistance = dist;
dof.aperture = aperture;
// Apply settings
postProfile.depthOfField.settings = dof;
}
}
Note that the image effect is computed on a plane perpendicular to the camera's direction, as opposed to radially from the camera's position. If you don't compensate for this, your focus target will be incorrect unless your target is along the optical axis.
So ins$$anonymous$$d of
float dist = Vector3.Distance(transform.position,focusTargets[$$anonymous$$athf.FloorToInt(focusTargetID)].position);
you need to map this to the correct plane:
float dist = Vector3.Dot(focusTargets[$$anonymous$$athf.FloorToInt(focusTargetID)].position-transform.position,transform.forward);
That's awesome! Thanks for the feedback @Wolfram - much appreciated!
Answer by zombience · Nov 27, 2019 at 09:08 PM
Updated for Unity 2019.2, and simplified a bit for my particular use case, also using correct plane distance comparison from @Wolfram
/// <summary>
/// this class is intended to be put on the focus object itself
/// </summary>
[ExecuteInEditMode]
public class DOFFocusOnObject : MonoBehaviour
{
[SerializeField]
Transform cam;
[SerializeField]
PostProcessVolume postProcessingVolume;
PostProcessProfile PostProfile => postProcessingVolume?.profile;
[SerializeField, Range(0.1f, 32f)]
float aperture;
[SerializeField, Range(1, 300)]
int focalLength = 20;
void Update()
{
if (PostProfile == null || cam == null) return;
float dist = Vector3.Dot(transform.position-cam.position, cam.forward);
var dof = PostProfile.GetSetting<DepthOfField>();
dof.focusDistance.value = dist;
dof.aperture.value = aperture;
dof.focalLength.value = focalLength;
}
}
Answer by mgeorgedeveloper · May 29, 2018 at 12:06 PM
It worked for a long time, but in Unity 2018.1 all runtime adjustments to DOF is ignored!
(Nope - I was actually accessing the ".profile" property from a scrip in Editor mode, causing the shared profiles to stop doing anything - i.e. when you edit a post processing profile in the Unity editor.)
Oh no! I'll have to check it out and see what I can come up with.
JPhilipp gives an example for the v2 PostProcessing in this answer: https://answers.unity.com/questions/1355103/modifying-the-new-post-processing-stack-through-co.html
Answer by Dwarf-Cavern · Jun 05, 2018 at 08:26 AM
Just a small clarification that this indeed still works, but you need to re-apply DOF Settings back to the profile after modifying them. This is what works perfectly for me for any Post Processing Model:
//create a Settings reference for DOF
DepthOfFieldModel.Settings dof = postProcessing.profile.depthOfField.settings;
//apply different DOF settings you might want to change here
dof.focusDistance = dist;
//apply settings back to the profile
postProcessing.profile.depthOfField.settings = dof;
Note that you have to already have a reference to PostProcessingBehaviour, in this case called "postProcessing" for this example to work for you. Hope that this helps someone out in the future.
But this will modify the profile asset. You can do run-time only changes with PostProcessingStack V2 by getting the effect object itself, and modifying the value of a setting:
DepthOfField depthOfFieldEffect;
profile.TryGetSettings(out depthOfFieldEffect); // will return false if profile doesn't have the effect
...
depthOfFieldEffect.focusDistance.value = distance;
If it's not desirable to change the asset itself, one can always make a run-time reference and have fun with it in a safe way, or store defaults on load and return them on unload, there are many ways to go about it, but of course it depends on individual case and needs :)
I am aware of the V2 functionality, but as far as I understand it, PostProcessingStack V2 is still in development phase. Or has that been changed?
Yes, there are many ways to not change the asset itself - I just showed the one I prefer.
Based on the number of features included in non-beta releases that are marked as "preview", I would say most of Unity is still in development (since about Unity 5), but I've seen the PostProcessingStack V2 in production in multiple projects now, and had little trouble with it.
Your answer
Follow this Question
Related Questions
How to change Focus Distance at run time? Using PostProcessing Depth of Field. 1 Answer
Depth of Field and Resolution 1 Answer
How do i get depth of field from volume? 0 Answers
Why is the Universal RP Depth of Field Focal Length value clamped between 0 and 300 in Bokeh mode? 0 Answers
Modify values on VolumeProfile URP. 1 Answer