Search Unity

Weird Glitches with Directional Shadows

Discussion in 'General Graphics' started by Narkata55, Oct 17, 2021.

  1. Narkata55

    Narkata55

    Joined:
    Feb 25, 2017
    Posts:
    63
    Hey all, been experimenting with some realtime lighting using a single directional light, and am running into a weird issue that I can't figure out.

    Basically, I'm taking a directional light and rotating it to act as the sun, and using some 3D meshes to cast shadows (the mesh for each "tile" is a separate mesh, so they're not all attached). However, I'm doing a couple extra things to make the effect appear how I want
    --> I'm rendering the actual game through a main camera, and am using a separate camera to render the 3D "lighting" scene separately (and rendering that to a random unused render texture so that it isn't shown on screen)
    ---> The video below shows the main camera, while the picture attached shows the view from the 2nd 3D only camera with the "lighting scene"

    --> I'm also using a command buffer on the directional light to pass the AfterScreenspaceMask to a different render texture that I set as the mesh on a meshRenderer that follows the main game camera

    m_ShadowmapCopy = new RenderTexture(Screen.width, Screen.height, 0);
    tempRenderTex = new RenderTexture(Screen.width, Screen.height, 0);
    this.GetComponent<Camera>().targetTexture = tempRenderTex;

    RenderTargetIdentifier shadowmap = BuiltinRenderTextureType.CurrentActive;
    cb = new CommandBuffer();

    // Change shadow sampling mode for m_Light's shadowmap.
    cb.SetShadowSamplingMode(shadowmap, ShadowSamplingMode.RawDepth);

    // The shadowmap values can now be sampled normally - copy it to a different render texture.
    cb.Blit(shadowmap, new RenderTargetIdentifier(m_ShadowmapCopy));

    // Execute after the shadowmap has been filled.
    lightHur.AddCommandBuffer(LightEvent.AfterScreenspaceMask, cb);


    --> This meshRenderer has a shader that basically clips anything from this texture that isn't shadow, and softens the alpha of the shadow so that things can still be seen behind it

    float4 frag(v2f i) : SV_Target
    {
    fixed4 col = tex2D(_MainTex, i.uv);
    clip((col.a * -1) + 0.999);
    //col.a = clamp((col.a - 1) * -1 * _AlphaOfShadow, 0, 0.7);
    col.a = _AlphaOfShadow;
    return (col * _Color);
    }


    However, at certain angles of the meshes, and how the light hits them, there is this weird shadow line flickering appearing that I can't fix by adjusting things like bias/normal bias/quality, etc. that you can see in the video below

    https://imgur.com/a/jWPRGJE

    Any help would be much appreciated! I have also tried adjusting the scale of the meshes to see if making them slightly bigger would cover the tiny seams between them, and also tried changing the shadow casting mode to 2-sided, but neither of those help
     

    Attached Files:

  2. Narkata55

    Narkata55

    Joined:
    Feb 25, 2017
    Posts:
    63
    Bump, but I'd also like to make a daring move

    I spend half my remaining life points to summon @bgolus

     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    Looks like your standard shadow acne. This particular case being the kind that neither normal biasing nor light direction biasing can really solve, at least when you have hard edged normals.

    Try using smoothed normals on your shadow geometry. If you’re importing them from an external application you can tell Unity to override the normals by adjusting the import settings. Change Normals from import to one of the average options, and change the smoothing angle to as high as it’ll go. Or in the program you’re building them with and use a single smoothing group / remove creases / whatever paradigm your program uses to control the surface normals. If you’re building these meshes in Unity using Probuilder, it has a similar smoothing group setting. If you’re making them from code, use shared vertices instead of unique vertices per face.
     
  4. Narkata55

    Narkata55

    Joined:
    Feb 25, 2017
    Posts:
    63
    Hey, thanks for the input! I'm actually making them in code, so I'm trying some of what you're recommending. So are you saying that, for example, a cube should only have 8 verts instead of 24? Or are you saying that I should smooth the normals for all of the vertexes by looking at all the vertexes at the same positions? I was looking at the primitive cube that comes built-in to Unity and the normals look the same as mine, so I feel like that might not be the issue (unless I'm overlooking something in that comparison)

    On top of some other stuff, I've been looking at the scene view (i.e. camera) and it seems like that weird shadow acne isn't present there (there's still a little gap in between the different mesh objects, but that's not that big a deal). Is there something going on between the LightEvent.AfterScreenspaceMask and the actual rendering of the scene on the camera (with regards to the shadows)?

    ^If that's the case, maybe it would just be worth the effort to try to figure out a way to have a multi-camera setup and run some kind of shader on the "lighting camera" that would remove all pixels that weren't shadows?

    I'm still kind of a noob when it comes to graphics + shadow stuff, so I might be totally off-base with my ideas!
     
  5. Narkata55

    Narkata55

    Joined:
    Feb 25, 2017
    Posts:
    63
    For anyone else that sees this in the future, I ended up solving the issue, so I'll write the general outline of my solution below (don't tear into it too hard, I know it's not that smart lol, but it works!)

    Basically, I scrapped accessing the shadowmap directly. Something in between the AfterScreenspaceMask LightEvent and the camera rendering ends up smoothing out the shadow acne/artifacts, so I decided it would be easier to just let Unity do all that stuff and use the actual camera render as the "shadow texture" that I want to overlay on the screen

    Now, to do that is not so simple (at least for me haha). Taking a camera render and clearing out everything except for the shadows doesn't have the most obvious solution (it's just a Texture, with nothing special about it, after all). However, I ended up figuring a way to get around this:

    I took all the meshes I used to cast and receive shadows and gave them a white, emissive material. This means that the entire camera view is pure white except for the shadowed areas (which are a wonky shade of grey depending on shadow intensity). Not pretty for my eyes, but Unity can cast shadows with it fine enough. This allowed me to use a custom shader that clips all pixels that have an r value >= 0.99, and then take the remaining pixels and do whatever you need with them (I ended up turning their r values into a percentage of a range that worked for me and using that as the alpha for the "shadow pixel").
    --> I also ran into an issue clearing and reusing RenderTextures every frame, so make sure to call GL.Clear on the texture in your camera's OnRenderImage function if there is any weird, lingering garbage in the texture

    Finally, I passed this blitted, shadered texture into a scaled mesh that I placed in front of the actual game-view camera and voila!

    I hate graphics stuff lol
     
    Tiro24 likes this.
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    Yes, 8 verts.

    What you're overlooking is the default Unity cube will have the same shadow artifacts you are trying to avoid. These kinds of issues are very common even in AAA titles when the light direction is parallel with a flat surface, especially when there's a hard edge and a surface that's facing the light in the same "line".

    The scene view renders its own shadow maps. Artifacts that might be visible in some cameras may not be visible in others. Shadow acne is essentially an aliasing problem, and the way the math works out with an orthographic camera what might appear as less obvious random dots in a perspective camera (like the scene view defaults to) may end up as much more obvious lines in an orthographic camera.
     
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    And yes. The shadow map is "softened" by blurring it during the sampling. The screen space shadow texture that gets generated by Unity's built in render pipeline samples the shadow map multiple times to blur it, which will reduce artifacts. Though clipping on a threshold means you'd still possibly end seeing streaks unless you tackled the shadow acne directly as blurred doesn't mean gone.