ReflectionScreenSpaceTrace.azsli 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <scenesrg_all.srgi>
  9. #include <viewsrg_all.srgi>
  10. #include <Atom/RPI/Math.azsli>
  11. float DistanceSqr(float2 v1, float2 v2)
  12. {
  13. v1 -= v2;
  14. return dot(v1, v1);
  15. }
  16. //
  17. // This is a modified version of the following work:
  18. // http://jcgt.org/published/0003/04/04/paper.pdf
  19. //
  20. // Copyright (c) 2014, Morgan McGuire and Michael Mara
  21. // All rights reserved.
  22. //
  23. // Released as open source under the BSD 2-Clause License
  24. // http://opensource.org/licenses/BSD-2-Clause
  25. //
  26. // 1. Redistributions of source code must retain the above copyright notice, this list of conditions
  27. // and the following disclaimer.
  28. //
  29. // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
  30. // and the following disclaimer in the documentation and/or other materials provided with the distribution.
  31. //
  32. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
  33. // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
  34. // PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
  35. // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
  36. // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  37. // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  38. // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  39. // POSSIBILITY OF SUCH DAMAGE.
  40. //
  41. // See http://kode80.com/blog/2015/03/11/screen-space-reflections-in-unity-5/index.html for additional
  42. // extensions and optimizations.
  43. //
  44. static const uint MaxSteps = 96;
  45. static const uint BinarySearchSteps = 16;
  46. bool TraceRayScreenSpace(float3 rayStartVS, float3 rayDirectionVS, uint2 dimensions, out float2 hitCoords)
  47. {
  48. float3 rayEndVS = rayStartVS + rayDirectionVS * PassSrg::m_maxRayDistance;
  49. // early out if endpoint is behind camera
  50. if (rayEndVS.z >= 0.0f)
  51. {
  52. return false;
  53. }
  54. // project into homogeneous clip space
  55. float4 H0 = mul(ViewSrg::m_projectionMatrix, float4(rayStartVS, 1.0f));
  56. float4 H1 = mul(ViewSrg::m_projectionMatrix, float4(rayEndVS, 1.0f));
  57. float k0 = 1.0f / H0.w;
  58. float k1 = 1.0f / H1.w;
  59. // convert to screen-space endpoints
  60. float2 P0 = H0.xy * k0 * 0.5f + 0.5f;
  61. P0 = float2(P0.x * dimensions.x, (1.0f - P0.y) * dimensions.y);
  62. float2 P1 = H1.xy * k1 * 0.5f + 0.5f;
  63. P1 = float2(P1.x * dimensions.x, (1.0f - P1.y) * dimensions.y);
  64. // the interpolated homogeneous version of the viewspace points
  65. float3 Q0 = rayStartVS * k0;
  66. float3 Q1 = rayEndVS * k1;
  67. // if the line is degenerate, make it cover at least one pixel
  68. #ifdef PRE_HLSL_2021
  69. // See /o3de/Gems/Atom/Feature/Common/Assets/ShaderLib/PRE_HLSL_2021.md for details.
  70. P1 += DistanceSqr(P0, P1) < EPSILON ? float2(0.01f, 0.01f) : 0.0f;
  71. #else
  72. P1 += select(DistanceSqr(P0, P1) < EPSILON, float2(0.01f, 0.01f), 0.0f);
  73. #endif
  74. // store all of the start variables in a single float4
  75. float4 PQK = float4(P0, Q0.z, k0);
  76. // compute the step amount for each variable (SIMD)
  77. float4 dPQK = float4(P1, Q1.z, k1);
  78. dPQK -= PQK;
  79. bool permute = (abs(dPQK.x) < abs(dPQK.y));
  80. if (permute)
  81. {
  82. PQK.xy = PQK.yx;
  83. dPQK.xy = dPQK.yx;
  84. }
  85. dPQK /= MaxSteps;
  86. // advance by one step before starting the ray march
  87. PQK += dPQK;
  88. // ray march until the expected ray depth is beyond the actual scene depth
  89. bool foundHit = false;
  90. float rayZMin = 0.0f;
  91. float rayZMax = 0.0f;
  92. float prevRayZMaxEstimate = rayStartVS.z;
  93. for (uint step = 0; step < MaxSteps; ++step)
  94. {
  95. // validate the current screenspace coordinates (stored in PQK.xy)
  96. hitCoords = permute ? PQK.yx : PQK.xy;
  97. float4 validate = float4(hitCoords.x >= dimensions.x, hitCoords.y >= dimensions.y, hitCoords.x < 0, hitCoords.y < 0);
  98. if (any(validate))
  99. {
  100. break;
  101. }
  102. // retrieve scene depth from the linear viewspace depth buffer
  103. float sceneDepth = -PassSrg::m_depthLinear.Load(uint3(hitCoords, 0)).r;
  104. if (sceneDepth == 0.0f)
  105. {
  106. break;
  107. }
  108. // compute expected depth, and swap min/max if necessary
  109. rayZMin = prevRayZMaxEstimate;
  110. rayZMax = (dPQK.z * 0.5f + PQK.z) / (dPQK.w * 0.5f + PQK.w);
  111. prevRayZMaxEstimate = rayZMax;
  112. if (rayZMin > rayZMax)
  113. {
  114. swap(rayZMin, rayZMax);
  115. }
  116. // a hit occurs when the expected ray depth is beyond the scene depth and within the depth threshold (thickness)
  117. if ((rayZMin < sceneDepth) && (rayZMax >= sceneDepth - PassSrg::m_maxDepthThreshold))
  118. {
  119. foundHit = true;
  120. break;
  121. }
  122. // increase all three variables by the step amount
  123. PQK += dPQK;
  124. }
  125. if (foundHit)
  126. {
  127. // binary search refinement on the hit
  128. // start by moving back one step to just before the hit
  129. PQK -= dPQK;
  130. // the stride reduces the dQKP increment each iteration of the binary search
  131. float stride = 0.5f;
  132. // the sign of the stride is changed each iteration to control the step direction
  133. float strideAndDirection = stride;
  134. for (uint binarySearchStep = 0; binarySearchStep < BinarySearchSteps; ++binarySearchStep)
  135. {
  136. dPQK *= strideAndDirection;
  137. PQK += dPQK;
  138. // current screen coordinates are stored in PQK.xy
  139. hitCoords = permute ? PQK.yx : PQK.xy;
  140. // retrieve linear depth
  141. float sceneDepth = -PassSrg::m_depthLinear.Load(uint3(hitCoords, 0)).r;
  142. // compute the expected depth of the ray at this point, by performing the perspective-divide
  143. // on the current homogenous z-coordinate (in PQK.z) by the current w-coordinate (in PQK.w)
  144. float rayDepth = PQK.z / PQK.w;
  145. // determine if the expected ray depth is beyond the scene depth and within the depth tolerance
  146. bool exceedsSceneDepth = (rayDepth < sceneDepth);
  147. // reduce the stride each iteration
  148. stride *= 0.5f;
  149. // move backwards if the ray depth exceeds the scene depth, otherwise move forward
  150. strideAndDirection = exceedsSceneDepth ? -stride : stride;
  151. }
  152. }
  153. return foundHit;
  154. }