Skip to content

Instantly share code, notes, and snippets.

@michaelybecker
Last active October 16, 2021 22:01
Show Gist options
  • Save michaelybecker/dd13f558b5bb0ff64d13f3e8f0678dc5 to your computer and use it in GitHub Desktop.
Save michaelybecker/dd13f558b5bb0ff64d13f3e8f0678dc5 to your computer and use it in GitHub Desktop.
DoF AutoFocus for Unity's new postprocessing stack
// An adaptaion of Frank Otto's AutoFocus script for Unity's new PostProcessing Stack
// Original at: http://wiki.unity3d.com/index.php?title=DoFAutoFocus
// Adapted by Michael Hazani
// For more info see: http://www.michaelhazani.com/autofocus-on-whats-important
using UnityEngine;
using System.Collections;
using System;
using UnityEngine.PostProcessing;
[RequireComponent(typeof(Camera))]
public class StackDoFAutoFocus : MonoBehaviour
{
private GameObject doFFocusTarget;
private Vector3 lastDoFPoint;
private PostProcessingProfile m_Profile;
public DoFAFocusQuality focusQuality = StackDoFAutoFocus.DoFAFocusQuality.NORMAL;
public LayerMask hitLayer = 1;
public float maxDistance = 100.0f;
public bool interpolateFocus = false;
public float interpolationTime = 0.7f;
public enum DoFAFocusQuality
{
NORMAL,
HIGH
}
void Start()
{
doFFocusTarget = new GameObject("DoFFocusTarget");
var behaviour = GetComponent<PostProcessingBehaviour>();
m_Profile = behaviour.profile;
}
void Update()
{
// switch between Modes Test Focus every Frame
if (focusQuality == StackDoFAutoFocus.DoFAFocusQuality.HIGH)
{
Focus();
}
}
void FixedUpdate()
{
// switch between modes Test Focus like the Physicsupdate
if (focusQuality == StackDoFAutoFocus.DoFAFocusQuality.NORMAL)
{
Focus();
}
}
IEnumerator InterpolateFocus(Vector3 targetPosition)
{
Vector3 start = this.doFFocusTarget.transform.position;
Vector3 end = targetPosition;
float dTime = 0;
// Debug.DrawLine(start, end, Color.green);
var depthOfField = m_Profile.depthOfField.settings;
while (dTime < 1)
{
yield return new WaitForEndOfFrame();
dTime += Time.deltaTime / this.interpolationTime;
this.doFFocusTarget.transform.position = Vector3.Lerp(start, end, dTime);
depthOfField.focusDistance = Vector3.Distance(doFFocusTarget.transform.position, transform.position);
m_Profile.depthOfField.settings = depthOfField;
}
this.doFFocusTarget.transform.position = end;
}
void Focus()
{
// our ray
Ray ray = transform.GetComponent<Camera>().ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, this.maxDistance, this.hitLayer))
{
Debug.DrawLine(ray.origin, hit.point);
// do we have a new point?
if (this.lastDoFPoint == hit.point)
{
return;
// No, do nothing
}
else if (this.interpolateFocus)
{ // Do we interpolate from last point to the new Focus Point ?
// stop the Coroutine
StopCoroutine("InterpolateFocus");
// start new Coroutine
StartCoroutine(InterpolateFocus(hit.point));
}
else
{
this.doFFocusTarget.transform.position = hit.point;
var depthOfField = m_Profile.depthOfField.settings;
depthOfField.focusDistance = Vector3.Distance(doFFocusTarget.transform.position, transform.position);
// print(depthOfField.focusDistance);
m_Profile.depthOfField.settings = depthOfField;
}
// asign the last hit
this.lastDoFPoint = hit.point;
}
}
}
@petru23
Copy link

petru23 commented Sep 19, 2017

I need to ask a question: using UnityEngine.PostProcessing is not recognized so what could be a substitute?

@abrad1212
Copy link

@petru23 Do you have the post processing stack installed from Unity?

@rost1975
Copy link

This is great, but works for interiors only.
For outdoor distant autofocus you need to place a big quad with renderer=false in front of your camera, as a child, to catch the raycast.
Or edit the code to track the situation where hit.point is not obtained, and set the focus distance to some const (say, 100-200m).
Also interpolation jumps in some situations, maybe it's better not to stop the coroutine, but constantly interpolate to hit.point.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment