Holes Tutorial Part 1

© Mike Williams 2002,2003

 

The problem with holes

If you ever try to construct an object that has a lot of holes in POVRay, you'll find that it renders incredibly slowly. This page attempts to explain why this happens and suggests something that you might be able to do about it.

First of all, lets see how bad the problem is. Here's a slab with 1600 holes in it. It's constructed as the difference between a box and 1600 cylinders. On my machine it took 26 minutes 25 seconds to render this image, whereas a plain slab without holes renders in 3 seconds, and a union of the 1600 cylinders renders in 4 seconds.
 

1600 cylinders

 

Why is the difference of these two fast things so slow?

We can see some of what's happening if we look at the statistics for the scene.
C:\Program Files\POV-Ray\scenes\MJW\HoleTest.pov Statistics, Resolution 512 x 384
----------------------------------------------------------------------------
Pixels:          196608   Samples:          196608   Smpls/Pxl: 1.00
Rays:            196608   Saved:                 0   Max Level: 1/5
----------------------------------------------------------------------------
Ray->Shape Intersection          Tests       Succeeded  Percentage
----------------------------------------------------------------------------
Box                             176850          107987     61.06
Cone/Cylinder                282960000          148853      0.05
CSG Intersection                176850           94663     53.53
Bounding Box                    168886           98133     58.11
Light Buffer                    268817          120392     44.79
Vista Buffer                    197305          120818     61.23
----------------------------------------------------------------------------
Calls to Noise:                  0   Calls to DNoise:             10
----------------------------------------------------------------------------
Shadow Ray Tests:           149634   Succeeded:                 8444
----------------------------------------------------------------------------
Smallest Alloc:                 26 bytes   Largest:            10284
Peak memory used:          2144877 bytes
----------------------------------------------------------------------------
Time For Parse:    0 hours  0 minutes   2.0 seconds (2 seconds)
Time For Trace:    0 hours 26 minutes  23.0 seconds (1583 seconds)
    Total Time:    0 hours 26 minutes  25.0 seconds (1585 seconds)
The significant line is
Cone/Cylinder                282960000          148853      0.05
This indicates that there's a huge number of cylinder tests being performed. If we divide the number of cylinder tests (282960000) by the number of rays cast (196608) we can see that there are an average of 1439.21 cylinder tests being performed on every ray. In fact, we can see that POVRay performed 176850 CSG intersection tests, and if we divide 282960000 by that number we can see that each CSG test required 1600 cylinder tests.
 
1600 cylinders

If we compare that with the relevant statistics from rendering a union of 1600 cylinders we see this:-

Ray->Shape Intersection          Tests       Succeeded  Percentage
----------------------------------------------------------------------------
Cone/Cylinder                   141522           73966     52.26
Bounding Box                    352932          107854     30.56
Light Buffer                   1834283          576040     31.40
Vista Buffer                   1512813          789580     52.19
The number of cylinder tests per ray is now only 0.72.

What's happening is that with a union of cylinders, POVRay is able to perform some very clever bounding. If creates a bounding box around each cylinder, and knows that it doesn't need to actually test the cylinder if the ray doesn't intersect the bounding box. Furthermore, it creates a second level of bounding boxes around clusters of first level bounding boxes, and it knows that it doesn't have to test each first level box if the ray doesn't intersect the second level bounding box, and so on.
 

So, what can we do about it

What we can do is to change from having one slab with 1600 holes to having a large number of small slabs, each with a few holes.
 
1600 cylinders

In this image I've deliberately pulled the small slabs apart so that you can see what's going on. If I keep the slabs together, then the resulting image is indistinguishable from the original image, but it now only takes 8 seconds to render.

The relevant statistics are:

Ray->Shape Intersection          Tests       Succeeded  Percentage
----------------------------------------------------------------------------
Cone/Cylinder                  2839792           93181      3.28
Bounding Box                    289371          106373     36.76
Light Buffer                   1538201          508758     33.07
Vista Buffer                    922299          461201     50.01
It turns out that the best we can do is to have 1600 slabs each with one hole in each which renders in 5 seconds and gives statistics like this:
Ray->Shape Intersection          Tests       Succeeded  Percentage
----------------------------------------------------------------------------
Cone/Cylinder                   179410           66043     36.81
Bounding Box                    597354          142627     23.88
Light Buffer                   3274980         1093527     33.39
Vista Buffer                   1640353          880769     53.69

Source Code

Download

Here's the scene file that I used to perform these tests. The number of slabs and the number of holes per slab are controlled by the variables at the start of the file.

#declare N = 4;     // Each box has N*N holes
#declare M = 10;    // There are M*M boxes
Setting N=40, M=1 gives the worst case, and N=1, M=40 gives the fastest render.

Other Tricks

Holes Tutorial Part 2: Iso-Holes
Holes Tutorial Part 3: Transparent Holes (Bubbles)
Holes Tutorial Part 4: Blob-Holes
 
Back to Mike's Homepage