COL781: Computer Graphics
4. Perspective
Projection
1.5
Last class 1.0
0.5
• Linear and af ne transformations
• Points and vectors -1.5 -1.0 -0.5 0.5 1.0 1.5
• Bases and coordinate frames -0.5
• Homogeneous coordinates -1.0
-1.5
fi
Last class
Given unit vectors u and v, nd a way to construct a rotation matrix R which maps u to v,
i.e. Ru = v. Is it unique, or are there many different such rotations?
Hint: What happens to u R=?
vectors other than u?
fi
Last class
• To draw a transformed polygon, I can just transform the vertices.
• To draw a shape speci ed by a function f (x,y) ≤ 0, I can just test each pixel (x,y).
f (x,y) = x2 + y2 − r2
How can I draw a transformed version of a shape speci ed by a function?
fi
fi
Hierarchical transformations
hip
chest
head
left upper arm
left lower arm
left hand
right upper arm
right lower arm
right hand
left upper leg
…
…
Hierarchical transformations
hip
chest
head
left upper arm
left lower arm
left hand
right upper arm
right lower arm
right hand
left upper leg
…
…
Each part’s transformation is relative to its parent
Given point in left hand frame,
Vec3 point_in_world
= hip_to_world * chest_to_hip
* luarm_to_chest * llarm_to_luarm
* lhand_to_llarm * point_in_lhand
Or simply…
Mat3x3 lhand_to_world
= hip_to_world * chest_to_hip
* luarm_to_chest * llarm_to_luarm
* lhand_to_llarm
Vec3 point_in_world
= lhand_to_world * point_in_lhand
Mat3x3 lhand_to_world
= hip_to_world * chest_to_hip
hip * luarm_to_chest * llarm_to_luarm
chest * lhand_to_llarm
head Mat3x3 lhand_to_world
left upper arm = llarm_to_world * lhand_to_llarm
left lower arm
left hand
right upper arm Going down the tree:
Current matrix = current matrix · child-to-parent matrix
right lower arm
Draw child with current matrix
right hand
Recursively draw its children
left upper leg
… Going back up: Revert to parent’s matrix
…
Efficient implementation: keep ancestors’ matrices in a
stack
hip
chest
head drawHierarchy(object):
left upper arm push stack.top() · object.matrix onto stack
left lower arm draw object.shape transformed by stack.top()
left hand for each child in object.children:
drawHierarchy(child)
right upper arm
pop matrix from stack
right lower arm
right hand At each step, stack contains object-to-world matrix of
left upper leg all ancestors, with current object’s matrix on top
…
…
Scene graph Scene
Usually the entire scene is represented Camera Car Lights ⋯
as a tree / DAG!
Nodes may contain geometry or other content Door Wheel
Edges contain transformations
Why a DAG and not a tree? So we can reuse the same
geometry multiple times: instancing
So far we know:
• How to draw 2D shapes
• How to transform 2D and 3D shapes
Now: How to draw 3D shapes on a 2D screen?
Parallel projection
Easy way: Just drop one of the coordinates lol
• Useful for engineering drawings
• Doesn’t match how eyes and cameras
actually see things!
Angel & Shreiner, Interactive Computer Graphics
Choose a view volume [l, r] × [b, t] × [ f, n]
for the region of the scene to be viewed
3
Map to canonical view volume [−1,1]
(1,1,1)
Then map canonical view volume to
1 1 1 1
image coordinates [− , w+ ] × [− , h+ ]
2 2 2 2
(−1,−1,−1)
• Keep z-coordinates as-is
(we will need them later for visibility testing)
(0,0) w
Then, we can just rasterize our triangles in 2D!
h (w,h)
Perspective
Jeff Lynch
Algorithmic drawing
in the 1500s
A point is drawn where the ray from
the viewpoint meets the image plane.
Albrecht Dürer
Pinhole camera model
3D object
camera / eye / etc.
sensor / retina aperture
/ pupil
Scene point
y p = (y,z)
Assume camera is at the origin, pointing in
the direction −z.
q = (y′,d) Where is the point p projected to?
d −z
y y′
=
z d
yd
Image plane y′ =
z


