FINISHED: Arduino Raytracer

Raytracers make a decent benchmark for measuring CPU and floating point performance: a lot of home computers from the 80s had raytracing software, so why not the humble Arduino? Well, it's not just a benchmark for processors - it's a good one for developers too, as a somewhat typical toy program to learn more about recursion... and feature creep : )

Here is a raytracer which handles purely ray-triangle intersection from a fixed camera viewpoint and one light source. It outputs square images, using scene data compiled-in as a .h file (and stored in PROGMEM). Each face may have an attached material, allowing a custom setting of ambient, diffuse, transparency and reflection per surface.

The heart of this raytracer is the Moller-Trumbore algorithm, which was invented by a couple guys far smarter than me! It does ray-triangle intersection, returning true if the two collide (as well as u-v coordinates, surface normal, and distance - all important for material calculation). A Google search will turn up plenty of resources to find out more about it.

All math is done using "double" floating point data types (on Arduino, double and float are the same). This means that there is a LOT of software floating-point calculation, which is grindingly slow. One improvement would be to convert to 16.16 fixed-point, but I'm already at the fringe of what should be done on a microcontroller and don't feel like sinking more time into this.

The output feeds back to the PC pixel-by-pixel over serial. Once the image is complete, the Arduino goes into an infinite loop. Copy-paste the output from serial monitor into a .ppm file and open in a graphics editor.

I actually wrote this as a standalone PC application first, and then converted the bits to Arduino. It can be compiled for either platform by using a shim arduino.h file which fakes things like millis(), Serial.print() and a couple other things.

Code is attached, and some test images to follow. Fun modifications to this would be texturing (procedural based on u-v, or just raster), other non-triangle primitives, fixed-point calculations, multiple light sources, ... Well, fun for you maybe. Me, I'll just use POVRay instead. (11.7 KB)

Attached is a (64x64) sample image rendered by the raytracer running on the Arduino. Two triangles, one in front of the other, along with a light source. The foreground triangle is semi-translucent.

Rendering this on the Arduino took 11176 milliseconds (11.176 seconds). My PC does the same in something like 8 ms, which means to build an Arduino render farm on par with a laptop you'd need roughly 1,500 Arduinos linked together.

EDIT: Here is the same scene as rendered by my PC, at 640x640. And flipped vertically : )


A more complicated scene this time. 505 vertices and 901 faces, with reflective surfaces (4 bounce limit). In fact, this would be a good opportunity for u-v calculated materials: the checkerboard floor in this scene is made of white and black triangles, while a "shader" to do the same would need only two triangles, setting the color based on uv position.

More primitives would help too. The icospheres are 20-sided, but a real sphere primitive would be much more accurate.

This 640x640 image comes from the PC, and took 263 seconds to complete.

The Arduino worked overnight on the same scene and didn't complete - at the same rate as before, it'd need to run for 4.26 days : ) A 64x64 version is rendering now.

impressive work

impressive work

Thanks! The math and recursion wasn't so much of a problem as I was able to work out most of this on the PC using traditional dev tools. In fact the quirks of PROGMEM were what gave me the most issue on the Arduino port!

Ok, finally, here is the detail_scene at 64x64 from the Arduino.

This render took 4008.471 seconds to do 1/100 the work of the PC. Once again, a difference of ~1500x or so.


See also: Arduino raytracer - Exhibition / Gallery - Arduino Forum


So tried the cornell box and tft screen

Great stuff.