Wednesday, September 02, 2009

OpenGL Matrix Tricks: Camera Properties

In my last post I pointed out that:
  • Planes have vectors "embedded" in them (the normal vector).
  • We can quite cheaply backward-transform a plane, because planes are transformed by a transposed inverted matrix - if we want to go "backward" the two matrix inversions cancel each other out.
Knowing this, we can play some fun camera tricks using OpenGL's model-view and projection matrices.

Finding The Clip Planes

To cull a sphere, we can transform the sphere's center into eye space, then test it's distance to each of the 6 viewing clip planes (in eye space). If we are outside the clip planes by more than the sphere radius, it is out of camera view.

(We don't want to do this in clip space because clip-space's scale is non-uniform - comparing a distance to the radius of the sphere would be very wrong.)

But where do we get our culling planes? Well, the clip planes in clip space are very simple, because clip space is just a 2-unit cube centered on the origin. The clip planes are therefore:
  • Left clip: (1,0,0,1) - that is 1x + 0y + 0z + 1 = 0, also known as X + 1 = 0.
  • Right clip: (-1,0,0,1)
  • Bottom clip: (0,1,0,1)
etc. etc.

So all we need to do is multiply these planes by the transposed projection matrix and we have our clip planes not in clip space but in eye space. In practice, we can "multiply out" the transpose and the multiply and get a code snippet that looks like this.

Basically the cost of finding our clip planes in eye space from the projection matrix is just a bunch of adds and subtracts. Cheap! I should also note that this works for any projection matrix - ortho, frustum, oblique frustum, or Dr. Demento's totally insane deformed matrix.

Finding Billboard Vectors

A billboard is a quad whose points are entirely within the XY plane in eye space. That is, it always faces the camera. To calculate a billboard, we need to find vectors in model-view space that map to "right" and "up" in eye space.

It turns out that this is a lot like the clip-plane problem above. Consider the YZ plane, facing "right" (that is, a plane whose plane coefficients are 1,0,0,0). We can transform that from eye to model-view space, then recover our normal vector and that's which way "right" is.

Once again we're taking a few static plane equations, running them through a transposed matrix, and out comes our answers. Here's a code snippet.

Another Way To Look At Billboarding

It turns out there's an even easier way to understand how the billboarding example works than transforming plane equations by a transposed matrix.

An affine transformation matrix consists of rows of basis vectors - that is, each row of the matrix is the destination X axis (then Y, then Z) in the source coordinate system.

So if we want to know which way "right" is in eye space (but we want our numbers in model-view space) we just take the top row of the 3x3 upper-left part of the model-view matrix.

Where Is My Camera Location?

Sadly you can't get the camera location in model-view space out of the model-view matrix. Our previous trick of reverse-transforming a vector or plane fails because the origin is a point - it needs to be "forward" transformed, which from eye space to model view space means having a real inverted model-view matrix. (Once you have that, the right-most column is the origin.)

The problem is that the origin, as embedded in the model-view matrix (in the right-most column) is the origin after rotations. This is generally not what we want.

(I suppose that, theoretically, we could invert the 3x3 upper left of the model view matrix, then apply this to the right-most column to find our origin, and I suppose this would be faster than calculating a full inverse matrix. I haven't tried this though - in X-Plane we simply invert the model view matrix as needed.)


  1. Inverting left upper 3x3 and transforming origin is actually a standard procedure for inverting affine transform matrices (view matrix transform is affine) - you transpose left upper 3x3 part (for an affine transform transpose and inverse are the same) and transform origin.

  2. Right - if I understand correctly this should work nicely for most sanely built model view matrices, but not for a perspective projection matrix or a modelview-projection matrix with projection.