Task 3 of 5

One Path Per Pixel, and It Looks Terrible

Here is the whole idea of path tracing. Fire a ray from the eye through a pixel. Where it lands, pick a random new direction and carry on. Multiply the surviving light by each surface's albedo as you go, and when the path finally escapes into the sky, that sky's brightness — scaled by everything it passed through — is your estimate of what the pixel should be.

One estimate. It is unbiased: average enough of them and you get the exact answer, no approximation anywhere. It is also, on its own, nearly worthless — one path is a single sample of an integral over every direction light could possibly have arrived from, and it is wrong by tens of percent.

The bounce itself is one line of geometry. Take a uniformly random point on the unit sphere, add it to the surface normal, and normalize: that gives you a cosine-weighted direction on the hemisphere — more likely to leave straight up than sideways, which is exactly how a matte surface actually scatters. Because the sampling density already matches the physics, no correction factor is needed; the throughput just picks up the albedo and moves on.

right on average, wrong every single time
Goal: finish the bounce — multiply the throughput by the surface's albedo, then send the ray off along normal + random unit vector, normalized.

Requirements

Hint 1 — why add the normal at all

(sx, sy, sz) is a random point on the whole sphere, so half of those directions point into the surface. Adding the unit normal shifts the sphere so it sits tangent to the surface: every direction then points outward, and the density of directions comes out proportional to the cosine of the angle from the normal — which is Lambert's law, for free.

Hint 2 — the six lines
through = through * albedo;
dx = nx + sx;
dy = ny + sy;
dz = nz + sz;
len = Math.sqrt(dx * dx + dy * dy + dz * dz);
dx = dx / len;
dy = dy / len;
dz = dz / len;
Hint 3 — what "right" looks like here

Grainy. Every pixel should be visibly wrong by itself, with soft shadows and the contact between the spheres and the floor only hinted at through the noise. If your image comes out smooth, the bounce is not random and the next task has nothing to average.

Same idea elsewhere

This loop — generate a ray, find the closest hit, sample a new direction, multiply the throughput, repeat — is the core of every production renderer, from Cycles and Arnold to the DXR sample code. The kernels get bigger (materials, textures, lights sampled directly) but the shape is this, and the reason it is on a GPU is the reason it is here: every pixel's path is independent of every other pixel's.

All tasks in Progressive Path Tracing: Noise Melting Into an Image

  1. Dice Every Thread Can Roll
  2. Where the Ray Hits
  3. One Path Per Pixel, and It Looks Terrible
  4. The Buffer That Outlives the Frame
  5. The Noise Falls Like 1/√n

This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.