SphereIntersection.azsl 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  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 <Atom/Features/RayTracing/RayTracingIntersectionAttributes.azsli>
  9. #include <Atom/Features/RayTracing/RayTracingSrgs.azsli>
  10. #include <Atom/Features/RayTracing/RayTracingSceneUtils.azsli>
  11. #include <Atom/Features/Bindless.azsli>
  12. // https://gamedev.stackexchange.com/a/96487
  13. bool IntersectRaySphere(float3 p, float3 d, float3 center, float radius, out float t)
  14. {
  15. t = 0.f;
  16. float3 m = p - center;
  17. float b = dot(m, d);
  18. float c = dot(m, m) - radius * radius;
  19. // Exit if r’s origin outside s (c > 0) and r pointing away from s (b > 0)
  20. if (c > 0.f && b > 0.f)
  21. return false;
  22. float discr = b * b - c;
  23. // A negative discriminant corresponds to ray missing sphere
  24. if (discr < 0.f)
  25. return false;
  26. // Ray now found to intersect sphere, compute smallest t value of intersection
  27. t = -b - sqrt(discr);
  28. // If t is negative, ray started inside sphere so clamp t to zero
  29. if (t < 0.f)
  30. t = 0.f;
  31. return true;
  32. }
  33. float3 ExtractScale(float3x4 m)
  34. {
  35. const float sx = length(float3(m[0][0], m[1][0], m[2][0]));
  36. const float sy = length(float3(m[0][1], m[1][1], m[2][1]));
  37. const float sz = length(float3(m[0][2], m[1][2], m[2][2]));
  38. return float3(sx, sy, sz);
  39. }
  40. [shader("intersection")]
  41. void SphereIntersection()
  42. {
  43. const float3 nonUniformScale = ExtractScale(ObjectToWorld3x4());
  44. const float objectUniformScale = max(max(nonUniformScale.x, nonUniformScale.y), nonUniformScale.z);
  45. const float3 sphereCenter = float3(0, 0, 0);
  46. const float sphereRadius = 1.f;
  47. const float3 sphereCenterWS = mul(ObjectToWorld3x4(), float4(sphereCenter, 1.f));
  48. // These two methods of calculating the world-space radius are equivalent:
  49. // const float sphereRadiusWS = sphereRadius * objectUniformScale;
  50. // const float sphereRadiusWS = asfloat(Bindless::GetByteAddressBuffer(GetBindlessBufferIndex()).Load(GetLocalInstanceIndex() * 4));
  51. // The second method is used to make sure that accessing the bindless buffer and local index work correctly
  52. const float sphereRadiusWS = asfloat(Bindless::GetByteAddressBuffer(GetBindlessBufferIndex()).Load(GetLocalInstanceIndex() * 4));
  53. float t;
  54. if (IntersectRaySphere(WorldRayOrigin(), WorldRayDirection(), sphereCenterWS, sphereRadiusWS, t))
  55. {
  56. ProceduralGeometryIntersectionAttributes attrib;
  57. attrib.m_position = ObjectRayOrigin() + ObjectRayDirection() * t;
  58. attrib.m_normal = half3(normalize(attrib.m_position - sphereCenter));
  59. attrib.m_uv = half2(0, 0);
  60. attrib.m_tangent = half4(1, 0, 0, 1);
  61. ReportHit(t, 0, attrib);
  62. }
  63. }