When I was just a scamp in my 20s I used to ray trace high resolution (320×200) 24-bit colour depth images on my 286. Don’t panic, I had a 8087 math coprocessor in there so it went super fast. A day or two per image.
Now you might very reasonably wonder what renderer you could get in those dark days in the 80s. What sort of interface would you have available? Surely Blender wasn’t around yet, right? AutoCAD 3D maybe?
In those days we cranked out images with POVRay.
POVRay is what is known as a “constructive solid geometry modeller”. Whereas these days (and even a lot of those days) we render meshes of triangles and use a surface normal function to fake the reflection of light rays into curves (yes, a modern rendered sphere is actually a mult-faceted gem and the renderer lies to you about its smoothness), a constructive solid geometry (CSG from now on) modeller uses primitives like spheres, cones, cylinders, boxes, and toruses that are described by their analog functions. So no impure faceted surfaces (unless you want them). Your light ray returns are pure. You are not being lied to.
The difference to the eye of course is uninteresting. But there’s a lot of joy in purity for some nerds, like me. Hell when gaming I don’t even like the d10 because it’s not a Platonic solid.
But what can you do with such appropriately named “primitives”?
Just about anything, as it turns out (though partially because one of your primitives is a bicubic patch but that’s for another time). The reason you can do plenty is because of the “constructive” part. If you know set theory you probably know what’s coming. Because in addition to placing cubes all over the place, you can perform constructive operations on them.
So, for example, you can take the intersection of two objects — what’s left and therefore rendered is the volume that exists only in both shapes. Or the difference: take a sphere and cut chunks out of it with boxes and cylinders. Or the union of course, just gluing them together. With these functions you can do an enormous amount of work before you even get to the tricks of texturing and colouring and finishing. Here’s what I’m working on right now with what is really the same POVRay I used in 1988.

That’s still a work in progress, as I say, but largely complete. Just needs some work on lighting and colouring.
But what, you ask, does the interface look like? Is it at least better than Blender?
Well hell yes it is. While there are third party interfaces that glue on to POVRay (which is super easy as you’ll see in a sec), the input into POVRay is a descriptive language. Like my other love, PostScript, POVRay uses a scene description language: you just type your description of the scene into a text file and then drive the renderer over it. Your image falls out the bottom.
That “just” is a little flippant. Here’s the code for the crew module of that ship:
#declare cabin_xlate = -25; merge { difference { sphere { <0, 0, 0>, 2 scale <2,1,1> translate <cabin_xlate,0,0> } cylinder { <-28,1,1>, <-24,1,1>, 0.3 } cylinder { <-28,-1,1>, <-24,-1,1>, 0.3 } cylinder { <-28,1,-1>, <-24,1,-1>, 0.3 } cylinder { <-28,-1,-1>, <-24,-1,-1>, 0.3 } box { <-23,10,10> <-23.1,-10,-10> } box { <-25,10,10> <-25.2,-10,-10> } box { <-28.5,10,10> <-28.6,-10,-10> } } difference { sphere { <0, 0, 0>, 1.95 scale <2,1,1> translate <cabin_xlate,0,0> } cylinder { <-28,1,1>, <-21,1,1>, 0.3 } cylinder { <-28,-1,1>, <-21,-1,1>, 0.3 } cylinder { <-28,1,-1>, <-21,1,-1>, 0.3 } cylinder { <-28,-1,-1>, <-21,-1,-1>, 0.3 } texture { pigment { Black } } } object { plate translate <cabin_xlate,0,0>} object { plate rotate <90,0,0> translate <cabin_xlate,0,0>} object { ring translate <cabin_xlate,0,0>} cylinder { <-25,0,0>, <-24.8,0,0> 2 } cylinder { <-24,0,0>, <-23.8,0,0> 2 } #declare store_texture = texture { normal { ripples 1 scale 0.2 } pigment { color rgb <0.2,0.15,0.1> } finish { ambient 0 diffuse 0.2 } } #declare fasten_texture = texture { pigment { color rgb <.2,.2,.2> } finish { specular 0.8 roughness 0.001 reflection { 0.4 metallic } } } merge { sphere { <-20, 0, 1>, 1 texture {store_texture} } torus { 1, 0.1 translate <-20,0,1> texture {fasten_texture } } torus { 1, 0.1 translate <-20,0,1> rotate <90,0,0> texture {fasten_texture } } } merge { sphere { <-20, 0, -1>, 1 texture {store_texture} } torus { 1, 0.1 translate <-20,0,-1> texture {fasten_texture } } torus { 1, 0.1 translate <-20,0,-1> rotate <90,0,0> texture {fasten_texture } } } merge { sphere { <-20, 1, 0>, 1 texture {store_texture} } torus { 1, 0.1 translate <-20,1,0> texture {fasten_texture}} torus { 1, 0.1 translate <-20,1,0> rotate <90,0,0> texture {fasten_texture}} } merge { sphere { <-20, -1, 0>, 1 texture {store_texture} } torus { 1, 0.1 translate <-20,-1,0> texture {fasten_texture}} torus { 1, 0.1 translate <-20,-1,0> rotate <90,0,0> texture {fasten_texture}} } merge { cone { <0,0,0> 0.2 <-40,0,0> 0.001 rotate -45*z rotate 90*x translate <-25,0,0> } cone { <0,0,0> 0.2 <-40,0,0> 0.001 rotate -45*z rotate 180*x translate <-25,0,0> } cone { <0,0,0> 0.2 <-40,0,0> 0.001 rotate -45*z rotate 270*x translate <-25,0,0> } cone { <0,0,0> 0.2 <-40,0,0> 0.001 rotate -45*z rotate 0*x translate <-25,0,0> } texture { pigment { color rgb 0 } finish { ambient 0 diffuse 0.2 specular 0.9 roughness 0.0001 reflection { 0.6 } } } } texture { pigment { body_pigment } finish{ ambient 0.0 diffuse 0.3 specular 0.9 roughness 0.001 reflection { metallic 0.4 fresnel 1 } } } translate <-4,0,0> }
Yeah okay that makes my “just” seem like a bit of an over-reach. But besides the nerdy joy I experience writing any kind of code, I adore the precision of this: things go exactly where you want them because you tell the machine exactly where it goes. No nudging of objects in the modeller’s mesh preview. No snapping to grids. Everything goes exactly where you say you want it. It gives me a rush every time.
Now I don’t expect anyone else to get off on this, but consider: this renderer I’m running is essentially the same today as it was 34 years ago. A few little features go in as processing improves (though it hasn’t been updated in some time now) but the renderer is basically complete. I can render a file (if I had a floppy disk drive) from 1989 without change. That’s like getting a WordPerfect file to load (and I was TODAY years old when I learned that WordPerfect actually still exists so bad analogy, Brad).
Anyway, I’m not trying to sell you on this dinosaur but rather explain the little joys I get from using it. The naked code, the purity of concept, the precision, and, of course, the nostalgia.
