123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using UnityEngine;
- using UnityEngine.EventSystems;
- using UnityEngine.Rendering;
- using UnityEngine.Rendering.Universal;
- namespace SicknessReduction.Visual.DoF
- {
- //TODO: look at https://catlikecoding.com/unity/tutorials/advanced-rendering/depth-of-field/ or pseudocode in paper
- public class DynamicDoF : MonoBehaviour
- {
- private const int NUMBER_OF_RAYS = 9; // Carneige, Rhee (2015)
- private const float RAY_OFFSET = 6f; //Exact value not mentioned in paper
- [Tooltip("Max Radius of Circle of Confusion in percent of display width")]
- public float maxFactorRadiusCOC = 0.0175f; //Carneige, Rhee (2015)
- [Range(0.0f, 2.0f)]
- public float pow = 0.3f;
- public float refocusTimePerMeter = 0.0000017f; //seconds; Carneige, Rhee (2015)
- public float maxFocusDistance = 30000f; //metres; Carneige, Rhee (2015
- public Camera playerCamera;
- public VolumeProfile postProcessProfile;
- private Transform cameraTransform;
- private DepthOfField doF;
- private bool doFAvailable;
- private float maxCocRadius;
- private List<float> rayDistances = new List<float>(NUMBER_OF_RAYS);
- private Vector3[] ends = new Vector3[NUMBER_OF_RAYS];
-
-
- //TODO: debug, remove
- public GameObject gizmoPrefab;
- private GameObject[] hits = new GameObject[NUMBER_OF_RAYS];
- private void Start()
- {
- cameraTransform = playerCamera.transform;
- //Debug.Log($"Screen Width = {playerCamera.pixelWidth}, scaled = {playerCamera.scaledPixelWidth}");
- //maxCocRadius = maxFactorRadiusCOC * playerCamera.scaledPixelWidth; FIXME: waaaay to small
- for (int i = 0; i < NUMBER_OF_RAYS; i++)
- {
- hits[i] = Instantiate(gizmoPrefab);
- hits[i].SetActive(false);
- } //TODO: debug, remove
- doF = (DepthOfField) postProcessProfile.components.FirstOrDefault(c => c is DepthOfField);
- doFAvailable = doF != null;
- if (doFAvailable)
- {
- // ReSharper disable once PossibleNullReferenceException
- doF.mode.value = DepthOfFieldMode.Bokeh;
- doF.focalLength.min = 0f;
- doF.focalLength.max = float.MaxValue;
- doF.aperture.max = float.MaxValue;
- doF.aperture.min = 0f;
- }
- else
- {
- Debug.LogWarning("No DepthOfField found in PostProcessing Profile!");
- }
- }
- private void Update()
- {
- if (!doFAvailable) return;
- var focusDistance = CastRays();
- if (focusDistance < 0)
- {
- focusDistance = doF.focusDistance.value;
- //return;
- }
- /*For real-time performance, we
- simply assume all users will take a static 500 ms
- to refocus from an infinite distance to a close distance
- (≈ 1 m), using the value calculated in earlier
- work13 (assuming a typical adult’s eyes). This
- translates to a linear interpolation between focal
- distances that takes ≈ 1.7*/
- doF.active = true;
- var timeNeededToRefocus = Mathf.Abs(focusDistance - doF.focusDistance.value) * refocusTimePerMeter;
- focusDistance = Mathf.Lerp(doF.focusDistance.value, focusDistance, Time.deltaTime / timeNeededToRefocus);
- doF.focusDistance.value = focusDistance;
- doF.focalLength.value = 1;
- var coc = maxFactorRadiusCOC * 1 / Mathf.Pow(focusDistance, pow);
- //var coc = maxFactorRadiusCOC * 1 - ()
- doF.aperture.value = ApertureForCocAndFocusDistance(coc, focusDistance);
- }
- private float ApertureForCocAndFocusDistance(float coc, float focusDistance) =>
- 1 / (1000 * coc * (focusDistance - 0.001f));
- private float CastRays()
- {
- var position = cameraTransform.position;
- var forward = cameraTransform.forward;
- var start = position + forward * playerCamera.nearClipPlane;
- ends[0] = position + forward * playerCamera.farClipPlane;
- ends[1] = ends[0] + cameraTransform.TransformDirection(new Vector3(RAY_OFFSET, 0, 0));
- ends[2] = ends[0] + cameraTransform.TransformDirection(new Vector3(-RAY_OFFSET, 0, 0));
- ends[3] = ends[0] + cameraTransform.TransformDirection(new Vector3(0, RAY_OFFSET, 0));
- ends[4] = ends[0] + cameraTransform.TransformDirection(new Vector3(0, -RAY_OFFSET, 0));
- ends[5] = ends[0] + cameraTransform.TransformDirection(new Vector3(RAY_OFFSET, RAY_OFFSET, 0));
- ends[6] = ends[0] + cameraTransform.TransformDirection(new Vector3(-RAY_OFFSET, RAY_OFFSET, 0));
- ends[7] = ends[0] + cameraTransform.TransformDirection(new Vector3(RAY_OFFSET, -RAY_OFFSET, 0));
- ends[8] = ends[0] + cameraTransform.TransformDirection(new Vector3(-RAY_OFFSET, -RAY_OFFSET, 0));
- rayDistances.Clear();
- for (var i = 0; i < ends.Length; i++)
- {
- var end = ends[i];
- if (Physics.Linecast(start, end, out var hit, Physics.DefaultRaycastLayers))
- {
- Debug.DrawLine(start, hit.point, Color.green);
- hits[i].transform.position = hit.point;
- hits[i].transform.localScale = Vector3.one * (hit.distance * 0.01f);
- hits[i].SetActive(true);
- //Debug.Log("DoF - Hit, Distance = " + hit.distance);
- rayDistances.Add(hit.distance);
- }
- else
- {
- hits[i].SetActive(false);
- Debug.DrawRay(position, position + forward * playerCamera.farClipPlane, Color.red);
- }
- }
- if (rayDistances.Count < 1) return -1;
- return Helpers.RemoveOutliers(rayDistances).Average();
- }
- }
- }
|