Introduction
There are three main disciplines of graphics: modeling shape and appearance, animation, and rendering.
Modeling
Raster (bitmap) graphics have a fixed array of pixels with values, and a fixed resolution. Vector graphics are driven by display commands. It has a scalable resolution, which makes it ideal for typography and animation.
Rendering
The main question rendering aims to answer is: how do we make a 3D picture appear on a 2D plane?
There are two ways to think about the problem:
- For every point on the paper, determine which object appears at the point
- For every object in the scene, determine which points on the paper it covers
These two approaches correspond to two major classes of rendering algorithms: raytracing and rasterization.

Raster Graphics
Images and Pixels
An image is a 2D grid of cells called pixels, where each pixel holds some set of values. A pixel (picture + element) is a unit with some kind of value. A pixel value can represent intensity (grayscale), color (RGB), opacity, or depth.
In the grayscale image below, each pixel requires only one value to encode its intensity. By convention, 0 is the lowest intensity (black) and 1 is the highest intensity (white).

In a RGBA image, each pixel has four distinct values, which each represents red, green, blue, and alpha. Each value ranges from 0 to 255.

An RGB value of (255, 0, 0) gives us red, (0, 255, 0) gives us green, and (0, 0, 255) gives us blue. Surprisingly, an RGB value of (255, 255, 255) gives us pure white. This is because we are working with light from the computer screen rather than pigment from paint. This gives us additive color rather than subtractive color.
Datatypes and Memory
Notice that intensity is represented with a float64, while color is represented with a uint8. The reason we use uint8 for RGB images is that float64 takes up a lot of storage. If we used floats for RGBA images, each pixel would require 16 bytes, and a 1000 x 1000 image would take up 16 MB of memory.
Most displays today use 24-bit true color, which is 8 bits per color channel. Since each color channel has distinct values, we generate million different RGB colors. The human eye can discriminate only around 10 million colors, so this is generally sufficient. However, humans are fairly good at noticing uneven changes in color in color banding, which can be undesirable.
There are multiple ways to store images in memory:
- Array of structs (AoS), where each struct represents an RGB value
- Struct of arrays (SoA), where each array represents a R, G, or B channel Different memory organizations affect cache locality.
It is also common to store 2D arrays of data in 1D data structures. There are two common ways of doing this: (1) row-major ordering and (2) column-major ordering.

References:
Vector Graphics
Pipeline
To create a 2D vector graphic:
- Define object geometry
- Transform object (translate, rotate, scale)
- Define "window" on the world
- Render into raster graphics
In each of the steps of this pipeline, there are many different coordinate systems:
- Object space is the local coordinate system in which a single object is defined
- World space is the global coordinate system for the scene
- Camera space is the coordinate system defined by the camera through which the scene is viewed
Why use multiple coordinate systems?
We want to keep different layers modular, so we can modify each independently. For example, we may want to scale or rotate individual objects. If all objects are defined in terms of world space, then there may not be a linear matrix transformation to do this.
Also, at higher levels of abstraction, we don't want to think about specific device coordinates to draw to. We may have to later consider the resolution of the device (normalized device coordinates) and the viewport of the OS window that the application runs in (screen space).
Transformations
Vectors
A vector transforms a scalar into another scalar. Below, we transform the scalar by shifting it units right and units up.
The dot product is a scalar representation of how similar two vectors are to each other.
Geometric interpretations of the dot product:
- is the squared length of the vector
- when the vectors are perpendicular
- , where is the angle between the arrows
The cross product takes in two vectors and produces a vector that is mutually perpendicular to both vectors. By convention, the direction that points in is determined by the right-hand rule.
Geometric interpretations of the cross-product:
- also gives the area of the parallelogram with and as edges. Half of this area gives us the triangle with and as edges.
Matrices
The identity matrix is a matrix such that for all matrices . The 2x2 identity matrix is:
If we have two matrices and such that , then we say that is the inverse of . Not all matrices have an inverse. For a 2x2 matrix, we have:
Transformations
A transformation is a function that acts on space.
- A linear transformation respects the property
- A matrix transformation is any transformation that can be written in terms of multiplying a matrix and a vector
- Every linear transformation is a matrix transformation
Note that matrix transformations can compose. For example, applying then to vector is equivalent to .
We can build matrix transformations using basis vectors. To find a matrix that sends:
We need to find a matrix that satisfies:
Arithmetically, this matrix is
To reverse the transformation, we can simply calculate the inverse of .
Thus, to send
We can simply calculate the matrix that sends
And the matrix that sends
And then calculate .
Scale, Rotate, and Translate
Both scale and rotate are linear transformations and can be represented by matrix transformations.
-
To scale by in the x-axis and scale by in the y-axis, apply the matrix:
-
To rotate by counterclockwise, apply the matrix:
Translation is not a linear transformation; it is an affine transformation. In order to represent it as a matrix transformation, we add another dimension to it. This is called homogeneous coordinates.
- is if the value is a scalars and if the value is a vector
- does not affect subtraction of two scalars (which produces a vector), addition of a vector and a scalar (which produces a scalar), or dot product (which is mostly between two vectors)
- also does not affect the linearity of scaling and rotation
To translate in the x-axis and in the y-axis, apply the matrix:
Algorithmically, this works to out be:
Note that the order of the transformations matters. The most widely used ordering is TRS, which means to scale, rotate, and then translate.
References:
Non-Axis Aligned 3D Rotation
Scaling and translation in 3D is similar to that in 2D. Rotations are more complicated because we can now rotate around an arbitrary axis. There are several ways to do this: Euler angles, axis-angles, and quaternions.
Euler angles are a composition of rotations around the x, y, and z axes.
The rotation matrices for each axis, by angle , are:
A full Euler angle rotation is the composition , applied right to left.
There are several problems with Euler angles. First, they don't trace the shortest rotational path because they don't follow the geodesic. To fix that, we can linearly interpolate each angle independently with some correction factor. However, this does not produce an even angular velocity. Also, when two rotation axes become parallel, we lose a degree of freedom, leading to odd behavior at the point.
Axis-angle rotations involve decomposing a point into two parts relative to the axis :
- is the part parallel to ; rotation does not change it
- is the part perpendicular to ; it rotates like a 2D point in a plain perpendicular to
To calculate it, we need:
Which can be expanded to:
The formula for the cross product can be represented as a matrix, so this entire equation can be rewritten in matrix form. Unfortunately, axis-angle rotations also do not sweep the shortest path between two points.
Quaternions are the only rotation method that can correctly do linear interpolation. A quaternion represents a rotation as a point on the surface of a 4D hypersphere. Because we are moving along the sphere, we get shortest path and constant angular velocity.
References: Visualizing quaternions
Views
View Volume
A view volume contains everything the camera sees
- A conical volume approximates what the eyes can see
- A frustrum is a rectangular pyramid which approximates the conical view volume
- A parallel volume doesn't stimulate the eye or camera
The 3D is projected onto the view plane sitting in world space. It should be perpendicular and centered around the look vector. It should also be located somewhere between the eye and the scene.
The parameters of the view volume are:
- Position
- Look vector
- Orientation (often represented by up vector)
- Aspect ratio
- View angle
A view angle determines the amount of perspective distortion in the picture. It is equivalent to a photographer choosing a specific type of lens (e.g. wide-angle or telephoto lens). In a frustrum, there is both a height angle and a width angle.

