Portfolio Header
Priyanshu Samal
Profile Picture
Back
1,780

How a Simple Ray Tracer in C Works: From Math to Shadows

At the heart of ray tracing lies a deceptively simple idea: light travels in straight lines until something blocks it.

2026-01-08·9 min read
How a Simple Ray Tracer in C Works: From Math to Shadows

At the heart of ray tracing lies a deceptively simple idea: light travels in straight lines until something blocks it. Everything else—shadows, brightness, darkness—is a consequence of that one rule. In this project, the entire visual result emerges from explicitly simulating that rule in code, without relying on the GPU, shaders, or any high-level graphics abstraction.

Implicit Geometry

The scene contains only two objects: a Sun, which emits light, and an Earth, which can block that light. Both are represented as circles in 2D space, defined only by a center point and a radius. There is no mesh, no texture, no triangle data. Geometry is implicit, meaning an object exists wherever a mathematical condition is true. For a circle, that condition is derived directly from the Euclidean distance formula. Any point whose distance from the center is less than or equal to the radius lies inside the object.

Generating Rays

Light emission begins at the Sun. Instead of guessing where shadows might be, the program explicitly sends rays outward from the Sun in all directions. Each ray is defined mathematically using a parametric equation: a starting point and a direction. The starting point is the Sun’s center, and the direction is computed using cosine and sine of an angle. By sweeping the angle from 0 to 2π, the program generates rays that uniformly cover all directions around the Sun. Each ray represents a possible path that light could take.

Ray Marching

Once a ray’s direction is known, the program advances it forward step by step using a technique called ray marching. At each step, the ray’s position is calculated using the equation position = origin + direction × distance. This is pure vector math, no graphics tricks involved. The distance parameter increases gradually, meaning the ray walks through space in small increments, sampling the environment as it goes. This stepping approach is not the most efficient way to trace rays, but it makes the logic extremely transparent and easy to reason about.

Intersection Testing

At every step along the ray, the program performs two critical checks.

  • Out of Bounds: First, it verifies whether the ray has left the screen. If the ray goes outside the visible region, there is no point in continuing; the ray escapes into space and is discarded.
  • 2. Collision: Second and more importantly, the program checks whether the ray has entered the Earth. This is done by computing the distance between the ray’s current position and the Earth’s center. If that distance is less than the Earth’s radius, the ray has intersected the Earth.

    The Nature of Shadows

    When a ray intersects the Earth, it is immediately terminated. This single decision is what creates shadows. There is no code that explicitly draws darkness behind the Earth. Instead, darkness appears naturally because no ray ever reaches that region. Light does not get removed after the fact, it simply never arrives. This distinction is fundamental to ray tracing and is why shadows emerge automatically without any special casing.

    If the ray is neither out of bounds nor blocked by the Earth, it is allowed to continue, and its current position is written to the framebuffer. This is purely for visualization. In a physically based ray tracer, rays are invisible and are used only to compute lighting values. Here, drawing the ray itself allows us to see the geometry of light transport directly, making the math tangible and intuitive.

    Conclusion

    What makes this approach “real” ray tracing is not performance or visual polish, but correctness of reasoning. Rays originate from a source, move through space, test intersections against geometry, and terminate on collision. These are the same principles used in production ray tracers and modern GPU ray tracing systems. The difference lies in scale and optimization. GPUs trace billions of rays in parallel, use analytic intersection equations instead of stepping, and accelerate queries using spatial data structures like BVHs. But conceptually, they answer the same question this program does: does a ray hit something before it reaches its destination?

    View Demo on X (Twitter)View Source Code on GitHub

    TechCGraphicsRay TracingMath
          |\__/,|   (`\
        _.|o o  |_   ) )
    -(((---(((--------
    ~ git commit -m "bye"_