Perspective and Camera Projection
CS 381 Lecture, Dr. Lawlor
Perspective is a funny thing. Objects that are far away appear smaller than close objects.
Here's an example of what I'm talking about. So I was riding in
the backpack of my enormous Chinese lumberjack friend, "Big" Genbin
Zheng:
No, that's a lie. Genbin is a normal-sized human. It's just
I'd been shrunk by a mysterious ray at Los Alamos National Labs, and
Genbin was smuggling me out.
Ok, that's a lie too. Really, I was mugging for the camera about
30 yards behind Gengbin, which you can see by the shrinking path, and
the angle at which Sameer Kumar is staring at me:
The human brain is really good at reversing the effect of perspective,
and estimating the true size and shape of objects distorted by
it. We're so good at doing this, that actually just drawing
exactly what you see--perspective--is pretty tricky.
The ancient Egyptians never really got perspective, even in their amazing tombs. The ancient Greeks never got perspective, even in their gorgeous vases. The ancient arcade game Qbert didn't use perspective.
Perspective really started taking off in the 1400's in Italy--note how the floor is no longer a line in these images. Hokusi got perspective, although he'd often use the hack (er, idiom) of interposing a layer of mist between foreground and background.
Terminology:
- Isometric projection--no
perspective. Draw stuff by dropping one axis, possibly after a
rotation. Nice because the size of an object doesn't change with
distance (iso-metric == same-measurement).
- Perspective projection, where things get smaller with distance.
In an OpenGL vertex program, an orthographic projection is easy to implement, by just dropping one of the input coordinates:
vec4 v = gl_Vertex;
gl_Position =vec4(v.x,v.y, 0.0,1.0);
General isometric projections are a little trickier. I always do
this by first figuring out where I want the input axes to go onscreen,
and then setting the scale factors so the axes lie in the right place:
vec4 v = gl_Vertex;
gl_Position = vec4(
/* output X_w */ +0.7*v.x +0.0*v.y -0.6*v.z -0.3*v.w,
/* output Y_w */ -0.2*v.x +0.8*v.y -0.3*v.z -0.5*v.w,
/* output Z_w */ 0,
/* output w */ 1.0 );
This makes the input x axis lie at (+0.7,-0.2) onscreen (the scale
factors on v.x), the Y axis lie at (0.0,+0.8) onscreen, the Z axis at
(-0.6,-0.3) onscreen, and the origin at (-0.3,-0.5). Try
it! You can even keep adding axes, to draw 4D or 5D isometric
pictures, but your brain won't actually understand them...
True perspective can only be implemented with a divide
operation--specifically, we divide by the distance from the camera
Z. But be careful! If Z is negative (behind your head), it
just results in negative X_w and Y_w--object behind you are just
mirror-reversed, but still there. OpenGL provides ways to "clip"
out geometry that's too near or far away.
vec4 v = gl_Vertex;
float s=1.0/(v.z+3.0); /* perspective scale factor */
gl_Position =vec4(
/* output X_w */ v.x*s,
/* output Y_w */ v.y*s,
/* output Z_w */ 0,
/* output w */ 1.0 );
To think about: how could you represent the perspective divide with a matrix?