Mimsy Were the Borogoves

Hacks: Articles about programming in Python, Perl, Swift, BASIC, and whatever else I happen to feel like hacking at.

Photo-editing with Persistence of Vision

Jerry Stratton, September 18, 2019

Raisin-Coconut Torte

There are more languages for scripting than are included in the current version of macOS, and it may very well be that in the future even the currently-installed languages will need to be installed. At the moment, the best way to install new languages and tools for the command-line is Homebrew

When I wrote 42 Astounding Scripts I deliberately left out languages that are not included with macOS. But if I had decided to relax that requirement, the language I would have relaxed it for would have been the Persistence of Vision Raytracer.

The easiest way to install it is probably using brew.

  • $ brew install povray

This installs the latest version of Persistence of Vision available for macOS. The command is povray. I have two tutorials for using POVRay online already; the one I used to use in classes at the University is Simple Photorealism using Persistence of Vision. There’s also Persistence of Text which contains several smaller tutorials.

The povray command, unfortunately, does not support a shebang line, nor does it support accessing arguments on the command-line. This means that you cannot create standalone script files as you can with Perl or Python. You usually end up hardcoding the options into the file. For example, suppose you want to overlay a flock of brightly-colored transparent spheres in front of a photo.

[toggle code]

  • // Jerry Stratton
  • // AstoundingScripts.com
  • #version 3.7;
  • global_settings {
    • assumed_gamma 1.0
  • }
  • #declare aspectRatio = image_width/image_height;
  • #include "screen.inc"
  • light_source {
    • <20, 35, -2>
    • color rgb <1, 1, 1>
  • }
  • Set_Camera_Location(<0, 0, -10>)
  • Set_Camera_Look_At(<0, 0, 0>)
  • //place the photo at the exact point in the background to fill the camera
  • #declare backgroundPhoto = texture {
    • pigment {
      • image_map {
        • jpeg "RaisinCoconutTorte.jpg"
      • }
    • }
    • finish {
      • ambient 1
      • diffuse 0
    • }
  • }
  • Screen_Plane(backgroundPhoto, 10000, <0,0>, <1,1>)
  • //random seed
  • #declare bubbleSeed = now*24*60*60;
  • //known seeds
  • //#declare bubbleSeed = 291893797;
  • //#declare bubbleSeed = 2147483648;
  • #warning concat("BUBBLE SEED IS ", str(bubbleSeed, 1, 0))
  • //generate a random number from -limit to limit, centered on zero
  • #declare randomSeed = seed(bubbleSeed);
  • #macro randomNumber(limit)
    • //rand generates a random number from 0 to 1
    • //subtracting .5 makes it -.5 to .5
    • //multiplying by 2*limit makes it -limit to +limit
    • (rand(randomSeed)-.5)*2*limit
  • #end
  • //counter is the number of spheres to place over the image
  • #declare counter=20;
  • #while (counter > 0)
    • #declare sphereLocation=<randomNumber(8)*aspectRatio, randomNumber(6), randomNumber(2)+8>;
    • #declare sphereColor = rgb <rand(randomSeed), rand(randomSeed), rand(randomSeed)>;
    • #declare sphereTransparency = randomNumber(.5)+.5;
    • #declare sphereRadius = randomNumber(.5)+1;
    • sphere {
      • sphereLocation
      • sphereRadius
      • pigment {
        • color sphereColor
        • filter sphereTransparency
      • }
      • //give it a shiny finish
      • finish {
        • ambient .2
        • diffuse .6
        • specular .75
        • roughness .001
        • reflection { .5 }
        • irid {
          • 0.35
          • thickness .5
          • turbulence .5
        • }
      • }
    • }
    • #declare counter = counter - 1;
  • #end

Save this as “bubblecake.pov”, download the cake photo here (JPEG Image, 238.7 KB) (or use a photo of your own) and use the command:

POVRay bubble cake 2147483648

Bubbles using random seed 2147483648.

  • povray bubblecake.pov Width=1200 Height=900

This will put twenty spheres on top of the photo.

Height and Width on the command line set the dimensions of the output image. They should be in the same ratio as the photo you’re using. This photo of a Raisin-Coconut torte from The Southern Living Fondue and Buffet Cookbook, for example, is 1600x1200. That’s a 4:3 ratio, wider than it is tall. 1200x900 is also a 4:3 ratio.

Persistence of Vision specifies locations in a 3-dimensional space of x (horizontal axis) y (vertical axis) and z (forward/backward axis). The virtual camera of this image is set to be right in the middle (0) of left/right and up/down, with a Z of -10, while looking at <0, 0, 0>. This means that it is sitting at <0, 0, -10> looking straight down the forward/backward axis toward that axis’s positive end.

There’s a light source up (at 35), right (at 20), and at -2 Z, which puts it up and to the right of the camera but between the camera and what the camera is looking at: -2 is between 0 (what the camera is looking at) and -10 (where the camera is).

The spheres are all located between -8 and 8 horizontally; -6 and 6 vertically; and 6 to 10 front/back. This puts them well beyond where the camera’s looking but obviously in the line of sight. The camera’s at -10, looking at 0, so that +6 to +10 further down that line.

Rather than have to hard-code into the scene file exactly where each of the spheres goes, the scene file uses a sort of random number generator. POV handles random numbers differently than most other languages, because consistency is essential when creating scenes. You provide the random number generator with a seed, an integer from 0 to 2,147,483,648. It then produces random numbers from that seed, and the random numbers will always be produced in the same order. This ensures that you can trust the scene not to change between renderings. Otherwise, we’d see a nice set of spheres over the image and go to render a higher quality version of the image, and they’d all be different again.

A great way to find a seed number is to use the password script from 42 Astoundingly Useful Scripts and Automations for the Macintosh.

  • $ password --numbers 9
  • 291893797

And then keep trying until you find a seed number that produces an image you like.

To try different seeds in this scene file, comment out the current bubbleSeed declaration by putting two slashes in front of it. Then, remove the two slashes from the declaration you want to use or add an entirely new #declare line.

If you want the spheres to be different every time you render the image, the common way to do it is by using POV’s “now” keyword. This produces the number of days, with fractions out to at least a second, since the start of January 1, 2000. Multiply it by 24 to get the random number to change every hour; by 24 * 60 to get the random number to change every minute; and by 24 * 60 * 60, as in the commented-out portion of the scene file, to get the random number to change every second.

That’s how this scene file works by default. It also outputs the seed number immediately after setting it, so that if you see a scene you like, you can reproduce it with that seed number. Just add it to the list of known seeds in the seed file.

As you can see from the scene file, Persistence of Vision supports some of the same standard scripting commands that other scripting languages do. In this case, the script—sorry, scene file—uses a while loop to create twenty bubbles at random locations and with random colors and sizes.1 Everything the #while and the #end will be repeated until counter drops down to zero.

Persistence of Vision lets you set the pigment and finish of any object. The pigment is basically the color; in this case, we set a random color with a random filter, which is a form of transparency. The finish is basically how the color interacts with the light and reflections in the area.

Colors in POV consist of red, green, and blue components; each component can range from zero to one. A hundred percent red, fifty percent green, and no blue, for example, would be color rgb <1, .5, 0>.

If you enjoy this, go through one of my tutorials, or one of the many tutorials for POVRay on the Internet. It really is a lot of fun to play with.

  1. It should really use a FOR loop, but I find WHILE loops easier to follow in povray.

  1. <- Apple 1 kit
  2. Merry Christmas! ->