Camera Space
The position, look vector, and up vector are enough to define the camera coordinate system. The equivalent of , , and world coordinate axes in camera space are unit vectors , , and .
- is in the opposite direction of the look vector
- is the component of the up vector that is perpendicular to the look vector
- is the vector perpendicular to both and

Having a camera coordinate system allows us to change the orientation of the camera:
- Roll: rotate camera around look
- Yaw: rotate camera around
- Pitch: rotate camera around

The canonical camera space is one where the camera sits at the origin, looks down at , and is oriented such that is up. The view matrix transforms objects from world space into camera space.

Suppose that the camera is in position in world space. The view matrix is made from a translation that moves the world by , followed by a rotation that aligns , , with , , .
Note that a matrix with columns [, , ] transforms the , , axes to , , directions. The inverse of does the opposite. As it turns out, because is a orthonormal matrix, the inverse of is just its transpose.
By putting the rotation and translation together, we get one matrix for transforming other objects into the camera's coordinate system.
Note that the view matrix does translation first and then rotation. This is because the matrix that transforms the camera to world space is composed of a rotation first and then a translation. The view matrix is the inverse of this matrix, so we must reverse the order of multiplication.
Scenes
Graphics applications typically represent scenes with a DAG which stores:
- Objects
- Attributes (color, texture)
- Transformations
To get the cumulative transformation matrix of an object, simply multiple the transforms along the path from the object node to the root. For example, to transform Object 1 to world space, first apply transformation A then C.

To generalize a bit, each of the vertices building pointing up to the world root are model matrices which transform the object from object space to world space. The final matrix is a view matrix that transforms any object from world space to camera space. The inverses of these matrices provide the transformation of the opposite direction.

