DynamicDoF.cs 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using UnityEngine;
  5. using UnityEngine.EventSystems;
  6. using UnityEngine.Rendering;
  7. using UnityEngine.Rendering.Universal;
  8. namespace SicknessReduction.Visual.DoF
  9. {
  10. //TODO: look at https://catlikecoding.com/unity/tutorials/advanced-rendering/depth-of-field/ or pseudocode in paper
  11. public class DynamicDoF : MonoBehaviour
  12. {
  13. private const int NUMBER_OF_RAYS = 9; // Carneige, Rhee (2015)
  14. private const float RAY_OFFSET = 6f; //Exact value not mentioned in paper
  15. [Tooltip("Max Radius of Circle of Confusion in percent of display width")]
  16. public float maxFactorRadiusCOC = 0.0175f; //Carneige, Rhee (2015)
  17. public float refocusTimePerMeter = 0.0000017f; //seconds; Carneige, Rhee (2015)
  18. public float maxFocusDistance = 30000f; //metres; Carneige, Rhee (2015
  19. public Camera playerCamera;
  20. public VolumeProfile postProcessProfile;
  21. private Transform cameraTransform;
  22. private DepthOfField doF;
  23. private bool doFAvailable;
  24. private List<float> rayDistances = new List<float>(NUMBER_OF_RAYS);
  25. private void Start()
  26. {
  27. cameraTransform = playerCamera.transform;
  28. doF = (DepthOfField) postProcessProfile.components.FirstOrDefault(c => c is DepthOfField);
  29. doFAvailable = doF != null;
  30. if (doFAvailable)
  31. {
  32. // ReSharper disable once PossibleNullReferenceException
  33. doF.mode.value = DepthOfFieldMode.Bokeh;
  34. doF.focalLength.min = 0f;
  35. doF.focalLength.max = float.MaxValue;
  36. doF.aperture.max = float.MaxValue;
  37. doF.aperture.min = 0f;
  38. }
  39. else
  40. {
  41. Debug.LogWarning("No DepthOfField found in PostProcessing Profile!");
  42. }
  43. }
  44. private void Update()
  45. {
  46. if (!doFAvailable) return;
  47. var focusDistance = CastRays();
  48. if (focusDistance < 0)
  49. {
  50. doF.active = false;
  51. return;
  52. }
  53. /*For real-time performance, we
  54. simply assume all users will take a static 500 ms
  55. to refocus from an infinite distance to a close distance
  56. (≈ 1 m), using the value calculated in earlier
  57. work13 (assuming a typical adult’s eyes). This
  58. translates to a linear interpolation between focal
  59. distances that takes ≈ 1.7*/
  60. doF.active = true;
  61. var timeNeededToRefocus = Mathf.Abs(focusDistance - doF.focusDistance.value) * refocusTimePerMeter;
  62. focusDistance = Mathf.Lerp(doF.focusDistance.value, focusDistance, Time.deltaTime / timeNeededToRefocus);
  63. doF.focusDistance.value = focusDistance;
  64. doF.focalLength.value = 1;
  65. doF.aperture.value = ApertureForCocAndFocusDistance(maxFactorRadiusCOC, focusDistance);
  66. }
  67. private float ApertureForCocAndFocusDistance(float coc, float focusDistance) =>
  68. 1 / (1000 * coc * (focusDistance - 0.001f));
  69. private float CastRays()
  70. {
  71. var position = cameraTransform.position;
  72. var forward = cameraTransform.forward;
  73. var start = position + forward * playerCamera.nearClipPlane;
  74. var ends = new Vector3[NUMBER_OF_RAYS];
  75. ends[0] = position + forward * playerCamera.farClipPlane;
  76. ends[1] = ends[0] + cameraTransform.TransformDirection(new Vector3(RAY_OFFSET, 0, 0));
  77. ends[2] = ends[0] + cameraTransform.TransformDirection(new Vector3(-RAY_OFFSET, 0, 0));
  78. ends[3] = ends[0] + cameraTransform.TransformDirection(new Vector3(0, RAY_OFFSET, 0));
  79. ends[4] = ends[0] + cameraTransform.TransformDirection(new Vector3(0, -RAY_OFFSET, 0));
  80. ends[5] = ends[0] + cameraTransform.TransformDirection(new Vector3(RAY_OFFSET, RAY_OFFSET, 0));
  81. ends[6] = ends[0] + cameraTransform.TransformDirection(new Vector3(-RAY_OFFSET, RAY_OFFSET, 0));
  82. ends[7] = ends[0] + cameraTransform.TransformDirection(new Vector3(RAY_OFFSET, -RAY_OFFSET, 0));
  83. ends[8] = ends[0] + cameraTransform.TransformDirection(new Vector3(-RAY_OFFSET, -RAY_OFFSET, 0));
  84. rayDistances.Clear();
  85. foreach (var end in ends)
  86. {
  87. if (Physics.Linecast(start, end, out var hit, Physics.DefaultRaycastLayers))
  88. {
  89. Debug.DrawLine(start, (end - start).normalized * hit.distance, Color.green);
  90. //Debug.Log("DoF - Hit, Distance = " + hit.distance);
  91. rayDistances.Add(hit.distance);
  92. }
  93. else
  94. {
  95. Debug.DrawRay(position, position + forward * playerCamera.farClipPlane, Color.red);
  96. }
  97. }
  98. if (rayDistances.Count < 1) return -1;
  99. return Helpers.RemoveOutliers(rayDistances).Average();
  100. }
  101. }
  102. }