Chapters

Hide chapters

Metal by Tutorials

Fifth Edition · macOS 26, iOS 26 · Swift 6, Metal 3 · Xcode 26

Section I: Beginning Metal

Section 1: 10 chapters
Show chapters Hide chapters

Section II: Intermediate Metal

Section 2: 8 chapters
Show chapters Hide chapters

Section III: Advanced Metal

Section 3: 8 chapters
Show chapters Hide chapters

13. Shadows
Written by Marius Horga & Caroline Begbie

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

In this chapter, you’ll learn about shadows. A shadow represents the absence of light on a surface. When light shines on an object, it casts a shadow on anything behind it. Adding shadows in a project makes your scene look more realistic and provides a feeling of depth.

Shadow Maps

Shadow maps are textures containing the information you need to create shadows for a scene. Typically, you render the scene from your camera’s location. However, to build a shadow map, you need to render your scene from the light source’s location - in this case, the sun.

A scene render
A scene render

The image on the left shows a render from the camera’s position with the directional light pointing down. The image on the right shows a render from the directional light’s position. The eye shows the camera’s position in the first image.

You’ll do two render passes:

Two render passes are needed
Two render passes are needed

  • First pass: You’ll render from the light’s point of view. Since the sun is directional, you’ll use an orthographic camera rather than a perspective camera. You’re only interested in the depth of objects that the sun can see, so you won’t render a color texture. In this pass, you’ll only render the shadow map as a depth texture. This is a grayscale texture, with the gray value indicating depth. Black is close to the light, and white is farther away.

  • Second pass: You’ll render using the scene camera as usual, but you’ll compare the camera fragment with each shadow map fragment. If the camera fragment’s depth is less than the shadow map fragment at that position, the fragment is in the shadow. The light can see the blue x in the above image, so it isn’t in shadow. This render pass will combine the shadow with the rest of the scene to make a final image.

Why would you need two passes here? Because you’re rendering a depth map from the light’s point of view, and then reading the texture in the main render from the camera’s point of view, you need to finish encoding the first pass before embarking on the second. In addition, the depth render target may have a completely different size from the the main render target.

The Starter Project

➤ In Xcode, open this chapter’s starter project.

Rqo ycuwtey eqp
Cbo cxopmus eky

1. Creating the New Render Pass

➤ In the Render Passes folder, create a new Swift file named ShadowRenderPass.swift, and replace the code with:

2. Declaring and Drawing the Render Pass

➤ In the Renderer folder, open Renderer.swift, and add the new render pass property to Renderer:

3. Setting up the Render Pass Drawing Code

➤ Open ShadowRenderPass.swift, and add the following code to draw(commandBuffer:scene:uniforms:params:):

4. Setting up the Light Camera

During the shadow pass, you’ll render from the point of view of the sun, so you’ll need a new camera and some new shader matrices.

5. Creating the Shader Function

As you may have noticed when you set up the shadow pipeline state object in Pipelines.swift, it references a shader function named vertex_depth, which doesn’t exist yet.

Yo cjikup coj
Xe ktebek viv

DSO psere rufbibi
KXA mxeda nonxune

Bje mmosoq cirm yidws cimxano
Wri xcavaf feky ceqhr rihzato

The Main Pass

Now that you have the shadow map saved to a texture, you need to send it to the main pass to use the texture in lighting calculations in the fragment function.

Xcaxuct ocvis
Tzukums ewsex

Shadow Acne

In the previous image, as the sun rotates, you’ll notice a lot of flickering. This is called shadow acne or surface acne. The surface is self-shadowing because of a lack of float precision where the sampled texel doesn’t match the calculated value.

Wjizebx tidn da okyi
Dhunepl verg yo uxhe

Identifying Problems

Take a look at the previous render, and you’ll see a problem. Actually, there are two problems. A large dark gray area on the plane appears to be in shadow but shouldn’t be.

Ahrxavyasqep xixuja fuu tohko
Ajzzesgimyuy dazoge wee mehzo

Xeubijw kuzoax acs jva cuprube
Kuapicc puxius owt byi cegvopu

Visualizing the Problems

In the Utility folder, DebugCameraFrustum.swift will help you visualize this problem by rendering wireframes for the various camera frustums. When running the app, you can press various keys for debugging purposes:

Pera us cta dgaru ut gabpisx.
Wuqi ig svo bveso od fibwarv.

Zru pbece cafifa qzoknob
Jpe mcoxe xatopo wjevwuw

Clo nelkh sial sehajo
Mfu vuxsj zuaq zuzora

Imyaymvipdujh lqh lku cjeja hoptazol awoi ixy nerjadi
Udwehfresdosv dwh mda jkeqe hazmehix oneu ibz yoggija

Lfu qrasi biqaxi wmecyev'k nuuqbibg kyleta
Rcu xgiqi wexehi xboyqoj'j coisnixl zqtepu

Solving the Problems

➤ In the Cameras folder, open ShadowCamera.swift. This file contains various methods to calculate the corners of the camera frustum. createShadowCamera(using:lightPosition:) creates an orthographic camera that encloses the specified camera.

Kelqz wieq galifu ikfhifik xzohi mawoqe mqujsoz
Koxxy xieg jiyiqu osrvufax yruqo riwiku jkixluh

Xqapby cbowult dgoy ngi modgj lubogu ar loo tefya
Xjusnt dcarafj gzoz rhe wedcf pomato aj joi domcu

Cascaded Shadow Mapping

A better method is cascaded shadow maps which balances performance and shadow depth. In Chapter 8, “Textures”, you learned about mip maps, textures of varying sizes used by the GPU depending on the distance from the camera. Cascaded shadow maps employ a similar idea.

Key Points

  • A shadow map is a render taken from the point of the light casting the shadow.
  • You capture a depth map from the perspective of the light in a first render pass.
  • A second render pass then compares the depth of the rendered fragment with the stored depth map fragment. If the fragment is in shadow, you shade the diffuse color accordingly.
  • The best shadows are where the light view volume exactly encases the scene camera’s frustum. However, you have to know how much of the scene is being captured. If the area is large, shadows will be blocky.
  • Shadows are expensive. A lot of research has gone into rendering shadows, and there are many different methods of improvements and techniques. Cascaded shadow mapping is a common technique.
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2026 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now