PERSPECTIVE
If you recall, the column vector (x y z w)T represents the point (x/w,y/w,z/w) in space. This all makes perfect sense if w=1. But as w approaches 0, everything seems to blow up. What's really happening is that we get the point at infinity (x y z 0)T, which just another way of saying the direction vector (x,y,z).
This now makes sense of the last row of a transformation matrix. The (0 0 0 1) along the bottom of the matrix expresses the fact that the first three columns, which represent the transformed X,Y and Z axes, refers to direction vectors, whereas the last column, which represents the transformed origin, refers to a point in space.
Of course the 16 numbers of the matrix can be anything at all, and the result is still a linear transformation. In particular, you can have something other than (0 0 0 1) as the last row. This means the X, Y or Z axis could be transformed not to another direction but to a point. Similarly, the origin could be transformed not to a point, but to a direction.
This is what a perspective transformation does: a point in space can become a direction toward infinity, and a direction can become a point.
We can use this to implement a perspective camera. In general, we want perspective so make things smaller as they get further away from a camera point. To make things easier, we can just think of the camera lens as a pinhole - we only see those light rays that pass through that pinhole.
Probably the most useful set-up is to place the camera pinhole along the z-axis at some positive z, looking into negative z. One way to describe this is to say that the "focal length" of the camera is f, where f < 0. Then we can use the perspective matrix:
This matrix transforms (x y z 1)T to (x y 1/f z/f+1)T
1 0 0 0 0 1 0 0 0 0 0 1/f 0 0 1/f 1
We can multiply through by f to get (fx fy 1 z+f)T
Then we can divide through by the last coordinate to get:
x → f x / (z+f)In practice, you don't really need to do the matrix multiplication. You can just transform the x and y coordinates as per the above equations, just before applying your viewport transform.
y → f y / (z+f)
z → 1 / (z+f)
NESTED GEOMETRY
For the last assignment, your API for a Geometry object looked something like:
public class Geometry { public Matrix matrix; public int faces[][]; public double vertices[][]; }For this week's assignment I would like you to extend Geometry objects so that they can contain recursively nested sub-objects. Each sub-object should be positioned relative to its parent object.
This will involve extending your API for a Geometry object to something like this:
public class Geometry { public Matrix matrix; public int faces[][]; public double vertices[][]; public Geometry child[]; // ARRAY OF NESTED CHILDREN public int nChildren; // CURRENT NUMBER OF CHILD OBJECTS }You'll probably want a method to add children, with an implementation something like:
... public Geometry add(Geometry src) { child[nChildren++] = src; return this; } ...When you render each frame, each child object should end up positioned relative to its parent object. This means that you should be traversing the objects in your scene as a tree structure. I suggest that you maintain a "world" object as the root of this tree, and that you build your scene by adding top-level objects as children to this world object.
During the render step, you might want to maintain a working
matrix within each object called globalMatrix
which contains the actual position of that object in the scene,
the result of premultiplying the object's matrix
by the globalMatrix
of its parent.
Then when you actually render the object,
you can use this globalMatrix
to
transform the object's vertices.
HOMEWORK DUE Wednesday February 20
As we said in class, the assignment is to make a cool scene that shows off the use of nested geometry with internally moving parts. Anything that has internal relative movement is cool, including cars with rotating wheels, human figures, cows and sheep, helicopters, clocks, or anything else you can think of. Have fun with it.
You should also apply perspective to the view, as per the beginning of these notes. A reasonable value to try for f is something like f=3.0, but you should play with it until you like the result.