Graphics Programming Virtual Meetup

Discord

Twitter

Tiny Renderer

Lesson 4-5

Perspective projection

and Moving the Camera

Tutorial link:
https://github.com/ssloy/tinyrenderer/wiki/Lesson-4-Perspective-projection


My Code:
https://github.com/cdgiessen/TinyRenderer

Lesson 4:
Perspective Projection

Start with 2D

Linear Transformations

matrix vector multiplication

\begin{bmatrix} a & b \\ c & d \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} = \begin{bmatrix} ax & by \\ cz & dy \end{bmatrix}
\begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix} \begin{bmatrix} 1 \\ y \end{bmatrix} = \begin{bmatrix} x \\ y \end{bmatrix}

"Identity' matrix does not change anything

Altering the diagonal alters the scale

\begin {bmatrix} 3/2 & 0 \\ 0 & 3/2 \end{bmatrix} \begin {bmatrix} x \\ y \end{bmatrix} = \begin {bmatrix} 3/2x \\ 3/2y \end {bmatrix}

Non-diagonal non-zero values create "sheer"

\begin {bmatrix} 1 & 1/3 \\ 0 & 1 \end {bmatrix} \begin {bmatrix} x \\ y \end{bmatrix} = \begin {bmatrix} x + y/3 \\ y \end{bmatrix}

multiple sheers combined together create rotation

A rotation matrix can be written directly using trigonometry

\begin {bmatrix} cos(\alpha) & -sin(\alpha) \\ sin(\alpha) & cos(\alpha) \end{bmatrix} \begin {bmatrix} x \\ y \end{bmatrix} = \begin {bmatrix} xcos(\alpha) - ysin(\alpha) \\ xsin(\alpha) + ycos(\alpha) \end{bmatrix}

Note: Matrix multiplication is not communicative

2D Affine transformations

\begin{bmatrix} a & b \\ c & d \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} + \begin{bmatrix} e \\ f \end{bmatrix} = \begin{bmatrix} ax + by + e \\ cx + dy + f \end{bmatrix}

Scale & rotation do not change the origin

Translation can be achieved by simple addition

But this doesn't combine well, each translation must be stored separately from the rotation and scale

\begin{bmatrix} a_2 & b_2 \\ c_2 & d_2 \end{bmatrix} \begin{pmatrix} \begin{bmatrix} a_1 & b_1 \\ c_1 & d_1 \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} + \begin{bmatrix} e_1 \\ f_1 \end{bmatrix} \end{pmatrix} + \begin{bmatrix} e_2 \\ f_2 \end{bmatrix}

Homogeneous coordinates

Instead of using 2x2 matrices, use 3x3, but make the last row (0, 0, 1)


Then make any 2d vector a 3d vector but with the last element set to 1

\begin{bmatrix} a & b & e \\ c & d & f \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} = \begin{bmatrix} ax + by + e \\ cx + dy + f \\ 1 \end{bmatrix}

This works because we embed 2D into 3D

 

Our 2D space is on the plane z=1 in the 3D space

 

We perform a linear 3D transformation then project it onto the 2D plane

 

Projecting 3D onto 2D is simple, divide by the 3D component

\begin{bmatrix} x \\ y \\ z \end{bmatrix} \rightarrow \begin{bmatrix} x/z \\ y/z \end{bmatrix}

Composing transformations

We want to rotate something other than the origin
First, translate it to the origin before rotating, then translate it back

\begin{bmatrix} 1 & 0 & x_0 \\ 0 & 1 & y_0 \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} cos(\alpha) & -sin(\alpha) & 0 \\ sin(\alpha) & cos(\alpha) & 0 \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} 1 & 0 & -x_0 \\ 0 & 1 & -y_0 \\ 0 & 0 & 1 \end{bmatrix}

Otherwise its a rotation about point (x, y)

What happens if we don't have (0,0,1) in the bottom row?

\begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ -1/5 & 0 & 1 \end{bmatrix}

Remember the y-buffer exercise from the previous chapter

 

We will do the same thing but projecting onto a vertical line

Picture a camera at point (5, 0)

 

To project it, trace lines from the camera to the points of our shape

 

Then project these onto the 'screen', a vertical column

Now replace the object with the transformed one, not touching the yellow lines

Now let us make it

\begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & r & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} = \begin{bmatrix} x \\ y \\ z \\ rz + 1 \end{bmatrix}

Do the same thing as 2D but add another dimention

Retro-projection for 4D onto 3D is:

\begin{bmatrix} x \\ y \\ z \\ rz + 1 \end{bmatrix} \rightarrow \begin{bmatrix} \dfrac{x}{rz + 1} \\ \dfrac{y}{rz + 1} \\ \dfrac{z}{rz + 1} \end{bmatrix}

3D illustration of previous 2D projection

x^\prime = \dfrac{x}{1 - z/c}
y^\prime = \dfrac{y}{1 - z/c}

