← Back to articles
The process of building a raytracer in C
2022-08-15 · 3 min read
This is in the process of being written, so check back on it often for updates! (I mean, it literally says the process of in the title...)
I borrowed a book called The Raytracer Challenge recently and have decided to step up to the challenge. I'm going to be building a raytracer... in C! Well, I originally wanted to build it in Python, but I haven't played around with C for a while, so we'll see how it goes.
The premise of the book is to write tests while you're writing the raytracer code, which is, dare I say it, kind of enjoyable? The book only provides pseudocode, which I think is a nice change from some of the other programming books I've read.
Anyways, one of the features to write was a virtual canvas implementation that would translate to a .ppm file. In other words, I was going to use a struct
containing a multidimensional array.
If you've programmed with C before, you know how well that goes. Especially when you're testing out different methods for writing the structure. You're basically guaranteed to get a segmentation fault at least fifteen times before you figure out the problem.
Eventually, I decided to simply implement a one-dimensional array using what C calls a flexible array member. Apparently, something like this in a struct is legal in C99:
typedef struct canvas {
int width;
int height;
color *pixels[];
}
You don't have to define the array length! And later you can just allocate the right memory size and start using it like a normal array:
canvas *newcanvas(int width, int height)
{
canvas *c = malloc(sizeof(canvas) + sizeof(color * [width * height]));
c->width = width;
c->height = height;
for (int i = 0; i < width * height; i++)
{
c->pixels[i] = newcolor(0, 0, 0);
}
return c;
}
Freeing it contains a bit more work since pixels
is an array of pointers:
void freecanvas(canvas *c)
{
for (int i = 0; i < c->width * c->height; i++)
{
free(c->pixels[i]);
}
free(c);
}
(Did I forget to mention that I totally forgot about freeing memory until I had written a small chunk of the program? Thank goodness that I remembered that valgrind
actually exists.)
I'm not used to allocating and freeing memory, so it's certainly been a nice brain exercise.
Struggling with strings has certainly been a problem as I continue to write the canvas implementation. I haven't used functions like sprintf
in a long time, so I did do some deeper digging (I almost wrote digger deeping while typing this up; sometime I feel like I have focus issues because I jump all over the place like I am doing now, but hey! this is a fun article) and learned more about some other similar functions, such as asprintf
, which was ten times more useful, as it prints to a string without needing a predetermined memory size being given to it.
Then I realized that asprintf
was only available on UNIX-based systems, so I wouldn't be able to compile the program on Windows. Now, I want this to be cross-compatible, so I had to rewrite a function that I have previously used asprintf
for - canvas_to_ppm
.
The final function: