A Painter's Algorithm


The painter's algorithm, sometimes called depth-sorting, gets its name from the process which an artist renders a scene using oil paints. First, the artist will paint the background colors of the sky and ground. Next, the most distant objects are painted, then the nearer objects, and so forth. Note that oil paints are basically opaque, thus each sequential layer completely obscures the layer that its covers.

A very similar technique can be used for rendering objects in a three-dimensional scene. First, the list of surfaces are sorted according to their distance from the viewpoint. The objects are then painted from back-to-front.

While this algorithm seems simple there are many subtleties. The first issue is which depth-value do you sort by? In general a primitive is not entirely at a single depth. Therefore, we must choose some point on the primitive to sort by. There are two popular choices:

  1. Sort by the minimum depth extent of the polygon
  2. Sort by the maximum depth extent of the polygon
  3. Sort by the polygon's centriod (Sum(vi, i = 1..N)/N)

The algorthim can be implemented very easily. First we extend the drawable interface so that any object that might be drawn is capable of suppling a z value for sorting.


    import Raster;

    public abstract interface Drawable {
        public abstract void Draw(Raster r);
        public abstract float zCentroid();
    } 
Next, we add the required method to our triangle routine:

    public final float zCentroid()
    {
        return (1f/3f) * (vlist[v[0]].z + vlist[v[1]].z + vlist[v[2]].z);
    } 
Then we implement the painter's algorithm.

    //
    // Use QuickSort to order the vertices from near to far
    //
    private void sort(int lo0, int hi0) {
	    int lo = lo0;
	    int hi = hi0;
	    if (lo >= hi)
	        return;
	    float mid = triList[(lo + hi) / 2].zCentroid();
	    while (lo < hi) {
	        while ((lo < hi) && (triList[lo].zCentroid() < mid)) {
		        lo++;
	        }
	        while ((lo < hi) && (triList[hi].zCentroid() >= mid)) {
		        hi--;
	        }
	        if (lo < hi) {
		        FlatTri T = (FlatTri) triList[lo];
		        triList[lo] = triList[hi];
		        triList[hi] = T;
	        }
	    }
	    if (hi < lo) {
	        int T = hi;
	        hi = lo;
	        lo = T;
	    }
	    sort(lo0, lo);
	    sort((lo == lo0) ? lo + 1 : lo, hi0);
    }

    void DrawScene()
    {
        view.transform(vertList, tranList, vertices);
        ((FlatTri) triList[0]).setVertexList(tranList);
        raster.fill(getBackground());
        sort(0, triangles-1);
        for (int i = triangles-1; i >= 0; i--) {
            triList[i].Draw(raster);
        }
    }
Here's an example:
The painter's algorthim works great... unless one of the following happens:

Big triangles and little triangles

This problem can usually be resolved using further tests (i.e. considering the bounding boxes as well). Suggest some.

Another problem occurs when the triangle from a model inter-penetrate as shown below.

This problem is a lot more difficult to handle. generally it requires that primitive be subdivided (which requires clipping).

Usually the painter algorithm is used only when the database does not include inter-penetrating primitives. Examples of this type of models include meshes and height-fields.


This page last modified on Wednesday, November 06, 1996