X & Y's change when projecting from the 3d space onto the 2d plane (z = 0)

Z isn't altered, it doesn't appear on a 2D image output

Lesson 5:
Moving the camera

Change of basis

\overrightarrow{OP} = \overrightarrow{i}x + \overrightarrow{j}y + \overrightarrow{k}z = \begin{bmatrix} \overrightarrow{i} & \overrightarrow{j} & \overrightarrow{k} \end{bmatrix} \begin{bmatrix} x \\ y \\ y \end{bmatrix}

Point P in basis O

Now to transform P into a point in O'

\begin{bmatrix} \overrightarrow{i^\prime} & \overrightarrow{j^\prime} & \overrightarrow{k^\prime}\end{bmatrix} = \begin{bmatrix} \overrightarrow{i} & \overrightarrow{j} & \overrightarrow{k}\end{bmatrix} \times M
\overrightarrow{OP} = \overrightarrow{OO^\prime} + \overrightarrow{O^\prime P} = \begin{bmatrix} \vec{i} & \vec{j} & \vec{k} \end{bmatrix} \begin{bmatrix} O^\prime_x \\ O^\prime_y \\ O^\prime_z \end{bmatrix} = \begin{bmatrix} \vec{i^\prime} & \vec{j^\prime} & \vec{k^\prime} \end{bmatrix} \begin{bmatrix} x^\prime \\ y^\prime \\ z^\prime \end{bmatrix}

Substitute (i', j', k') with M

\overrightarrow{OP} = \overrightarrow{OO^\prime} + \overrightarrow{O^\prime P} = \begin{bmatrix} \vec{i} & \vec{j} & \vec{k} \end{bmatrix} \begin{pmatrix} \begin{bmatrix} O^\prime_x \\ O^\prime_y \\ O^\prime_z \end{bmatrix} + M \begin{bmatrix} x^\prime \\ y^\prime \\ z^\prime \end{bmatrix} \end{pmatrix}

And then we can change coordinates basis

\begin{bmatrix} x \\ y \\ z \end{bmatrix} = \begin{bmatrix} O^\prime_x \\ O^\prime_y \\ O^\prime_z \end{bmatrix} + M \begin{bmatrix} x^\prime \\ y^\prime \\ z^\prime \end{bmatrix} \implies \begin{bmatrix} x^\prime \\ y^\prime \\ z^\prime \end{bmatrix} = M^-1 \begin{pmatrix} \begin{bmatrix} x \\ y \\ z \end{bmatrix} - \begin{bmatrix} O^\prime_x \\ O^\prime_y \\ O^\prime_z \end{bmatrix} \end{pmatrix}

Previous lessons had the camera only located on the Z axis

We want to draw the scene so that the 'eye' could be at any point

Look at function

void lookat(Vec3f eye, Vec3f center, Vec3f up) {
    Vec3f z = (eye-center).normalize();
    Vec3f x = cross(up,z).normalize();
    Vec3f y = cross(z,x).normalize();
    Matrix Minv = Matrix::identity();
    Matrix Tr   = Matrix::identity();
    for (int i=0; i<3; i++) {
        Minv[0][i] = x[i];
        Minv[1][i] = y[i];
        Minv[2][i] = z[i];
        Tr[i][3] = -center[i];
    }
    ModelView = Minv*Tr;
}

Viewport

screen_coords[j] = Vec2i((v.x+1.)*width/2.,(v.y+1.)*height/2.);

Turns a [-1, 1] square into coordinates [0, width], [0, height]

Rewrite in a matrix

Matrix viewport(int x, int y, int w, int h) {
    Matrix m = Matrix::identity(4);
    m[0][3] = x+w/2.f;
    m[1][3] = y+h/2.f;
    m[2][3] = depth/2.f;

    m[0][0] = w/2.f;
    m[1][1] = h/2.f;
    m[2][2] = depth/2.f;
    return m;
}
\begin{bmatrix} w/2 & 0 & 0 & x + w/2 \\ 0 & h/2 & 0 & y + h/2 \\ 0 & 0 & d/2 & d/2 \\ 0 & 0 & 0 & 1 \end{bmatrix}

Combine the matrices all together

Viewport * Projection * View * Model * v

Vec3f v = model->vert(face[j]);
screen_coords[j] =  Vec3f(ViewPort*Projection*ModelView*Matrix(v));

Model is the 'location' of the object we are drawing
Currently (0,0,0)

Result

Transformation of normal vectors

Can't scale normal vectors non-uniformly
Causes noticeable distortion

 

 

Not necessary for current lesson, but will need to be tackled for proper lighting.

Solution is to multiply the normals by the inverse transpose of the transformation matrix

Graphics Programming Virtual Meetup

Tiny Renderer Lesson 4-5

By Charles Giessen

Tiny Renderer Lesson 4-5

  • 274