Inspiration

The most realistic modern method for rendering 3D images is raytracing. The core of raytracing comes down to the exact same question repeated millions of times: if we shoot out a ray for each pixel in the scene, what part of the 3D scene does it hit first? If you can answer that quickly and reliably, you can create shadows, reflections, and realistic lighting effects. Because custom chips can take months to come back from fabrication, we wanted to build something that’s not just fast, but safe, robust, and carefully verified.

What it does

Our project is a small Application-Specific Integrated Circuit (ASIC) that accelerates 3D rendering. In real life, rays of light emanate from a light source, many of these rays never actually reach the observer/camera. From a computation perspective, this is wasteful since many rays are completely ignored. For raytracing, we instead shoot rays out of the observer/camera, then compute what each ray hits first in the scene. That “first hit” tells us what surface a pixel is looking at, which lets us assign a color and lighting to that pixel. Our ASIC speeds up this core step by quickly determining whether a ray hits a surface, where that hit occurs, and which side of the surface was hit, so software can reliably turn the results into a final rendered image. In the end we are able to take any 3D model stored as a .stl and create a rendering with multiple movable light sources and realistic shadows.

How we built it

On the software preprocessing side, we start with a standard 3D model and convert it into a simple 3D scene representation that marks which locations are solid or empty. We then generate one camera ray per pixel based on the camera position and image resolution, and convert each ray into a compact “job” format that the ASIC can process efficiently. This step packages the ray into the initial state the hardware needs to traverse the scene.

On the hardware side, our ASIC contains a scene-loading interface that writes the preprocessed scene data into on-chip memory, and a job interface that accepts ray jobs using a valid/ready handshake. A control finite state machine orchestrates traversal, repeatedly stepping through the scene and stopping under clear termination conditions: a hit is found, the ray leaves the scene bounds, or a maximum step count is reached. The core traversal logic is implemented as a pipelined datapath that performs the per-step computations, including selecting which direction to step, updating the traversal state, checking bounds, and reading occupancy from memory.

On the rendering side, the software collects the ASIC’s per-ray results and converts them into pixel colors to form the final image. For rays that hit a surface, we use the reported hit location to look up a base color and use the reported face information to infer a surface direction for simple lighting. For rays that miss or time out, we assign a background or debug color. The resulting pixel buffer is then written out as an image file.

Challenges we ran into

One of our first challenges was developing a solid conceptual understanding of ray tracing itself. At the start, we assumed ray tracing meant simulating light by shooting rays outward from the light source, but we quickly learned that this is not the case. It took time (and a few wrong turns) to fully internalize the key idea of shooting rays from the observer/camera outward into the scene.

A second major challenge was implementing shadows in a way that fit our pipeline. Once a camera ray hits a surface, producing a realistic shadow requires an additional test: we need to shoot a new ray from the hit position toward the light source to check whether something blocks the light. This extra “shadow ray” layer adds complexity because it introduces additional rays per pixel and more logic to combine the results into final shading.

Finally, verification was a major challenge throughout the project. Because hardware designs can’t be easily patched after fabrication, we needed a high level of confidence that our design was correct across both common cases and rare edge cases. Building strong verification meant writing self-checking testbenches, creating directed tests for tricky corner cases, and running large randomized tests so that failures could be debugged and fixed reliably.

Accomplishments that we're proud of

Built a complete end-to-end system that integrates our ASIC with rendering software to produce a final image.

Created a user-friendly GUI that lets us render and compare different 3D scenes and camera setups quickly during demos.

Added realistic shadows by casting additional rays from hit points toward the light source to test whether light is blocked.

Extended the renderer to support multiple light sources, improving realism and making the demo more visually compelling.

Achieved a smooth, repeatable demo workflow where scene loading, ray submission, and image output run reliably from start to finish.

What we learned

Verification is not something you add at the end. For a hardware chip, a missed bug is much harder to fix later than it is in software.

Some geometric edge cases happen rarely, but they still matter. Tie situations are a good example, so we test them directly instead of hoping they show up by chance.

A strong hardware demo is more than showing one successful run. The best demos are repeatable, measurable, and supported by automated tests.

What's next for ASIC Ray Tracing Accelerator

Implement mirrored surfaces by shooting additional reflection rays after a hit and combining the returned results into reflective shading.

Support higher-resolution scenes beyond the current 32×32×32 limit, including updating memory/storage formats and scaling traversal to handle larger grids efficiently.

Built With

  • cocotb
  • cognichip
  • python
  • systemverilog
Share this project:

Updates