A Polygon-Free Ray Tracer: Rendering Bicubic Bézier Surfaces in Clojure
Introduction
This work was partly inspired by a blog post by Alexandre Grison?(see?Grison's Blog Post),?in which he explains how to build a raytracer in Clojure.?In March 2020,?during the COVID lockdown when I had to spend my vacation at home without the possibility of traveling,?I dedicated a week to coding in Clojure.?After reading Grison’s post,?I recalled my own project on rendering bicubic Bézier splines that I began in 1999 as a high school student and completed in 2003 as a university student.?Originally written in C++,?the code was later rewritten in Clojure in 2020.
For demonstration purposes,?this system employs classic objects from computer graphics such as the Utah Teapot?(see?Wikipedia: Utah Teapot)?and teacups.?I also used datapoints for the teapot and teacup models from?Ryan Holmes’s website,?for which I express my gratitude.
I intentionally avoided including Clojure code examples in this article.?For readers who are not familiar with Clojure,?the detailed code might be challenging.?However,?those who know Clojure can follow the link to the?GitHub repository?to read the full source code.?Fortunately,?Clojure encourages writing very compact code,?and the rendering method presented here is both concise and simple to grasp.
This project is primarily educational and was undertaken simply because I enjoy exploring these techniques.?This article describes a ray-tracing system implemented in Clojure that renders a scene without using any polygons.?Instead,?it uses smooth,?mathematically defined?Bézier patches?combined with spatial data structures to achieve high visual fidelity.?The discussion focuses on two main phases:?first,?the construction of a spatial?quadtree?for the Bézier patches,?and second,?the rendering phase in which the system computes the intersection between a ray and a bicubic Bézier surface using iterative refinement via the?Newton-Raphson method.
Result of the Rendering
Rendering this image on my MacBook took almost 95 hours?—?roughly four days.
Overview of the Rendering Pipeline
For this project,?I forked the?Clojure2D-examples repository?(I would like to express my sincere gratitude to the authors for making such a valuable resource available).?Since the original engine only rendered spheres,?I enhanced it by adding support for objects made of bicubic Bézier splines.?I kept only one file—ch12_random_scene.clojure;?this file manages the entire rendering pipeline,?which includes:
Detailed Explanation of Spatial Quadtree Building
The spatial acceleration structure is built specifically for Bézier patches.?I use a quadtree that recursively subdivides a Bézier patch into smaller regions,?each represented by a bounding sphere.?This approach reduces the number of detailed intersection tests during rendering.
Normalization and Tree Construction
Before subdivision,?the control points that define a Bézier patch are normalized relative to a computed center and radius.?Each control point is translated?(by subtracting the center)?and scaled?(by dividing by the radius).?This step simplifies subsequent calculations and improves numerical stability.
Recursive Subdivision and Bounding Sphere Aggregation
The tree-building process follows these steps:
领英推荐
Detailed Method of Bézier Surface and Ray Intersection
The intersection process between a ray and a Bézier surface involves two main stages:?an initial estimation using the spatial quadtree and an iterative refinement using the Newton-Raphson method.
Initial Intersection Estimation
Newton-Raphson Iterative Refinement
Once an approximate intersection is obtained,?I refine it using the Newton-Raphson method:
By combining the coarse filtering of the quadtree with the fine-tuning of the Newton-Raphson method,?the system efficiently computes precise intersections between rays and bicubic Bézier surfaces.
The Uniqueness of a Polygon-Free Scene
This ray-tracing system is unique because it renders an entire scene with?zero polygons—achieving mathematical precision with continuous Bézier patches,?ensuring data efficiency through compact representations,?and delivering superior visual fidelity with perfectly smooth curves and no edge seams or shading discontinuities.
Future Works and Alternative Intersection Methods
While the current implementation uses the Newton-Raphson method for refining ray-surface intersections,?alternative methods—such as Broyden's,?the Secant,?Halley's,?Gauss-Newton,?and Levenberg-Marquardt methods—could be explored as potential replacements that might be more suitable and efficient in this context.?Additionally,?the presented solution could potentially be transformed into a series of matrix operations that would parallelize well on GPU architectures,?opening up the possibility for real-time ray tracing with the same mathematical precision and visual fidelity.
Conclusion
This Clojure-based ray-tracing system demonstrates advanced graphics programming by rendering a scene with zero polygons using smooth Bézier patches.?The process is divided into two main phases:?first,?the construction of a spatial quadtree that recursively subdivides a Bézier patch into smaller regions?(each represented by a bounding sphere)?to quickly eliminate regions that a ray does not intersect;?and second,?the rendering phase,?where the system computes the intersection between a ray and a bicubic Bézier surface using an initial estimation followed by iterative refinement via the Newton-Raphson method.?Together,?these techniques enable high-precision intersection tests while maintaining efficiency.
The combination of these approaches results in an efficient and accurate ray tracer,?paving the way for further innovations in rendering technology.
Programmer | Software Architect | Early-stage startup enthusiast
4 天前???? My current contract is ending, and I’m looking for a new opportunity as a Clojure developer. I’d be happy to share my CV upon request.