What if the camera is not at the origin and/or not looking along −z?
Just change to a coordinate system in which it is.
Viewing transformation
Usually, user specifies:
• center of projection c
• target point t or view direction v = (t−c)/‖t−c‖
• “up vector” u
Construct orthonormal basis
e2 = (v×u)/‖v×u‖
e1 = v×e2
e3 = −v
Camera space
Object space
World space
Camera → world: M = [e1 e2 e3 c]
World → camera: M−1
[yd/z]
xd/z
Once point is in camera space, projected point =
Canon EF Lens Work III
16 mm (110°)
Up close and zoomed wide
with short focal length Ren Ng
Walk back and zoom in
200 mm (12°) with long focal length
Ren Ng
(ℓ,t)
(r,t)
Choosing an angle of view = choosing a rectangle
[l, r] × [b, t] in the projection plane
(ℓ,b)
• Usually centered: l = − r, b = − t (r,b)
• To preserve aspect ratio: w/h = r/t
Then
Coordinates after
θ perspective division
t = | d | tan
2
The view frustum
In practice, we will also limit z to a finite
range [f, n]
View volume is now a cone cut off at near
and far “clipping planes”: view frustum
Why?
• Exclude objects behind the camera
• Finite precision of depth coordinate (we’ll
see why next class)
Angel & Shreiner, Interactive Computer Graphics
Marschner & Shirley
If we can somehow transform the view frustum into a box,
we can then reuse the machinery of parallel projection!
2.0
Homogeneous coordinates revisited 1.5
1.0
x x
[1 ] [0 ]
Recall points vs. vectors: p = y , v = y p
0.5
v
Let’s generalize: points can have any w ≠ 0
-1.5 -1.0 -0.5 0.5 1.0 1.5 2.0
-0.5
x
[ w]
-1.0 p̂ = y with w ≠ 0
Any point in homogeneous coordinates
corresponds to the 2D point p = (x/w, y/w)
-1.5
The main idea: Points in 2D correspond to lines through the
origin in 3D!
cx
[c]
All points p̂ = cy on a line represent the same point p = (x, y)
where the line meets the plane w = 1
Analogy: Various tuples (2,4), (−1,−2), (5,10), … all represent the
same rational number ½
Linear and affine transformations still work as before!
[Worked example on whiteboard…]
What can we do with this new freedom?
Allow any (n+1)×(n+1) matrix and still make sense of the results.
ax + by + cw
a b c x ax + by + cw gx + hy + iw
[w]
d e f y = dx + ey + fw ∼ dx + ey + fw
g h i gx + hy + iw gx + hy + iw
1
This is called a projective transformation or a homography.
• Preserves straight lines
• Does not preserve parallelism 2 0 −1
M= 0 3 0
2 1
0 3 3
Perspective projection: (x,y,z) → (xn/z, yn/z)
Scene point
x,y p = (x,y,z) With a projective transformation:
x x xn/z
y y yn/z
z → z ~
q = (x′,y′,n) n
1 z/n 1
n −z
1 0 0 0
0 1 0 0
Corresponding matrix:
0 0 1 0
Image plane
0 0 1/n 0
Hang on, we’ve lost depth information:
all points end up on the near plane z = n.
To retain depth, let’s copy (a multiple of) w into the z-coordinate:
x x xn/z
y y yn/z
z → ~
−1/n −1/z
1 z/n 1
1 0 0 0 z values are nonlinearly transformed!
0 1 0 0
Matrix:
0 0 0 −1/n • Not possible to preserve lines while keeping z linear.
0 0 1/n 0
• Many systems use a slightly different matrix where
points on near plane z = n are unchanged
Marschner & Shirley, Fundamentals of Computer Graphics
z=f perspective
transformation
z=n
(r,t,n)
2n l+r
r−l
0 l−r
0
(ℓ,b,n)
2n b+t
affine 0 t−b b−t
0
M=
transformation f+n 2fn
0 0 n−f f−n
M
(1,1,1)
0 0 1 0
Canonical
view volume
(−1,−1,−1)
• Modeling transformation: object space → world space
• Camera transformation: world space → camera space
• Projection transformation: camera space → orthographic view volume
→ canonical view volume
• Viewport transformation: canonical view volume → screen space
All of these are now 4×4 matrices! In the end: divide by w
Clipping
Keenan Crane
• Discard triangles outside view frustum… especially those behind the camera!
• Subdivide triangles partially intersecting view frustum
Usually implemented in homogeneous coordinates (before division)
for each of six planes:
if all vertices are outside plane:
discard triangle
else if some vertices are outside plane:
clip triangle against plane
if result is a quadrilateral:
break into two triangles
To clip a triangle against a plane:
Marschner and Shirley
see e.g. Marschner & Shirley 12.4.3, Hughes et al. 36.5.2
Visibility a.k.a. hidden surface removal
Which surfaces are visible? Those that are not hidden by nearer surfaces.
Polygons drawn without Correct result
considering depth / visibility
LearnOpenGL.com
LearnOpenGL.com
Homework: Do it yourself
Draw a wireframe cube!
Start with vertices at (±1,±1,±1),
translate somewhere along −z,
apply some rotation,
compute the projected points,
and join them with edges.
(Use any plotting library e.g. matplotlib,
or look up how to draw points and lines in SDL2)