Raytracing
Raytracing takes the 3D objects we see at each point in camera space and projects them onto a view plane.
For each pixel, we need to solve two problems:
- What object appears in the pixel?
- What color should the pixel be?
To determine what object appears in the pixel, we trace a ray from the camera, through the middle of the pixel, and solve for the first point of intersection in the scene.
There are two types of rays:
- Primary rays start from the camera and determine which parts of the scene are visible from the window.
- Secondary rays starts at points in the scene, where the primary rays hit, and are traced to other parts of the scene. They are used to compute light effects.
Primary Rays
A primary ray should take the form , where is the origin of the ray and is the direction vector.
should be set to the camera's coordinates in world space.
To find the direction of the ray, we need to find the coordinates of a pixel on the view plane. If that coordinate is , then is simply .
Given a coordinate of a pixel on the view plane, we want to find the point corresponding to its center. This should be given by
Each coordinate is first shifted so that it is centered on the pixel. Then it is normalized by the dimensions of the scene plane so that it becomes a one-by-one grid. Finally, it is shifted by so that the center of the grid is at the origin .
Ray-Object Intersections
Most production raytracers model shapes using triangles, but this is fairly complicated.
Ray-object intersections are easier to calculate with implicit objects. An implicit object is an object that can be definited by a function , where iff is a point on the service.
To find where a ray , we intersects an object, we just need to solve for the values of such that . There may be multiple values of that satisfies the equation, but we just care about the smallest, nonnegative value of .
Note that these objects are usually sitting in world space, but the implicit equations are defined in terms of object space. When solving for ray-object intersections, it is much easier to transform the ray into object space rather than transform the equation into world space.
Implicit Object Equations
A 2D circle of radius at the origin at the origin has the equation:
An infinite plane with a normal vector that is distance away from has the equation:
A sphere of radius at the origin at the origin has the equation:
An infinite cylinder of unit radius running along the y-axis has the equation:
Usually, it's more useful to work with a finite object, and define bounds on the truncated unit cylinder:
We should also define the implicit equations for the top and the bottom circles making up the cylinder:
Lighting
Lighting is the simulation of how light interacts with objects. Because accurately simulating lighting is very hard, we usually used simplified lighting models.
Direct illumination is light striking a surface directly from a light source. Indirect illumination light bouncing off other objects toward the surface element. It is a much more difficult to simulate than direct illumination, but we can often approximate indirect illumination with direct illumination.
Light Sources
In graphics, there are three common models of light sources:
| Type | Description | Parameters |
|---|---|---|
| Point light | A point that emits light equally in all directions | Position and light intensity |
| Spot light | A point that emits light in a finite cone of directions | Position , light intesity , light direction , and cutoff angle |
| Directional light | A light that shines in a particular direction; good for modeling the sun | Direction and light intensity |
Light Attenuation
Light attenuation is the idea that the amount of light illuminating an object should decrease with the square of distance. Thus, to be physically correct, when lighting a point that is distance away from a light source with intensity , we should attenuate the intensity as .
The problem is that this produces a very harsh lighting effect because there is no indirect illumination. Instead, we can use this hack:
Where , , and are experimentally defined constants.
Surface Normal Vectors
For illumination, we want to find the normal vector at the point . We can find this normal vector by taking the gradient at that point:
Once we have calculated the normal vector in object space, we need to transform the normal vector into world space.
Note that we cannot just apply the transformation matrix because when there is scaling. Scaling distorts the normal in exactly the opposite sense of scale applied to a surface. For example, scaling by a factor of causes the normal to scale by .
The right equation is , unless the transformation includes a reflection (i.e. ). The most foolproof equation is
BRDFs
Give a surface normal , an incoming light direction , and an outgoing light direction , the bidirectional reflectance distribution function (BRDF) describes how much incoming light is scattered in that outgoing direction:
Some common types of BRDFs are:
- Lambertian (diffuse): light scatters equally in all directions
- Specular (glossy): light scatters more tightly around one direction
- Mirror
The name of the Lambertian BRDF comes from Lambert's cosinne law, which states that light intensity falls off with the cosine of the angle between and : . Given as an RGB color, this gives us the Lambertian BRDF:
Most materials are not perfectly Lambertian; most BRDFs are viewer dependent. This is where specular BRDFs. One of the most common Specular BRDFs is Phong BRDF:
where:
- is an RGB color
- is the vector to the viewer
- is the angle between and
- controls how tight the specular highlight is
Phong Illumination Model
The Phong illumination model combines what we know about modeling light sources and materials to define a way to light objects.
First, it breaks lighting into three components: 1. Ambient stands in for the effect of indirect light; it affects all surfaces equally 2. Diffuse is the effect of direct light on a surface with a dull or rough finish 3. Specular is the effect of direct light on a shiny surface when the vector to the eye is closely aligned to the light's reflected rays

The Phong illumination model has three terms:
The full equation is:
where:
- is the set of all lights in the scene
- is the direction of light
- is the RGB intensity of light
- is the attenuation factor for light
Note that the normal should point toward the viewer. You should check the dot product of normal with the view direction and flight the normal if it is negative.
Shading
We have to compute color for every pixel in our output image, but we don't necessarily have to evaluate the full Phong illumination equation at every equation.
Graphics systems often make the distinction between:
- Lighting: what illumination model is evaluated and at what points
- Shading: how every pixel is colored based on the lighting evaluations
This is mostly relevant for real-time graphics, with objects made out of polygons. With implicit objects, we usually just evaluate the exact equation at each pixel where an intersection occurs.
Common shaders include:
- Flat shading: compute the normal (and lighting) once per face and apply that single color to all pixels in it
- Gouraud shading: compute lighting at each vertex using the vertex normal, then linearly interpolate the resulting colors across the face
- Phong shading: interpolate the normals themselves across the face, then run the lighting calculation per pixel using the interpolated normal
References: