SphereIntersection.azsl 3.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  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/RPI/Math.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. float2 NormalToAzimuthElevation(float3 normal)
  41. {
  42. float azimuth = atan2(normal.y, normal.x); // Azimuth in range [-PI, PI]
  43. float elevation = asin(normal.z); // Elevation in range [-PI/2, PI/2]
  44. return float2(azimuth / TWO_PI + 0.5f, elevation / PI + 0.5f);
  45. }
  46. [shader("intersection")]
  47. void SphereIntersection()
  48. {
  49. const float3 nonUniformScale = ExtractScale(ObjectToWorld3x4());
  50. const float objectUniformScale = max(max(nonUniformScale.x, nonUniformScale.y), nonUniformScale.z);
  51. const float3 sphereCenter = float3(0, 0, 0);
  52. const float sphereRadius = 1.f;
  53. const float3 sphereCenterWS = mul(ObjectToWorld3x4(), float4(sphereCenter, 1.f));
  54. // These two methods of calculating the world-space radius are equivalent:
  55. // const float sphereRadiusWS = sphereRadius * objectUniformScale;
  56. // const float sphereRadiusWS = asfloat(Bindless::GetByteAddressBuffer(GetBindlessBufferIndex()).Load(GetLocalInstanceIndex() * 4));
  57. // The second method is used to make sure that accessing the bindless buffer and local index work correctly
  58. const float sphereRadiusWS = asfloat(Bindless::GetByteAddressBuffer(GetBindlessBufferIndex()).Load(GetLocalInstanceIndex() * 4));
  59. float t;
  60. if (IntersectRaySphere(WorldRayOrigin(), WorldRayDirection(), sphereCenterWS, sphereRadiusWS, t))
  61. {
  62. float3 position = ObjectRayOrigin() + ObjectRayDirection() * t;
  63. float3 normal = normalize(position - sphereCenter);
  64. ProceduralGeometryIntersectionAttributes attrib;
  65. attrib.SetPosition(position);
  66. attrib.SetNormal(normal);
  67. attrib.SetUv(NormalToAzimuthElevation(normal));
  68. attrib.SetTangent(float4(1, 0, 0, 1));
  69. ReportHit(t, 0, attrib);
  70. }
  71. }