3D with raymarching · lesson 1 of 7 · Intermediate · about 5 min

Fake 3D: normals & light

You will build: Shade the disc as a sphere: compute z and the normal, then diffuse light from the given direction.

The picture produced by the shader in the lesson "Fake 3D: normals & light"
The target picture. In the lesson you write the shader that draws it, and a match bar shows how close you are.

Before real 3D, let's fake it — and learn the thing that makes all 3D look 3D: lighting.

A normal is a unit vector pointing straight out of a surface. How bright a surface is depends on how much its normal faces the light: diffuse = max(dot(normal, lightDir), 0.0). (Dot product of two unit vectors = cosine of the angle between them: 1 facing the light, 0 sideways, negative facing away.)

For a sphere of radius R centered at the origin, a point at (x, y) on screen sits at height z = sqrt(R² − x² − y²). The normal at that point is simply normalize(float3(x, y, z)). Pixels outside the sphere get a mask of 0.

The numbers you need

Everything the picture depends on is given, so the challenge is the code, not guessing.

Radius
0.8
Light direction
normalize(float3(0.5, 0.7, 0.6))
Brightness
base * (0.15 + 0.85 * diffuse)
Base color
float3(0.9, 0.45, 0.3)

Try it in your own language

The lesson is written once and shown in every language. Pick yours: Unity (Built-in), Unity (URP), Unreal, Shadertoy / WebGL, OpenGL (LWJGL, raylib), three.js, GameMaker, LÖVE (Lua), Godot, Apple (iOS, macOS), WebGPU / Bevy.

Start coding

Go deeper

Concept explainers with a live example: Fresnel effect.