2
\$\begingroup\$

I'm trying to do realtime lighting, and my shadows are acting very strange. When I project a spotlight out from the camera (like a flashlight in a first person game) my shadows show up as 3D skews of the objects, and I can actually clip through them if I move the camera in the right way... if I detach lights from the camera and just have them sit in one position, I don't seem to have this issue (cylinder to the right is just the model Im using for the flashlight... its attached to the camera):

enter image description here

enter image description here

  • All objects have Cast Shadows set to "On"
  • All objects have Receive Shadows checked
  • All objects have Light Probes and Reflection Probes set to "Blend Probes"
  • All objects have Motion Vectors set to "Per Object Motion"
  • All objects have Lightmap Static unchecked
  • All objects have Dynamic Occluded checked
  • All lights have Mode set to "Realtime"
  • All lights have Shadow Type set to "Soft Shadows"
  • All lights have Realtime Shadow Strength set to 1
  • All lights have Realtime Shadow Resolution set to "Very High Resolution"
  • All lights have Realtime Shadow Bias set to 0 (changing this doesn't seem to effect the issue)
  • All lights have Realtime Shadow Normal Bias set to 0 (changing this doesn't seem to effect the issue)
  • All lights have Realtime Shadow Near Plane set to 0.1 (changing this doesn't seem to effect the issue)
  • All lights have Draw Halo unchecked
  • All lights have Render Mode set to "Auto"
  • All lights have Culling Mask set to "Everything"
  • Camera has Clear Flags set to "Solid Color"
  • Camera has Background set to #000000
  • Camera has Culling Mask set to "Everything"
  • Camera has Projection set to "Perspective"
  • Camera has Field of View set to 60
  • Camera has Near Clipping Plane set to 0.3
  • Camera has Far Clipping Plane set to 1000
  • Camera has Viewport Rect set to 0, 0, 1, 1
  • Camera has Depth set to 0
  • Camera has Rendering Path set to Use Graphics Settings
  • Camera has Occlusion Culling checked
  • Camera has Allow HDR checked
  • Camera has Allow MSAA checked
  • Camera has Allow Dynamic Resolution unchecked
  • Camera has Target Display set to "Display 1"
  • In Player, Rendering Color Space set to "Linear"
  • In Player, all Rendering Auto Graphics APIs checked (win, mac, lin)
  • In Player, Rendering Static Batching checked
  • In Player, Rendering Dynamic Batching checked
  • In Player, Rendering GPU Skinning unchecked
  • In Player, Rendering Graphics Job (Experimental)* unchecked
  • In Player, Rendering Lightmap Encoding set to "High Quality"
  • In Graphics, all Built-in Shader Settings are set to "Built-in shader"
  • In Graphics, Shader Stripping Lightmap Modes is set to "Automatic"
  • In Graphics, Shader Stripping Fog Mode is set to "Automatic"
  • In Graphics, Shader Stripping Instancing Variants is set to "Strip Unused"
  • In Graphics, Preloaded Shaders Size is set to 0
  • In Lighting, Skybox Material is set to "Default-Skybox"
  • In Lighting, Sun Source is set to "None (Light)"
  • In Lighting, Environment Lighting Source is set to "Color"
  • In Lighting, Environment Lighting Ambient Color is set to #000000
  • In Lighting, Environment Lighting Ambient Mode is set to "Realtime" (and the dropdown box is greyed out, so I can't change it)
  • In Lighting, Environment Reflections Source is set to "Custom"
  • In Lighting, Environment Reflections Cubemap is set to "None (Cubemap)"
  • In Lighting, Environment Reflections Compression is set to "Auto"
  • In Lighting, Environment Reflections Intensity Multiplier is set to 0
  • In Lighting, Environment Reflections Bounces is set to 1
  • In Lighting, Realtime Global Illumination is checked
  • In Lighting, Baked Global Illumination is unchecked
  • In Lighting, Fog is unchecked
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

Just like your main rendering camera, your shadowcasting lights have a near plane. They can only see geometry that's out past the near plane - anything closer along the light's axis gets clipped out.

As described in the Unity manual on shadowing:

enter image description here

A low Shadow near plane offset value create the appearance of holes in shadows

When you let a shadowcasting light get arbitrarily close to an object, eventually the object will clip that near plane. The clipped region will no longer be visible in the light's shadow map, and so it won't block light from reaching content further away.

As you've found, just reducing the light's shadow near plane parameter alone won't fix this issue - we can't reduce the near plane below about 1/100th of the light's range for a spot light, or 0.1, whichever is smaller. (This constraint helps keep the shadow map's depth precision well-distributed over the light's range, to minimize shadow acne and leaking). If our light can get closer to the object than that, we're basically* out of luck.

So, we need to keep the light from pushing that close to an object. One way you can do this for a moving light is to check it against the object colliders, using eg. SphereCasts or ComputePenetration to find a safe place to stop the light before it clips in.

Whatever collision shape you use should encompass the whole tip of the light cone closer than the near plane, so don't forget to account for the width of the light disc at near plane distance. ie...

float halfAngle = 0.5f * light.spotAngle * Mathf.Deg2Rad;
nearConeRadius = light.shadowNearPlane * Mathf.Tan(halfAngle);
nearConeSideLength = light.shadowNearPlane/Mathf.Cos(halfAngle);

To help match the collision volume you need for good shadow rendering against the rendered geometry of the flashlight, you can situate the point of the light itself deeper inside the housing of the light source, and use the front lens/reflector section to fill out the no-go zone for collisions.

* I say "basically" because there are potential workarounds, but they're not pretty. For most cases, you're better off keeping the light slightly away from potential occluders.

If you really need close shadows though, you can set up a kind of cascaded setup, stacking:

  • A second light at the same position of the first, with its far plane at the original light's near plane, and its near plane correspondingly closer.

  • A camera at the same position as this "near light" that renders the same depth range into a masking RenderTexture

  • Use this RenderTexture mask as a light cookie or shadowcaster seen by your far light, to pass shadow information from the near light down to the more distant end of the light cone.

  • If needed, you could in principle stack this multiple layers deep.

\$\endgroup\$
1
  • \$\begingroup\$ I'm a little late, but thanks for this in-depth answer! \$\endgroup\$
    – the8thbit
    Jan 14, 2020 at 19:19

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .