1
$\begingroup$

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
}

enter image description here

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();
    }
$\endgroup$
0
$\begingroup$

I decided to use R16B16G16A16_FLOAT format instead to avoid problems as I'm working on hobby/academic code and it isn't fully fledged game/graphics engine.

I solves my problem. FYI, I've also tried using R8B8G8A8_SNORM instead of *_UNORM, but I failed to get correct results with that. Maybe it was due to other factors, so I don't fully blame format.

| improve this answer | |
$\endgroup$
  • 1
    $\begingroup$ SNORM (-1 to 1) and UNORM (0 to 1) are by definition clamped (normalised). See also docs.microsoft.com/en-us/windows/win32/direct3d10/… $\endgroup$ – PaulHK Aug 20 at 10:28
  • $\begingroup$ @PaulHK Thank you. I've already read about UNORM/SNORM formats and realized my mistake. Therefore, I tried to use it for storing normals. But as I said - it's academic project so I decided to use FP16 to avoid problems and get better image quality. $\endgroup$ – DirectX_Programmer Aug 20 at 11:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.