Fixing Rendering Artifacts

Introduction: Subtle Problems with Big Impact

In the previous lesson, you built a recursive path tracer that simulates realistic light transport through Lambertian diffuse materials. Your ray tracer can now render matte surfaces with natural shading, and light bounces between objects, creating subtle color bleeding effects. This was a major achievement, transforming your simple ray tracer into something that simulates real-world physics. However, if you look closely at your rendered images, you might notice two issues that detract from the overall realism.

The first issue is subtle but annoying once you notice it. You might see small dark speckles or patterns scattered across surfaces that should be smoothly shaded. These artifacts look like noise or dirt on the surface, and they appear even in areas that should be uniformly lit. This problem is called shadow acne, and it's caused by floating-point precision errors in our ray-surface intersection calculations. The second issue is more obvious: your images probably look darker than they should. The bright areas aren't as bright as you'd expect, and the overall image feels muddy or underexposed. This happens because we're not accounting for how human vision perceives brightness and how display devices expect color values to be encoded.

These might seem like minor technical details, but they have a significant impact on the final image quality. Shadow acne breaks the illusion of smooth surfaces and makes your renders look amateurish. Incorrect brightness perception makes your carefully calculated lighting look wrong, wasting all the effort you put into simulating realistic light transport. In this lesson, we'll fix both of these problems with relatively simple solutions that will dramatically improve your rendered images. By the end, you'll understand why these artifacts occur and how to prevent them, giving your ray tracer the polish it needs to produce truly professional-looking results.

Understanding Shadow Acne

Shadow acne manifests as small dark spots or speckled patterns on surfaces, particularly noticeable on large, flat areas that should be smoothly shaded. The name comes from its similarity to the shadow mapping artifact in real-time graphics, though the underlying cause in ray tracing is slightly different. When you render a scene with diffuse materials, you might see these dark speckles scattered randomly across your spheres or ground plane. They look like the surface is dirty or has some kind of texture, but they're actually calculation errors.

The root cause of shadow acne in ray tracing is floating-point precision errors during ray-surface intersection tests. Here's what happens: when a ray hits a surface and bounces off, we calculate a new ray starting from the hit point. In theory, this new ray should start exactly on the surface and head off in the scatter direction. However, due to the limited precision of floating-point numbers, the calculated hit point might not be exactly on the mathematical surface. It might be slightly above or slightly below the true surface position.

When the hit point is calculated to be slightly below the surface, the scattered ray starts inside the object. As this ray travels outward, it immediately intersects the same surface it just bounced from. The ray tracer thinks it hit a different part of the surface and calculates another bounce, but this is a false intersection. The ray didn't actually travel anywhere meaningful. It just detected the same surface again due to numerical error. This false self-intersection causes the ray to lose energy incorrectly, creating a dark spot in the final image.

To understand why this happens, we need to think about how floating-point arithmetic works. When we calculate the intersection point using the ray equation, we're performing operations like multiplication and addition on numbers that have limited precision. A typical double-precision floating-point number has about fifteen to seventeen decimal digits of precision. This sounds like a lot, but when we're calculating positions in three-dimensional space and comparing distances, small errors accumulate. The difference between the calculated hit point and the true mathematical surface might be on the order of 1e-15 or 1e-16, which is tiny in absolute terms but large enough to cause the hit point to be on the wrong side of the surface.

Think of it like trying to measure something with a ruler that's slightly bent. If you're measuring the length of a table and your ruler has a tiny curve in it, you might get a measurement that's off by a millimeter or two. That error seems small, but if you're trying to determine whether a point is exactly on a line, that millimeter matters. In our case, the "bent ruler" is the limited precision of floating-point arithmetic, and the "millimeter" is the tiny error that puts our hit point on the wrong side of the surface.

The randomness of shadow acne comes from the fact that these precision errors vary depending on the exact numbers involved in the calculation. Different rays hit the surface at different points and angles, leading to different amounts of numerical error. Some rays might have their hit points calculated slightly above the surface (no problem), while others have hit points slightly below (causing false intersections). This creates the characteristic speckled appearance where some pixels are correct and others are too dark.

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal