| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include <Atom/Features/SrgSemantics.azsli>
- #include <Atom/RPI/Math.azsli> // PI
- #include "RTAODefines.azsli"
- // GlobalSrg
- ShaderResourceGroup RayTracingGlobalSrg : SRG_RayTracingGlobal
- {
- RaytracingAccelerationStructure m_scene;
- Texture2D<float> m_depth;
- Texture2D<float4> m_worldNormalMap;
- RWTexture2D<float4> m_outputAO;
- float m_aoRadius; // Ambient occlusion radius. Default: 0.4f
- float m_rayMinT; // The minimum ray extent
- int m_frameCount; // Used for unique random seeds each frame. Default: 0
- uint m_numRays; // Number of ray casted for each pixel
- // Copy of ViewSrg::m_viewProjectionInverseMatrix since we can't access ViewSrg in ray tracing shaders ATM.
- row_major float4x4 m_viewProjectionInverseMatrix;
- };
- // Generates a seed for RNG from 2 input values
- uint InitRandomSeed(uint value1, uint value2)
- {
- uint v0 = value1, v1 = value2, s0 = 0;
- [unroll]
- for (uint n = 0; n < 16; n++)
- {
- s0 += 0x9e3779b9;
- v0 += ((v1 << 4) + 0xa341316c) ^ (v1 + s0) ^ ((v1 >> 5) + 0xc8013ea4);
- v1 += ((v0 << 4) + 0xad90777d) ^ (v0 + s0) ^ ((v0 >> 5) + 0x7e95761e);
- }
- return v0;
- }
- // Get a vector perpendicular to an input vector
- // "Efficient Construction of Perpendicular Vectors Without Branching"
- float3 GetPerpendicularVector(float3 input)
- {
- float3 absInput = abs(input);
- uint x = ((absInput.x < absInput.y) && (absInput.x < absInput.z)) ? 1 : 0;
- uint y = (absInput.y < absInput.z)? (1 ^ x) : 0;
- uint z = 1 ^ (x | y);
- return normalize(cross(input, float3(x, y, z)));
- }
- // Get a cosine-weighted random vector centered around a specified normal direction.
- float3 GetCosHemisphereSample(inout uint randSeed, float3 hitNorm)
- {
- // Get 2 random numbers to select our sample with
- float2 randVal = float2(NextRandomFloatUniform(randSeed), NextRandomFloatUniform(randSeed));
- float3 bitangent = GetPerpendicularVector(hitNorm);
- float3 tangent = cross(bitangent, hitNorm);
- float radius = sqrt(randVal.x);
- float angle = 2.0f * PI * randVal.y;
- // Get our cosine-weighted hemisphere lobe sample direction
- return tangent * (radius * cos(angle)) + bitangent * (radius * sin(angle)) + hitNorm.xyz * sqrt(1 - randVal.x);
- }
- float ShootRay(float3 orig, float3 dir, float minT, float maxT)
- {
- // Setup AO payload. Set the default to 0.0f (assume it's a hit)
- AORayPayload rayPayload = { 0.0f };
- RayDesc rayAO = { orig, minT, dir, maxT };
- // Skip closest-hit shader and stop as soon as any intersection found
- uint rayFlags = RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH | RAY_FLAG_SKIP_CLOSEST_HIT_SHADER;
- TraceRay(RayTracingGlobalSrg::m_scene, rayFlags, 0xFF, 0, 1, 0, rayAO, rayPayload );
- return rayPayload.aoValue;
- }
- [shader("raygeneration")]
- void AoRayGen()
- {
- // Where this thread's ray is on the screen
- uint2 launchIndex = DispatchRaysIndex().xy;
- uint2 launchDim = DispatchRaysDimensions().xy;
- // Initialize a random seed, per-pixel, based on a screen position and temporally varying count
- uint randSeed = InitRandomSeed(launchIndex.x + launchIndex.y * launchDim.x, RayTracingGlobalSrg::m_frameCount);
- // Get world position from screen position and depth
- float depth = RayTracingGlobalSrg::m_depth.Load(uint3(launchIndex, 0));
- // Position in native device coordinate
- float2 ndcPos = float2((float)launchIndex.x/(float)launchDim.x, 1.0f - (float)launchIndex.y/(float)launchDim.y) * 2.0f - 1.0f;
- float4 projectedPos = float4(ndcPos, depth, 1.0f);
- float4 worldPos = mul(RayTracingGlobalSrg::m_viewProjectionInverseMatrix, projectedPos);
- worldPos /= worldPos.w;
- float4 encodedNormal = RayTracingGlobalSrg::m_worldNormalMap.Load(uint3(launchIndex, 0));
- float3 worldNorm = DecodeNormalSignedOctahedron(encodedNormal.rgb);
- // Default ambient occlusion value if it hits the background
- float ambientOcclusion = 1.0f;
- // depth == 0 for background pixels; only shoot AO rays elsewhere
- if (depth != 0.0f)
- {
- // Start accumulating from zero if we don't hit the background
- ambientOcclusion = 0.0f;
- for (int i = 0; i < RayTracingGlobalSrg::m_numRays; i++)
- {
- // Sample cosine-weighted hemisphere around surface normal to pick a random ray direction
- float3 worldDir = GetCosHemisphereSample(randSeed, worldNorm.xyz);
- // Shoot our ambient occlusion ray and update the value we'll output with the result
- float minT = RayTracingGlobalSrg::m_rayMinT;
- float maxT = RayTracingGlobalSrg::m_aoRadius;
- ambientOcclusion += ShootRay(worldPos.xyz, worldDir, minT, maxT);
- }
- ambientOcclusion = ambientOcclusion / float(RayTracingGlobalSrg::m_numRays);
- }
-
- // Save out AO color
- RayTracingGlobalSrg::m_outputAO[launchIndex].rgb = ambientOcclusion;
- RayTracingGlobalSrg::m_outputAO[launchIndex].a = 1.0f;
- }
|