CSC418 - Notes
Topic 6: Visibility

Navigation: back up next notes exercises

Key concepts & Readings

OpenGL

Opengl achieves hidden surface removal through the use of a depth buffer and depth testing.

Depth buffering must be enabled with the command glEnable(GL_DEPTH_TEST). Before drawing a scene the depth buffer should be cleared with the command glClear(GL_DEPTH_BUFFER_BIT).

The command glCullFace(GLenum mode) indicates which polygons should be discarded (culled) before they are converted to screen coordinates. The parameter mode is either GL_FRONT, GL_BACK, or GL_FRONT_AND_BACK to indicate front-facing, back-facing, or all polygons. To take effect, the state variable for culling, GL_CULL_FACE must be enabled.

Aside: In OpenGL a polygon has a front and a back side. Each side can be drawn in the same mode or in different modes to allow cut-away views of solid objects for example. The command glPolygonMode(GLenum face, GLenum mode) controls the drawing mode for a polygon's front and back faces. The parameter face can be GL_FRONT, GL_BACK, or GL_FRONT_AND_BACK; mode can be GL_POINT, GL_LINE, or GL_FILL to indicate whether the polygon should be drawn as points, outlined, or filled. By default, both the front and back faces are drawn filled.

glEnable(GL_DEPTH_TEST); /* enable depth testing; required for z-buffer */
...
/* drawing the scene */
glClear(GL_DEPTH_BUFFER_BIT); /* clears the depth buffer */
DrawScene();

Also, to draw the scene with back-face culling enabled (as it is more efficient)

glEnable(GL_DEPTH_TEST); /* enable depth testing; required for z-buffer */
glEnable(GL_CULL_FACE); /* enable polygon face culling */
glCullFace(GL_BACK); /* tell opengl to cull the back face*/
...
/* drawing the scene */
glClear(GL_DEPTH_BUFFER_BIT); /* clears the depth buffer */
DrawScene();

Notes

In a 3 dimensional scene objects closer to the viewpoint may obscure objects further from the viewpoint. To draw a realistic scene, hidden surface removal must be performed to ensure that obscured parts of objects are not visible.


Two objects are intersecting (and occluding) each other

Back-face Culling

Back-face culling directly eliminates polygons not facing the viewer.


Back-Face Culling in VCS

Back-face culling can be performed in either VCS or NDCS. We'll first discuss face-culling in VCS. A first attempt at performing back-face culling might directly use the z-component of the surface normal, as expressed in VCS. This does not always work, however.

A better strategy is to construct the plane equation for the polygon and to test whether the eye-point falls above or below this plane.
Plane(Peye)<0 implies the eyepoint is below the plane containing the polygon and that the polygon should thus be culled.

Summary for VCS culling

Face Culling in NDCS

In NDCS, the z-component of the surface normal does reflect the true visibility, as desired. If the z-component is positive, the normal points away from the eye and the polygon should thus be culled.

Computing Surface Normals

In order to do the face culling, we need a surface normal.


Method 1

Use the cross-product of two polygon edges. The order in which vertices are stored should be consistent. For example, if polygon vertices are stored in CCW order when viewed from above the `front face', then we could use
N = ( P2 - P1 ) x ( P3 - P2 )

Method 2

A more robust method is to use the projected area onto the yz, xz, and yz planes. To see that areas can be used to calculate a normal, first consider the 2D case.


The areas for the required 3D projections (and thus the components of the normal) can be calculated as follows:


Visibility


Visibility algorithms are needed to determine which objects in the scene are obscured by other objects. They are typically classified into two groups:

The Z-buffer

The z-buffer records depth information with each pixel:


Summary of Z-buffer algorithm:

for all i,j {
	  Depth[i,j] = MAX_DEPTH
	  Image[i,j] = BACKGROUND_COLOUR
}
for all polygons P {
	  for all pixels in P {
			if (Z_pixel < Depth[i,j]) {
			  Image[i,j] = C_pixel
			  Depth[i,j] = Z_pixel
			}
	  }
}

Characteristics of the z-buffer algorithm:

When does the z-buffer perform poorly?

Scan-converting Z

Scan conversion proceeds much like the 2D case, only now appropriate Z-values must be generated. In order to determine z = f(x,y), we begin with the plane equation of the polygon in device coordinates as given by

   0 = A x + B y + C z + D,

We can solve for z as

   z = ( - A x - B y - D ) /C.

The value of z at the adjacent pixel (x+1, y) is

   z' = ( - A (x+1) - B y - D ) /C.

or

   z' = z - A/C.

Generating z-values when travelling across a scanline is thus simple once the z-value is known at the left edge of the polygon. This can also be calculated in an incremental fashion, knowing that

   x' = x + 1/m.

The new left edge of the polygon is (x+1/m, y+1) and we can determine the difference in z as being

   z' = z - (A/m + B )/C.

Bilinear interpolation

The A-Buffer

The data for each surface includes:

BSP trees

BSPtree *BSPmaketree(polygon list) {
   choose a polygon as the tree root
   for all other polygons {
      if polygon is in front, add to front list
      if polygon is behind, add to behind list
      else split polygon and add one part to each list
   }  
   BSPtree = BSPcombinetree(BSPmaketree(front list),
                            root,
                            BSPmaketree(behind list) )
}
DrawTree(BSPtree) {
	   if (eye is in front of root) {
			DrawTree(BSPtree->behind)
			DrawPoly(BSPtree->root)
			DrawPoly(BSPtree->front)
	   } else {
			DrawTree(BSPtree->front)
			DrawPoly(BSPtree->root)
			DrawTree(BSPtree->behind)
	   }
}

Depth-sorting Algorithms (Painting Algorithm)

Several object-space algorithms achieve a front-to-back ordering in other ways. These depth-sort algorithms have the following basic steps:

Ambiguities can be resolved in several ways.

If these fail, exchange the order of the surfaces and repeat. If this still fails, the polygons can be intersected to split one of the polygons if necessary. In the worst case, the algorithm can generate O(n^2) new polygons.

Scan-line algorithms

for each scanline (row) in image
	  for each pixel in scanline
		determine closest object
		calculate pixel colour, draw pixel
	  end
end

Exercises

Exercise 1

Consider the following scene

  1. Build a BSP tree for the following scene using the polygons (shown as lines) as cutting planes. You should build
    your BSP tree by inserting the polygons into the BSP tree in alphabetical order. Represent the '+' side of each cutting
    plane as the right child and the '-' side as the left child.
  2. Use your BSP tree to produce a painter's algorithm ordering from the given eyepoint. Show your work.
  3. How will a BSP tree deal with 3 cyclically-overlapping polygons?

Exercise 2

For the following scene, the polygons forming a closed solid are represented by edges. Which faces would be
removed by backface culling?

Exercise 3

Sketch the graphics pipeline and indicate where the following are performed a) clipping b) Gouraud shading c)
Z-buffer computations.

Exercise 4

In Situation A, you render a scene with 10,000 polygons into a 640x480 pixel framebuffer. In Situation B, your
scene has 1000 polygons and your buffer is 4800x2400 pixels. Briey discuss which hidden surface removal methods
would be efcient for each scene, justifying your decision in terms of the distinction between image space and object
space.

Exercise 5

A Z-buffer algorithm will draw a given pixel if its Z value is less than the Z-value currently stored in the Z-buffer for that pixel, i.e., Z_pixel <= Z_buf[x,y]. Will the Z-buffer always produce the same image regardless of the way in which the primitives (polygons) are drawn?