0
$\begingroup$

My main problem is: When I'm raymarching (should I use View Space or Screen Space/Clip Space?), how do I determine condition to end marching and find which UV's color should be used?

My current code and results:

bool GetReflection(float3 ScreenSpaceReflectionVec, float3 ScreenSpacePos, out float3 ReflectionColor)
{
    for (int RayStepIdx = 0; RayStepIdx < NUM_RAY_MARCH_SAMPLES; RayStepIdx++)
    {
        float3 RaySample = (RayStepIdx * MAX_REFLECTION_RAY_MARCH_STEP) * ScreenSpaceReflectionVec + ScreenSpacePos;
        float ZBufferVal = (hiZBuffer.Sample(pointSampler, RaySample.xy).r); // Current Pos
        float ZBufferOrigin = (hiZBuffer.Sample(pointSampler, ScreenSpacePos.xy).r); // Origin Pos
                
        if (ZBufferOrigin > ZBufferVal)
        {
            ReflectionColor = colorBuffer.SampleLevel(pointSampler, RaySample.xy, 0).rgb;
            return true;
        }
    }

    return false;
}

// Camera ray in CS
inline void GenerateCameraRay(uint2 index, uint2 dimensions, float4x4 projectionToWorld, inout float3 origin, out float3 direction)
{
    // Doesn't really matter, works correctly
}

float4 SSR(PixelInputType input) : SV_TARGET
{
    float3 normalVS = normalBuffer.Sample(pointSampler, input.uv).xyz;
    if (!any(normalVS))
    {
        return float4(colorBuffer.SampleLevel(pointSampler, input.uv, 0).rgb, 1.0f);
    }
    normalVS = mul(float4(normalVS, 0.0f), g_matricesCB.viewMatrix).xyz;
    
    float depthCS = hiZBuffer.Sample(pointSampler, input.uv).r;
    
    // Calculate primary ray origin and direction
    float3 primaryRayOrigin = g_constantBuffer.cameraPosition.xyz;
    float3 primaryRayDirection;
    GenerateCameraRay(input.uv * float2(1024.0f, 512.0f), float2(1024.0f, 512.0f), g_matricesCB.invViewProjMatrix, primaryRayOrigin, primaryRayDirection);      
    
    float3 ReflectionColor = float3(0, 0, 0);
    if (GetReflection(primaryRayDirection, float3(input.uv, depthCS), ReflectionColor))
    {
        return float4(ReflectionColor + colorBuffer.SampleLevel(pointSampler, input.uv, 0).rgb, 1.0f);
    // * float4(1, 0, 0, 1); // DEBUG multiply
    }
    
    return float4(colorBuffer.SampleLevel(pointSampler, input.uv, 0).rgb, 1.0f); // Just return scene color
}

White is 3D plane model and red is reflection of that model reflected on black plane: enter image description here enter image description here

But using presented code above with textures, it looks like this (because iteration always stops on first Suzanne's pixel due to current depth condition): enter image description here

My question is - how can I correctly obtain UV in reflection plane and what should be terminating condition of raymarching?

$\endgroup$

1 Answer 1

1
$\begingroup$

The termination condition is that the ray passes through the surface represented in the depth buffer. To track this, you have to calculate the z position of the ray at the current step, and compare it to the value sampled from the depth buffer at the ray's current screen-space position. In terms of your code, this would be something like RaySample.z > ZBufferVal (assuming that your Z is increasing with distance from the camera).

To make it a bit fancier and more accurate, you could also interpolate the position over the last two steps: the second to last step is known to be in front of the depth buffer, and the last step is known to be behind it, so you could calculate the intersection point of the ray approximating the depth as linear between those last two steps.

$\endgroup$
5
  • $\begingroup$ I've tried it but it doesn't work (I've also tried it in the past and it also failed). Using this condition, iterations stops after first step because moving farther from camera increases Z value. My Z buffer (or Hi-Z buffer mip0) is simply Z position in Projection Space obtained by multiplying MVP matrix, so if I understand correctly, my Z Buffer is exactly the same as RaySample.z which causes the problem. I assume that I should use different Z buffer, but I don't know what this buffer should contain. $\endgroup$ Commented Oct 23, 2021 at 18:35
  • 1
    $\begingroup$ You probably have to skip the check on the first step. (Try starting RayStepIdx at 1 instead of 0.) Once you are stepping along the ray, it is heading away from the surface where it was reflected, so it should not be immediately going behind the depth buffer unless it's in a concavity where the reflected ray would actually be blocked. $\endgroup$ Commented Oct 23, 2021 at 18:43
  • 1
    $\begingroup$ And just to be clear, you're stepping the ray position in 3D - so it steps through Z at a fixed rate, the same as it steps through the UV space. The Z position of the ray is that obtained from the parametric ray equation, and you compare against the Z value obtained from the Hi-Z buffer under the ray's sample point. $\endgroup$ Commented Oct 23, 2021 at 18:44
  • $\begingroup$ It doesn't work unfortunatelly. If you have a working and simple source code of SSR/RTLR implementation, I would appreciate it. Because it seems that maybe I've made several mistakes in my code that I cannot see. $\endgroup$ Commented Oct 25, 2021 at 8:33
  • 1
    $\begingroup$ Sadly, my previous implementations of Hi-Z ray marching were for past jobs where I no longer have source access. But I think there are many implementations of it available on the web. You could also look at the UE4 source code (I'm not sure off the top of my head if it uses Hi-Z or not). $\endgroup$ Commented Oct 25, 2021 at 16:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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