I am working on ray/path-tracer using DXR and I'm starting with simple AO. In first pass, I am storing normal value and RayTCurrent():
[shader("closesthit")]
void ClosestHit(inout RayPayload payload, in Attributes attrib)
{
// Calculate triangle normal code - unrelated, so I deleted it
// Store RayTCurrent value in payload
payload.normalWithDepth = float4(triangleNormal, RayTCurrent());
}
[shader("raygeneration")]
void RayGen()
{
RayDesc ray = { origin, 1e-4f, rayDir, 1e+38f};
RayPayload payload;
payload.worldPos = float4(0, 0, 0, 0);
payload.normalWithDepth = float4(0, 0, 0, 0);
TraceRay(SceneBVH, RAY_FLAG_NONE, 0xFF, 0, 1, 0, ray, payload);
// Simply storing returned payload in RWTexture<float4>
RTOutputNormal[DispatchRaysIndex().xy] = payload.normalWithDepth;
}
In AO pass I am using this information to calculate world space position of given pixel (i.e. transform from ndc to world space coordinate):
[shader("raygeneration")]
void RayGenAO()
{
float2 pixelCenter = (float2) LaunchIndex / (float2) LaunchDimensions + float2(0.5f / (float) LaunchDimensions.x, 0.5f / (float) LaunchDimensions.y);
float4 normalAndDepth = NormalTextureInput.SampleLevel(g_sampler, pixelCenter, 0);
// normalAndDepth.w is clamped to 1.0f, therefore information about distance to hit point is lost
}
You can see, that above code results in correct AO results (1 spp noise with no filtering or accumulation) for happy buddha model. However, any AO can be seen when camera is extremely close (max 1 unit from object). In other cases I am getting completely white, flat model. The problem is that texture is clamping value in first pass to 1, so I am losing information about real distance between ray origin and ray hit.
Here is a hlsl code for AO generation. I don't think that any problem exists in there, apart from not enough information from texture. Therefore, below code can be ignored:
[shader("raygeneration")]
void RayGen()
{
uint2 LaunchIndex = DispatchRaysIndex().xy;
uint2 LaunchDimensions = DispatchRaysDimensions().xy;
// Figure out pixel world space position (using length of a primary ray found in previous pass)
float3 primaryRayOrigin, primaryRayDirection;
GenerateCameraRay(LaunchIndex, primaryRayOrigin, primaryRayDirection);
float2 pixelCenter = (float2) LaunchIndex / (float2) LaunchDimensions + float2(0.5f / (float) LaunchDimensions.x, 0.5f / (float) LaunchDimensions.y);
float4 normalAndDepth = NormalTextureInput.SampleLevel(g_sampler, pixelCenter, 0);
float3 pixelWorldSpacePosition = primaryRayOrigin + (primaryRayDirection * normalAndDepth.w);
//RTOutput[LaunchIndex] = float4(normalAndDepth.w, normalAndDepth.w, normalAndDepth.w, 1.0f);
//return;
if (normalAndDepth.w == 0.0f)
{
// Terminate if primary ray didn't hit anything
RTOutput[LaunchIndex] = float4(0.0f, 0.2f, 0.4f, 1.0f);
return;
}
uint seed = initRand(LaunchIndex.x + LaunchIndex.y * LaunchDimensions.x, g_cubeCB.frameCount);
float3 worldDir = getCosHemisphereSample(seed.x, normalAndDepth.xyz);
RayDesc aoRay;
RayPayload payload;
float3 aoSampleDirection = worldDir;
float ao = 0.0f;
aoRay.Origin = pixelWorldSpacePosition;
aoRay.TMin = g_aoCB.minT;
aoRay.TMax = g_aoCB.aoRadius; //< Set max ray length to AO radius for early termination
aoRay.Direction = aoSampleDirection;
payload.T = g_aoCB.aoRadius; //< Set T to "maximum", to produce no occlusion in case ray doesn't hit anything (miss shader won't modify this value)
// Trace the ray
TraceRay(SceneBVH, RAY_FLAG_NONE, 0xFF, 0, 1, 0, aoRay, payload);
ao = payload.T / g_aoCB.aoRadius;
RTOutput[LaunchIndex.xy] = float4(ao, ao, ao, 1.0f); //< Replace all cached AO with current result
}
[shader("miss")]
void Miss(inout RayPayload payload : SV_RayPayload)
{
// Empty
}
[shader("closesthit")]
void ClosestHit(inout RayPayload payload, in Attributes attrib)
{
payload.T = RayTCurrent();
}