In a previous article I've written about how to implement a physically based rendering engine. For that engine I was using Signed Distance Fields, Constructive Solid Geometry and Ray Marching. These three components form together a convenient and powerful way to represent and manipulate many 2D/3D geometric shapes, not only for rendering applications. So I thought it would be worth the effort to make out of my previous work a standalone module, easy to reuse in various projects, and of course share it here. Even if there are tons of material about these topics on the web, I couldn't find a ready to use module in C. Now there will be mine, at least.
My module is available for download here, or you can get it from the command line as follow: wget https://baillehachepascal.dev/2023/Data/SdfCsg/sdfcsg.0.2.0.tar.gz, and decompress it using tar xvf sdfcsg.0.2.0.tar.gz. This will create a subdirectory SdfCsg, inside which executing the command make demo should compile and run the demo below (click to play). Note that it automatically download and install locally my X11Display module.
The module consists of two files: sdfcsg.h and sdfcsg.c. To use it, include the header in your project #include "sdfcsg.h", and compile for example as follow gcc -o main main.c sdfcsg.c -O3 -lm.
All the functionalities of the module are available through an opaque structure SdfCsg, which you can create with functions, one per primitive or CSG operator.
A user-defined SDF function can also be used to extend the module with other shapes. The SdfCsg structure also has a void* hook in case the user needs to attach data to shape instances (extra parameters for the user-defined SDF function for example).
To release the memory used by a shape, one can use the dedicated function:
Transformations can be applied to shapes:
Setters and getters allow the user to access and modify shapes' properties after creation.
The SDF of a shape, and its gradient, can then be evaluated with:
And ray marching can be performed with:
The result structure of ray marching looks as follow
The implementation of ray marching is here slightly different from the one introduced in my previous article (marching in world coordinate system instead of local coordinate systems to make things simpler and hopefully more efficient and better handling numerical imprecision problems).
Two functions to find the nearest position guaranteed to be inside/outside a shape from a given position can be conveniently used to handle the undetermination of the insideness or outsideness of the result of ray marching. This is particularly useful when computing reflection, refraction and diffraction.
As an example of use, the basis for a 3d renderer using this module would look like:
The image below gives a basic example of 3D rendering several shapes, with reflection, texturing and fake soft shadow.
Finally the complete header sdfcsg.h:
and body sdfcsg.c:
Edit on 2023/08/27: A very interesting video about how the same idea as ray marching can be used to solve partial derivative equations.
Edit on 2024/06/11: Remove a useless calculation in SdfCsgDistIntersection; Refactor SdfCsgDistEllipsoid for faster calculation and avoid divide by zero; Update version number to 0.2.0.