RTAOGeneration.azsl 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  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/SrgSemantics.azsli>
  9. #include <Atom/RPI/Math.azsli> // PI
  10. #include "RTAODefines.azsli"
  11. // GlobalSrg
  12. ShaderResourceGroup RayTracingGlobalSrg : SRG_RayTracingGlobal
  13. {
  14. RaytracingAccelerationStructure m_scene;
  15. Texture2D<float> m_depth;
  16. Texture2D<float4> m_worldNormalMap;
  17. RWTexture2D<float4> m_outputAO;
  18. float m_aoRadius; // Ambient occlusion radius. Default: 0.4f
  19. float m_rayMinT; // The minimum ray extent
  20. int m_frameCount; // Used for unique random seeds each frame. Default: 0
  21. uint m_numRays; // Number of ray casted for each pixel
  22. // Copy of ViewSrg::m_viewProjectionInverseMatrix since we can't access ViewSrg in ray tracing shaders ATM.
  23. row_major float4x4 m_viewProjectionInverseMatrix;
  24. };
  25. // Generates a seed for RNG from 2 input values
  26. uint InitRandomSeed(uint value1, uint value2)
  27. {
  28. uint v0 = value1, v1 = value2, s0 = 0;
  29. [unroll]
  30. for (uint n = 0; n < 16; n++)
  31. {
  32. s0 += 0x9e3779b9;
  33. v0 += ((v1 << 4) + 0xa341316c) ^ (v1 + s0) ^ ((v1 >> 5) + 0xc8013ea4);
  34. v1 += ((v0 << 4) + 0xad90777d) ^ (v0 + s0) ^ ((v0 >> 5) + 0x7e95761e);
  35. }
  36. return v0;
  37. }
  38. // Get a vector perpendicular to an input vector
  39. // "Efficient Construction of Perpendicular Vectors Without Branching"
  40. float3 GetPerpendicularVector(float3 input)
  41. {
  42. float3 absInput = abs(input);
  43. uint x = ((absInput.x < absInput.y) && (absInput.x < absInput.z)) ? 1 : 0;
  44. uint y = (absInput.y < absInput.z)? (1 ^ x) : 0;
  45. uint z = 1 ^ (x | y);
  46. return normalize(cross(input, float3(x, y, z)));
  47. }
  48. // Get a cosine-weighted random vector centered around a specified normal direction.
  49. float3 GetCosHemisphereSample(inout uint randSeed, float3 hitNorm)
  50. {
  51. // Get 2 random numbers to select our sample with
  52. float2 randVal = float2(NextRandomFloatUniform(randSeed), NextRandomFloatUniform(randSeed));
  53. float3 bitangent = GetPerpendicularVector(hitNorm);
  54. float3 tangent = cross(bitangent, hitNorm);
  55. float radius = sqrt(randVal.x);
  56. float angle = 2.0f * PI * randVal.y;
  57. // Get our cosine-weighted hemisphere lobe sample direction
  58. return tangent * (radius * cos(angle)) + bitangent * (radius * sin(angle)) + hitNorm.xyz * sqrt(1 - randVal.x);
  59. }
  60. float ShootRay(float3 orig, float3 dir, float minT, float maxT)
  61. {
  62. // Setup AO payload. Set the default to 0.0f (assume it's a hit)
  63. AORayPayload rayPayload = { 0.0f };
  64. RayDesc rayAO = { orig, minT, dir, maxT };
  65. // Skip closest-hit shader and stop as soon as any intersection found
  66. uint rayFlags = RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | RAY_FLAG_SKIP_CLOSEST_HIT_SHADER;
  67. TraceRay(RayTracingGlobalSrg::m_scene, rayFlags, 0xFF, 0, 1, 0, rayAO, rayPayload );
  68. return rayPayload.aoValue;
  69. }
  70. [shader("raygeneration")]
  71. void AoRayGen()
  72. {
  73. // Where this thread's ray is on the screen
  74. uint2 launchIndex = DispatchRaysIndex().xy;
  75. uint2 launchDim = DispatchRaysDimensions().xy;
  76. // Initialize a random seed, per-pixel, based on a screen position and temporally varying count
  77. uint randSeed = InitRandomSeed(launchIndex.x + launchIndex.y * launchDim.x, RayTracingGlobalSrg::m_frameCount);
  78. // Get world position from screen position and depth
  79. float depth = RayTracingGlobalSrg::m_depth.Load(uint3(launchIndex, 0));
  80. // Position in native device coordinate
  81. float2 ndcPos = float2((float)launchIndex.x/(float)launchDim.x, 1.0f - (float)launchIndex.y/(float)launchDim.y) * 2.0f - 1.0f;
  82. float4 projectedPos = float4(ndcPos, depth, 1.0f);
  83. float4 worldPos = mul(RayTracingGlobalSrg::m_viewProjectionInverseMatrix, projectedPos);
  84. worldPos /= worldPos.w;
  85. float4 encodedNormal = RayTracingGlobalSrg::m_worldNormalMap.Load(uint3(launchIndex, 0));
  86. float3 worldNorm = DecodeNormalSignedOctahedron(encodedNormal.rgb);
  87. // Default ambient occlusion value if it hits the background
  88. float ambientOcclusion = 1.0f;
  89. // depth == 0 for background pixels; only shoot AO rays elsewhere
  90. if (depth != 0.0f)
  91. {
  92. // Start accumulating from zero if we don't hit the background
  93. ambientOcclusion = 0.0f;
  94. for (int i = 0; i < RayTracingGlobalSrg::m_numRays; i++)
  95. {
  96. // Sample cosine-weighted hemisphere around surface normal to pick a random ray direction
  97. float3 worldDir = GetCosHemisphereSample(randSeed, worldNorm.xyz);
  98. // Shoot our ambient occlusion ray and update the value we'll output with the result
  99. float minT = RayTracingGlobalSrg::m_rayMinT;
  100. float maxT = RayTracingGlobalSrg::m_aoRadius;
  101. ambientOcclusion += ShootRay(worldPos.xyz, worldDir, minT, maxT);
  102. }
  103. ambientOcclusion = ambientOcclusion / float(RayTracingGlobalSrg::m_numRays);
  104. }
  105. // Save out AO color
  106. RayTracingGlobalSrg::m_outputAO[launchIndex].rgb = ambientOcclusion;
  107. RayTracingGlobalSrg::m_outputAO[launchIndex].a = 1.0f;
  108. }