DynamicDoF.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  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. [Serializable]
  11. public struct CocRadius
  12. {
  13. public float factor;
  14. public float min;
  15. public float max;
  16. }
  17. //TODO: look at https://catlikecoding.com/unity/tutorials/advanced-rendering/depth-of-field/ or pseudocode in paper
  18. public class DynamicDoF : MonoBehaviour
  19. {
  20. private const int NUMBER_OF_RAYS = 9; // Carneige, Rhee (2015)
  21. private const float RAY_OFFSET = 6f; //Exact value not mentioned in paper
  22. public CocRadius cocRadius; //Carneige, Rhee (2015)
  23. public float refocusTimePerMeter = 0.0000017f; //seconds; Carneige, Rhee (2015)
  24. public float maxFocusDistance = 30000f; //metres; Carneige, Rhee (2015
  25. [Tooltip("Offset in degrees of the ray from the center of the image (+ upwards/- downwards")]
  26. public float offsetFromCenter = 10f;
  27. public Camera playerCamera;
  28. public VolumeProfile postProcessProfile;
  29. private Transform cameraTransform;
  30. private DepthOfField doF;
  31. private bool doFAvailable;
  32. private float maxCocRadius;
  33. private List<float> rayDistances = new List<float>(NUMBER_OF_RAYS);
  34. private Vector3[] ends = new Vector3[NUMBER_OF_RAYS];
  35. //TODO: debug, remove
  36. //public GameObject gizmoPrefab;
  37. //private GameObject[] hits = new GameObject[NUMBER_OF_RAYS];
  38. private void Start()
  39. {
  40. cameraTransform = playerCamera.transform;
  41. //Debug.Log($"Screen Width = {playerCamera.pixelWidth}, scaled = {playerCamera.scaledPixelWidth}");
  42. //maxCocRadius = maxFactorRadiusCOC * playerCamera.scaledPixelWidth; FIXME: waaaay to small
  43. /*for (int i = 0; i < NUMBER_OF_RAYS; i++)
  44. {
  45. hits[i] = Instantiate(gizmoPrefab);
  46. hits[i].SetActive(false);
  47. } *///TODO: debug, remove
  48. doF = (DepthOfField) postProcessProfile.components.FirstOrDefault(c => c is DepthOfField);
  49. doFAvailable = doF != null;
  50. if (doFAvailable)
  51. {
  52. // ReSharper disable once PossibleNullReferenceException
  53. doF.mode.value = DepthOfFieldMode.Bokeh;
  54. doF.focalLength.min = 0f;
  55. doF.focalLength.max = float.MaxValue;
  56. doF.aperture.max = float.MaxValue;
  57. doF.aperture.min = 0f;
  58. }
  59. else
  60. {
  61. Debug.LogWarning("No DepthOfField found in PostProcessing Profile!");
  62. }
  63. }
  64. private void Update()
  65. {
  66. if (!doFAvailable) return;
  67. var focusDistance = CastRays();
  68. if (focusDistance < 0)
  69. {
  70. focusDistance = doF.focusDistance.value;
  71. //return;
  72. }
  73. /*For real-time performance, we
  74. simply assume all users will take a static 500 ms
  75. to refocus from an infinite distance to a close distance
  76. (≈ 1 m), using the value calculated in earlier
  77. work13 (assuming a typical adult’s eyes). This
  78. translates to a linear interpolation between focal
  79. distances that takes ≈ 1.7*/
  80. doF.active = true;
  81. var timeNeededToRefocus = Mathf.Abs(focusDistance - doF.focusDistance.value) * refocusTimePerMeter;
  82. focusDistance = Mathf.Lerp(doF.focusDistance.value, focusDistance, Time.deltaTime / timeNeededToRefocus);
  83. doF.focusDistance.value = focusDistance;
  84. doF.focalLength.value = 1;
  85. //var coc = maxFactorRadiusCOC * 1 / Mathf.Pow(focusDistance, pow);
  86. var coc = Mathf.Clamp(cocRadius.factor * (-Mathf.Log(focusDistance) + 1.9f * Mathf.Exp(1)), cocRadius.min, cocRadius.max);
  87. //Debug.Log($"COC = {coc}, focusDistance = {focusDistance}");
  88. //var coc = maxFactorRadiusCOC * 1 - ()
  89. doF.aperture.value = ApertureForCocAndFocusDistance(coc, focusDistance);
  90. }
  91. private float ApertureForCocAndFocusDistance(float coc, float focusDistance) =>
  92. 1 / (1000 * coc * (focusDistance - 0.001f));
  93. private float CastRays()
  94. {
  95. var position = cameraTransform.position;
  96. var forward = cameraTransform.forward;
  97. var up = cameraTransform.up;
  98. var adjustedForward = Vector3.RotateTowards(forward, up, offsetFromCenter * Mathf.Deg2Rad, 0f);
  99. var start = position + forward * playerCamera.nearClipPlane;
  100. ends[0] = position + adjustedForward * playerCamera.farClipPlane;
  101. ends[1] = ends[0] + cameraTransform.TransformDirection(new Vector3(RAY_OFFSET, 0, 0));
  102. ends[2] = ends[0] + cameraTransform.TransformDirection(new Vector3(-RAY_OFFSET, 0, 0));
  103. ends[3] = ends[0] + cameraTransform.TransformDirection(new Vector3(0, RAY_OFFSET, 0));
  104. ends[4] = ends[0] + cameraTransform.TransformDirection(new Vector3(0, -RAY_OFFSET, 0));
  105. ends[5] = ends[0] + cameraTransform.TransformDirection(new Vector3(RAY_OFFSET, RAY_OFFSET, 0));
  106. ends[6] = ends[0] + cameraTransform.TransformDirection(new Vector3(-RAY_OFFSET, RAY_OFFSET, 0));
  107. ends[7] = ends[0] + cameraTransform.TransformDirection(new Vector3(RAY_OFFSET, -RAY_OFFSET, 0));
  108. ends[8] = ends[0] + cameraTransform.TransformDirection(new Vector3(-RAY_OFFSET, -RAY_OFFSET, 0));
  109. rayDistances.Clear();
  110. for (var i = 0; i < ends.Length; i++)
  111. {
  112. var end = ends[i];
  113. if (Physics.Linecast(start, end, out var hit, Physics.DefaultRaycastLayers))
  114. {
  115. /*Debug.DrawLine(start, hit.point, Color.green);
  116. hits[i].transform.position = hit.point;
  117. hits[i].transform.localScale = Vector3.one * (hit.distance * 0.01f);
  118. hits[i].SetActive(true);*/
  119. //Debug.Log("DoF - Hit, Distance = " + hit.distance);
  120. rayDistances.Add(hit.distance);
  121. }
  122. /*else
  123. {
  124. hits[i].SetActive(false);
  125. Debug.DrawRay(position, position + forward * playerCamera.farClipPlane, Color.red);
  126. }*/
  127. }
  128. if (rayDistances.Count < 1) return -1;
  129. return Helpers.RemoveOutliers(rayDistances).Average();
  130. }
  131. }
  132. }