Custom Shadow Mapping in Unity

What Is Shadow Mapping and Why Should I Care

Shadow mapping Implementation, a Top Down Look

  1. A visible set needs to be determined for directional light’s camera
  2. The depth of each fragment of the visible set is rendered to a buffer by the light camera. The depth is of course in relation to the light camera. This is also where different resolution of depth maps are rendered in different shadow cascades
  3. The main camera renders the depth of each fragment it sees, in relation to itself
  4. In the shadow collecting phase, first using the depth map of the main camera, the world position of each fragment is reconstructed
  5. Then using the VP matrix of the light camera (projection*view), the pixel is transferred from world space coordinates to the homogeneous camera coordinate of the light camera. In this coordinate system x and y correlate to the width and height position of the fragment in the light camera texture and z is the depth of the fragment in the light camera. We have successfully converted the fragment coordinates in a system where we can compare its depth with an occluder’s depth and determine if the occluder is occluding the fragment.
  6. At this stage, it is time to unpack the depth value we rendered in stage 2. If there are no shadow cascades, we can simply remap the homogeneous coordinates we calculated from -1 to 1 to 0 to 1 and sample the light camera’s depth texture. However with shadow cascades this is a bit different, since we need to first choose the correct shadow cascades texture to sample and calculate the uv coordinate for that specific texture.
  7. Once that is done, we sample the light camera’s depth texture. Then we compare the z value we calculated at stage 5 with the depth value we just retreated. If the depth value of the depth texture is smaller than our z value, it means the fragment is occluded and is in shadow. The logic here is that smaller depth value means that there exists a fragment, along the path of the fragment we are rendering to the light, which is closer to the light than the fragment we are rendering and hence it is occluded by whatever fragment the light is seeing. (Of course you need to keep an eye on reversed Z buffer and such)

Implementation in Unity

cb = new CommandBuffer;RenderTargetIdentifier shadowmap = BuiltinRenderTextureType.CurrentActive;
m_ShadowmapCopy = new RenderTexture(1024, 1024, 16, RenderTextureFormat.ARGB32);
m_ShadowmapCopy.filterMode = FilterMode.Point;
cb.SetShadowSamplingMode(shadowmap, ShadowSamplingMode.RawDepth);
var id = new RenderTargetIdentifier(m_ShadowmapCopy);
cb.Blit(shadowmap, id);
cb.SetGlobalTexture(“m_ShadowmapCopy”, id);Light m_Light = this.GetComponent<Light>();
m_Light.AddCommandBuffer(LightEvent.AfterShadowMap, cb);
YourCamera.projectionMatrix * YourCamera.worldToCameraMatrix;
float4 near = float4 (vDepth >= _LightSplitsNear); 
float4 far = float4 (vDepth < _LightSplitsFar);
float4 weights = near * far;
float3 shadowCoord0 = mul(unity_WorldToShadow[0], float4(outS.p.xyz, 1.)).xyz; 
float3 shadowCoord1 = mul(unity_WorldToShadow[1], float4(outS.p.xyz, 1.)).xyz;
float3 shadowCoord2 = mul(unity_WorldToShadow[2], float4(outS.p.xyz, 1.)).xyz;
float3 shadowCoord3 = mul(unity_WorldToShadow[3], float4(outS.p.xyz, 1.)).xyz;
float3 coord =
shadowCoord0 * weights.x + // case: Cascaded one
shadowCoord1 * weights.y + // case: Cascaded two
shadowCoord2 * weights.z + // case: Cascaded three
shadowCoord3 * weights.w; // case: Cascaded four
float shadowmask = tex2D(m_ShadowmapCopy, coord.xy).g;
shadowmask = shadowmask < coord.z; 
col *= max(0.3, shadowmask);

Further Readings, References and Thanks

--

--

--

Technical Artist

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Using Backblaze(B2) Service With Python Part 1

Java — Version Evolution

ABC of Mobile Testing for a newbie

Learning to be Curious Again (or why I am changing careers at 50)

Our Investment in Questmate.

Create a Kubernetes cluster with Azure AKS using Terraform

Launching AWS EC2 Instance and Configuring Webserver automatically using Ansible

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Shahriar Shahrabi

Shahriar Shahrabi

Technical Artist

More from Medium

Animator Controller as a Finite State Machine in Unity

Space Cadet VR — Moving the Ship

Made With Unity | 2D Space Shooter Part 5: Spawning Enemies

Performance Tips for Unity — Other Optimizations #3