🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Screen Space Shadows

Started by
7 comments, last by __MONTE2020 2 years, 9 months ago

Has anyone tried implementing screen space shadows? You can see what I am talking about https://panoskarabelas.com/posts/screen_space_shadows/​​ . It seems simple so I tried to implement it and here is my HLSL code:

static const uint SSS_MAX_STEPS = 16; // Max ray steps, affects quality and performance.
static const float SSS_RAY_MAX_DISTANCE = 0.05f; // Max shadow length, longer shadows are less accurate.
static const float SSS_THICKNESS = 0.1f; // Depth testing thickness.
static const float SSS_STEP_LENGTH = SSS_RAY_MAX_DISTANCE / (float) SSS_MAX_STEPS;

float ScreenSpaceShadows(float2 uv)
{
    float depth = depthTx.Sample(linear_wrap_sampler, uv);
    float3 ray_position = GetPositionVS(uv, depth);
    
    float3 ray_direction = normalize(-current_light.direction.xyz);
    float3 ray_step = ray_direction * SSS_STEP_LENGTH;
    ray_position += ray_step * dither(uv);
    
     // Ray march towards the light
    float occlusion = 0.0;
    float2 ray_uv = 0.0f;
    
    for (uint i = 0; i < SSS_MAX_STEPS; i++)
    {
        // Step the ray
        ray_position += ray_step;

        float4 ray_projection = mul(float4(ray_position, 1.0), projection);
        ray_projection.xyz /= ray_projection.w;
        ray_uv.xy = 0.5 * ray_projection.xy + 0.5;
        ray_uv.y = 1.0 - ray_uv.y;

        [branch]
        if (IsSaturated(ray_uv))
        {
            float depth_z = depthTx.Sample(linear_wrap_sampler, ray_uv);
            
            // Compute the difference between the ray's and the camera's depth
            float depth_linear = ConvertZToLinearDepth(depth_z);
            float depth_delta = ray_projection.z - depth_z;

            // Check if the camera can't "see" the ray (ray depth must be larger than the camera depth, so positive depth_delta)
            if (depth_delta > 0 && (depth_delta < SSS_THICKNESS))
            {
                // Mark as occluded
                occlusion = 1.0f;

                // screen edge fade:
                float2 fade = max(12 * abs(ray_uv - 0.5) - 5, 0);
                occlusion *= saturate(1 - dot(fade, fade));

                break;
            }
        }
    }

    // Convert to visibility
    return 1.0f - occlusion;
}

The problem is it's full of artifacts. I am not sure if I have bug in my code or this method works on simple scenes (I tested it using Sponza, I can send screenshots if needed). Any thoughts?

Advertisement

I did not read the article or code, but I have this to offer as help:

Screen space shadows don't work. There are at least three problems:

  1. Performance – tracing a line from the lit-pixel through the screen to each light source is a lot of texture samples
  2. Screen space – things (or part of things) will stop casting shadows when they are clipped at the edge of the screen
  3. Occlusion – light cannot pass behind things on the screen. A tree in the foreground will cast a shadow as if it's a wall extending from its position, back to infinity

enum Bool { True, False, FileNotFound };

Screenshots? The type of artifact is really important here, a decent amount of things you simply can't get rid of with screenspace hacks like shadows. Others you can, so screenshots/video would help a lot.

Post screenshot of artifact. As previous comments said, screen space shadows will always have some issues, but they can also work fine for very short range detail shadows, the kind of thing that shadow maps cannot capture.

Sorry for late response, here is the screenshot. My intention wasn't to use screen space shadows instead of shadow maps but to combine them.

EDIT: I can't upload image for some reason, here is the link https://ibb.co/sKSLWm0

ray_position += ray_step * dither(uv);

This looks suspicious to me, is it possible that the ray start gets moved back into the geometry? Have you tried without this step (and on a simpler scene)? Also please include the code for “dither”.

Eternal said:

ray_position += ray_step * dither(uv);

This looks suspicious to me, is it possible that the ray start gets moved back into the geometry? Have you tried without this step (and on a simpler scene)? Also please include the code for “dither”.

Self occlusion problems are definitely a classic shadowing artifact… it looks possible and there's that odd pattern of the artifact disappearing and reappearing in a pattern so…

It should actually be

ray_position += ray_step * dither(uv * frame_cbuf.screen_resolution);

but the artifacts are still there. dither returns Bayer matrix element (a positive number) so I don't think dithering is a problem. Anyway, this is low priority problem for me, I was just wondering if someone else had similar experience with them.

This topic is closed to new replies.

Advertisement