| DATE | AIM |
|---|---|
| 1/31 | Peering into the depths of color (color depth, file formats) |
| 2/2 | Utilities (emacs, imagemagick) |
| 2/5 | Bresenham's Line Algorithm |
| 2/12 | Representing Image Data |
| 2/13 | Matrix Math in Graphics |
| 2/26 | Transformations (translation, dilation, rotation) |
| 3/5 | Parametrics |
| 3/6 | Curves (hermite, bezier) |
| 3/13 | 3D Shapes (cube, sphere, torus) |
| 3/20 | Vector Math (dot product, cross product) |
| 3/23 | Rendering 3D Objects (wireframe, polygon) |
| 3/28 | Backface Culling |
| 4/11 | Relative Coordinate System (CS stack) |
| 4/17 | Filling in Triangles (scanline conversion) |
| 4/19 | Z-Buffering |
| 4/26 | Lighting (Phong reflection model) |
| 5/7 | Compilers (lexer, parser, semantic analyzer) |
| 5/17 | Animation |
| 5/29 | Shading Models (flat, Gourand) |
How and when you calculate the color (I) for each shape.
Calculating I once per polygon (what we have right now).
Calculate I for each vertex of the polygon.
Interpolate I in scanline conversion and draw_line.
/| It
/ |
I0./__|.I1
/ |
Im \ |
\ |
\ |
\| Ib
Calculate I once per pixel.
Instead of having 3 different values for I at each vertex (Gourand), have 3 different normal values at each vertex.
Interpolate the surface normal in scanline conversion and draw_line.
/| Nt
/ |
N0./__|.N1
/ |
Nm \ |
\ |
\ |
\| Nb
v0
|\
| \
A | \ B
| \
|____\
v1 C v2
The way we calculate normals now, all 3 values at each vertex would be the same.
_____
|\ |
| \ A |
| \ |
| B \ |
|____\|
|\ |
| \ C |
| \ |
| D \ |
|____\|
Vertex normals will instead be the combined value of all surface normals for polygons that share a commmon vertex.
Generate multiple images in a sequence with small changes between each image.
We will apply transformations over time.
move 400 0 0
sphere 40 50 0 50
__________ __________
| | | |
|o | -------------------------------------------------> | o|
|__________| |__________|
__________ __________ __________ __________ __________
| | | | | | | | | |
|o | -> | o | -> | o | -> | o | -> | o|
|__________| |__________| |__________| |__________| |__________|
0% 25% 50% 75% 100%
These are applied to objects, not transformations.
move 400 0 0 0.00 |
400 0 0 0.25 |
400 0 0 0.50 | knob
400 0 0 0.75 |
400 0 0 1.00 |
move 400 0 0 m0
| Function | Definition | Example |
|---|---|---|
vary knob, start frame, end frame, start value, end value |
compute and store the knob values for each frame | vary m0, 0, 4, 0 1 |
frames number of frames |
defines number of frames in animation | frames 5 |
basename name |
gif name | basename rolling |
-delay N |
delays animation by N/100 seconds between frames | convert -delay 10 |
transformation args [knob] |
if there is a knob, look up its value in the symbol table and modify args by it | move x y z m0 |
- Setup
- Set frames if frame command is present.
- set basename if basename command is present
- stop if
varyis present but notframes2.
- Vary - compiler errors
- compute and store all knob values for every frame
- stop if vary range is invalid
- Draw
- repeat loop for each frame
- update the knobs in the symbol table at the beginning of each loop
- at the end of each loop, save the current frame
- once the loop ends, stitch the frames together
knob frames
__ __ __ __ __ __
|m0| |_1|_2|_3|_4|_5|
|__|
|__|
|__|
|__|
frames
__
|_0| -> m0, 0 -> m1, 2.5
|_1|
|_2|
|_3|
|_4|
Organization may depend on the type of language used, but Mr. DW recommends frame major.
In C, an array of linked lists.
k
vary | 0 3 0 .5
vary | 4 6 .5 .8
vary | 9 11 .8 1
There is no value for 7 and 8.
___ ___ ___ ___ ___ ___
|.5_|.65|.8_|_?_|_?_|.8_|
4 5 6 7 8 9
You don't deal with it at all, throw a compiler warning instead and let the programmer deal with it.
Or you can default to 0, because that error should be easily seen in the animation.
source code --------------------------> image(s)
source code ---------compiler---------> executeable code
| ^
v |
lexer -> syntatic -> semantic -> optimizer* -> code
analyzer analyzer generator
| Name | Input | Output |
|---|---|---|
| lexer | source code (text) | token list |
| parser | token list | syntax tree |
| semantic analyzer | syntax tree | operations list and symbol table |
| optimizer | operations list | optimized operations list |
| code generator | optimized operations list and symbol table | binary executeable |
Performs lexical analysis. "Knows" all the valid tokens in a language.
- String literals
- operators
- formatting characters (semicolons, parentheses, brackets)
- keywords (if, else)
- numeric literals
- identifiers (function names, variables)
Reads in source code and outputs a token list.
The types of errors a lexer will catch: bad character formatting, weird variable names (i.e. fro?)
Basically it only catches invalid tokens (NOT improper formatting).
Sample Code:
int main() {
long x = 5 + 6;
printf("%d", x);
return x;
}Sample Token List:
int
main
(
)
{
long
x
=
5
+
6
;
...
- C: lex, flex (free lex)
- java:
Performs syntax analysis, and checks the token list against the defined structure of the language.
Output is a syntax tree.
If the lexer checks for words, the parser checks for grammar.
Sample Syntax Tree:
int main
____________|_____________
= printf return
_______|___ _____|___ _|_
long x + "%d" x x
___|___
5 6
- C: yacc (yet another compiler compiler), bison
- java: javacc
Evaluates the syntax tree and creates an operation list and symbol table (stores identifiers and associated information).
If the languaged is typed, type-checking will occur here.
The semantic analyzer does NOT evaluated expressions.
- post: L -> R -> M
- pre: M -> L -> R
- in: L -> M -> R
| Number | Operation | Arguments |
|---|---|---|
| 0 | + | 5 , 6 |
| 1 | = | x , (0) |
| 2 | return | x |
| Symbol | Category | Type? |
|---|---|---|
| main | function | int |
| x | value | long |
| printf | function | int |
Runs through operations list and may replace functions in code to make it run faster.
For example: if ( x != 0 ) may turn into if ( x ), and if ( 1 ) may be removed completely.
We aren't making an optimizer, so this doesn't really matter.
Rather self-explanatory. Translates the operation list into binary assembly code.
image script -> token list -> syntax tree -> operations list -> image
symbol table
- call image
- creation/manipulation functions
- Color, intensity, and location of light.
- Reflective properties of objects.
Ambient Light: general lighting of an image
- a single source - comes from all locations equally
- represented by a normal color value
Point Light: comes from a specific location
- can have many sources for an image
- we will assume the light is from very far away
- represented by a color and location
Models real world reflections by breaking them into 3 parts.
I (color, normalization) = ambient + diffuse + specular
- diffuse and specular are point light
I = A·Ka + P·Kd · (N · L)
- A - Ambient Light (0->255)
- Ka - constant of ambient reflection(0->1)
- Ambient light = A·Ka
Reflection of a point light source. Reflected evenly in all directions. These are things that have a matte/dull finish.
- P - color of point light source
- Kd - constant of diffuse reflection
L N
\ |
\ |
\θ|
_____\|______
We can model the strength of the reflection with cosθ.
- cosθ = N · L
- diffuse = P · Kd · (N · L)
Reflects a point light source in a specific direction (shiny/glossy surfaces).
L N R
\_R__|__S_/ idk how to diagram it, but there is also another
\ T / view vector V between N and the x-axis.
\ | / the angle between V and R is α
\θ|θ/
_____\|/______
- P - color of point light
- Ks - constant of specular reflection
| Component | Value |
|---|---|
| R | T + S |
| S | T - L |
| R | 2T - L |
| cosα | R · V |
| T | (N·C) · N |
| R | 2(N·L)·N - L |
| cosα | (2(N·L)·N - L) · V |
L
/.
||L|| / .
/ .
/ .
/θ___._N
||T||
- cosθ = ||T|| / ||L||
- cosθ = ||T|| (L is a normalized vector)
- N · L = ||T||
Specular = P · Ks · [(2(N·L)·N - L) · V]x
power of x is to simulate a narrow reflection
Keeping a separate set of z values corresponding to each point the color grid to draw only the most front-facing side of an object.
Z-BUFFER: 2D array of floating point values that directly correspond to the screen
- check the z-buffer each time before updating the screen
- initialize each z-value to the smallest possible value
plotmust check/modify the z-bufferdraw_linemust compute z-valuesscanlinemust compute z-values
Given a triangle, how can we fill in its area?
- Fill with smaller and smaller triangles.
- Edge line fill (blending an edge to its opposite point; parallel lines to an edge)
- Scanline fill
- Flood fill (a single point, and plot its adjacent points; recursive)
Things that are good about scanline:
- no double coverage
- no weird spacing (horizontal lines only)
- full coverage
- takes advantage of the optimized draw_line
Filling in a polygon by drawing consecutive horizontal (or vertical) lines.
- Need to order vertices vertically for bottom, middle, and top, and find the endpoints of each scanline.
top (T)
/\
/ \
x0 / \
/ __\ middle
/__---
bottom (B)
| Value | Pseudocode / Equivalent |
|---|---|
| y | yB -> yT y++ |
| x0 | on the line BT xB -> xT x0 += Δ0 |
| Δ0 | (xT - xB) / (yT - yB) or Δx / # of scanlines, aka Δy |
| x1 | on the line BM until y = yM, then on MT x1 += Δ1 |
| Δ1 | (xM - xB) / (yM - yB) or (xT - xM) / (yT - yM) |
What happens with a triangle where the middle has the same y-value as the top or bottom point?
T
/\
/ \
/ \
/______\
B M
Could possibly be dividing by 0, in which case you switch the order you draw each scanline. For example, instead of going from top to bottom, go from bottom to top (or vice versa).
If you flip, make sure to set x = xM to ensure that you cover the middle value.
Currently transformations are applied to all shapes in the polygon/edge matrix.
In a RCS, we use transformations to modify the world in which we draw our shapes, rather than the shapes themselves.
Each shape can be drawn in a different world.
| 1 0 0 0 | | 2 0 0 50 |
| 0 1 0 0 | · | 0 2 0 100 |
| 0 0 1 0 | | 0 0 2 0 |
| 0 0 0 1 | | 0 0 0 1 |
A B
- Generate polygons/edges.
- Apply the current coordinate system to these points.
- Draw the polygons/edges to the image.
sphere rotate
rotate ===> move
move sphere
apply
Everything must be done in reverse.
We will maintain a stack of coordinate systems.
The top will always be the most current system.
The bottom will always be the identity matrix (the global system).
All transformations modify the top, but none of the other matrices.
Push will push a copy of the top.
transformation · top vs. top · transformation
| Command | Stack |
|---|---|
| push | I I |
| move | I I · M1 |
| box | "" |
| push | M1 · I M1 · I I |
| move | I·M1·M2 I·M1 I |
| rotate | I·M1·M2·R I·M1 I |
| sphere | "" |
| pop | I·M1 I |
Only drawing the polygons that are forward-facing.
Given a surface, take the surface normal (N), and a vector from the surface to the observer (V).
The angle between the two will tell us which way the surface is facing.
Taking 3 points (a triangle P0, P1, P2), two vectors A and B point away from the same point.
- N = A x B
- A = < P1 - P0 >
- B = < P2 - P0 >
- N · V = || N || · || V || · cosθ
- V = < 0, 0, 1 >
- N = < x, y, z >
- N · V = 0 + 0 + z
Cosine is positive from -90 to 90, so if N · V > 0, draw.
Shape is defined by the edges that connect points on the surface.
add_box -> add_edge -> add_point
draw_lines -> draw_line
- edges cannot be filled in
- perspective is difficult to discern just by looking at the screen
Shape is defined by the polygons that connect points on the surface (triangles).
add_box -> add_polygon -> add_point
new function (because draw_lines is no longer suitable): draw_polygons -> draw_line
P0
/\
P1 /__\ P2
/\
/__\
P3 P4
[ P0P1, P0P2, P1P2, ... ]
- six edges
[ P0P1P2, P2P3P4 ]
- two triangles
- points must be added in counter-clockwise order
Vectors have direction and magnitude.
| ( x, y, z ) | ^ < x, y, z >
| · | /
|____________ |/____________
Given vector A, < x,y,z >, the magnitude || A || = sqrt( x2, y2, z2 ).
A normalized vector is a unit vector that preserves the direction of another vector.
A' = 1 / || A || * < Ax, Ay, Az >
Given vectors A and B, and angle θ between them:
- || A || · || B || · cosθ
- Ax · Bx + Ay · By + Az · Bz
Perpendicular to A and B.
Magnitude = area of the parallelogram formed by A and B.
- || A || · || B || · sinθ
- < AyBz - AzBy , AzBx - AxBz , AxBy - AyBy >
PQ: < Q-P >
QP: < P-Q >
Defining points: vertices
Given information: P0 (top, left, front), width (x), height (y), depth (z)
Defining points: points on the surface
Given information: center, radius
Generate the defining points by drawing a circle and rotating it.
If you rotate about the z-axis, there is no 3D shape. Rotate about the x or y-axis.
| 1 0 0 | | rcosθ | | x |
| 0 cosϕ -sinϕ | · | rsinθ | = | y |
| 0 sinϕ cosϕ | | 0 | | z |
θ = angle of circle creation
ϕ = angle of rotation (0 -> 2π)
There are a few ways to draw a sphere:
- Draw a circle and rotate it 180 degrees.
- Draw a semi-circle and rotate it 360 degrees (the better option).
for (ϕ: 0 -> 2π) {
for (θ: 0 -> π) { // Semicircle
x = rcosθ + cx
y = rsinθcosϕ + cy
z = rsinθsinϕ + cz
}
}
Defining points: points on the surface
Given information: R (radius of the whole torus), r (radius of cross section), center
Generate the image by translating a circle and rotating it.
If we move over x, rotate about y.
If we move over y, rotate about x.
y-rotation circle torus
| cosϕ 0 sinϕ | | rcosθ + R | | cosϕ(rcosθ + R) + cx | | x |
| 0 1 0 | | rsinθ | = | rsinθ + cy | = | y |
| -sinϕ 0 cosϕ | | 0 | | -sinϕ(rcosθ + R) + cz | | z |
θ = 0 -> 2π
ϕ = 0 -> 2π
Curves that can be combined smoothly.
We will only draw cubics, and combine them to form these curves.
for t: 0->1, t += step
- x = ax · t3 + bx · t2 + c · t + dx
- y = ay · t3 + by · t2 + c · t + dy
- endpoints: P0, P1
- rate of change at each endpoint: R0, R1
- points on curve: f(t) = a · t3 + b · t2 + c · t + d
- rates of change: f'(t) = 3a · t2 + 2b · t + c
| When t = 0 | When t = 1 |
|---|---|
| f(t) = d : P0 | f(t) = a + b + c + d : P1 |
| f'(t) = c : R0 | f'(t) = 3a + 2b + c : R1 |
| 0 0 0 1 | | a | | P0 |
| 1 1 1 1 | · | b | = | P1 |
| 0 0 1 0 | | c | | R0 |
| 3 2 1 0 | | d | | R1 |
H C G
Unfortunately, we don't need to solve for G, because that is already given.
| 2 -2 1 1 | | P0 | | a |
| -3 3 -2 -1 | · | P1 | = | b |
| 0 0 1 0 | | R0 | | c |
| 1 0 0 0 | | R1 | | d |
Defined by n + 1 points (n = degree of the equation).
We will start with a line.
. P1
/
/
. Pt
/
/
. P0
- Pt moves along the line P0P1
- Pt = (1 - t) · P0 + t · P1
Three points: P0, P1, P2.
Q0 moves along the line P0P1.
Q1 moves along the line P1P2.
Qt moves along the line Q0Q1.
Qt = (1 - t) · Q0 + t · Q1
= (1 - t)2 · P0 + 2t · (1 - t) · P1 + t2 · P2
Rt moves along the line R0, R1.
R0 moves along the quadratic Q0, Q1.
R1 moves along the quadratc Q1, Q2.
= (1 - t) · R0 + t · R1
= (1 - t)2 · P0 + 3t · (1 - t)2 · P1 + 3 · t2 · (1 - t) · P2 + t3 · P3
= ( -P0 + 3P1 - 3P2 + P3 ) t3 + ( 3P0 - 6P1 + 3P2 ) t2 + ( -3P0 + 3P1 ) t + P0
- a = -P0 + 3P1 - 3P2 + P3
- b = 3P0 - 6P1 + 3P2
- c = -3P0 + 3P1
- d = 3P2
| -1 3 -3 1 | | P0 | | a |
| 3 -6 3 0 | | P1 | | b |
| -3 3 0 0 | · | P2 | = | c |
| 1 0 0 0 | | P3 | | d |
Instead of defining a curve in which x and y are directly dependent on each other, they will be separately related to another variable.
Essentially, define a curve as a system of equations based on an independent variable (t).
- for example:
x = f(t),y = g(t),z = h(t) - For consistency, think of
tas going from 0 to 1.
for t : 0->1, t += increment
x = f(t)
y = g(t)
add(x, y)
t will not be an integer (the smaller the value, the more precise the image, and the slower your program will be)
- f(t) = x0 + t(Δx)
- g(t) = y0 + t(Δy)
When t = 0, x = x0 and y = y0.
When t = 1, x = x1 and y = y1.
- f(t) = r · cos(2π·t) + xc
- g(t) = r · sin(2π·t) + yc
- θ : 0 -> 2π
Because of floating point math on computers, when you write your code, it's better to have integer based controls. So if you want 100 steps, instead of making the increment 0.01, make t go from 0 to 100, and divide by 100 at the end.
Translation, dilation, rotation (affine transformations; preserves orientation and vertices).
- E: edge matrix
- T: transformation matrix
- E · T or T · E ( i.e. 3x3 · 3xN = 3xN )
( x, y, z ) -> T(a, b, c) -> ( x+a, y+b, z+c );
| 1 0 0 a | | x | | x+a |
| 0 1 0 b | · | y | = | y+b |
| 0 0 1 c | | z | | z+c |
| 0 0 0 1 | | 1 | | 1 |
4x4 4xN 4xN
( x, y, z ) -> D(a, b, c) -> ( ax, by, cz )
| a 0 0 0 | | x | | ax |
| 0 b 0 0 | · | y | = | by |
| 0 0 c 0 | | z | | cz |
| 0 0 0 1 | | 1 | | 1 |
D(a,b,c)
With this formula, the size of the object being drawn will be scaled, but so will the distance from the origin. To avoid this, you can translate the object to the origin, dilate, and then translate back.
y
| · (x1,y1)
|
| · (x,y)
|
|______________ x
( x, y ) -> Rθ -> ( ? , ? )
θ is the angle between the two points
Φ is the angle between (x,y) and the original x-axis
- x = r·cos(Φ)
- y = r·sin(Φ)
- x1 = r·cos(Φ + θ)
x1 = r·cos(Φ)cos(θ) - r·sin(Φ)sin(θ)
x1 = x·cos(θ) - y·sin(θ) - y1 = r·sin(Φ + θ)
y1 = y·cos(θ) + x·sin(θ)
| cos -sin 0 0 | | x | | xcos - ysin |
| sin cos 0 0 | | y | | ycos + xsin |
| 0 0 1 0 | · | z | = | z |
| 0 0 0 1 | | 1 | | 1 |
R(θ,z)
z
| · (y1,z1)
|
| · (y,z)
|
|______________ y
/
/
/
x
y = rcosθ, z = rsinθ
y1 = ycosθ - zsinθ
z1 = zcosθ + ysinθ
x
| · (z1,x1)
|
| · (z,x)
|
|______________ z
/
/
/
y
z1 = zcosθ - xsinθ
x1 = xcosθ + zsinθ
E0 (edges), T (translate), R (rotate), S (scale)
- T · E0 = E1
- R · E1 = E2
- S · E2 = E3
- E3 = ( S · R · T ) · E0
THIS IS ASSOCIATIVE
Read from right to left. Translate first, then rotate, then scale.
[ P0 , P1 , P2 , P3 ... Pn ]
| x0 , x1 ... xn |
| y0 , y1 ... yn |
| z0 , z1 ... zn |
3 x N (3 rows, N columns)
- # of columns in M = # of rows in N
- A x B · B x C = A x C
- Non-commutative: M·N ≠ N·M
| a |
| 1 2 3 | · | b | = | 1a + 2b + 3c |
1x3 | c | 1x1
3x1
| 1 2 3 | | a b | | 1a+2c+3a 1b+2d+3f |
| 4 5 6 | · | c d | = | 4a+5c+6e 4b+5d+6f |
| 7 8 9 | | e f | | 7a+8c+9e 7b+8d+9f |
3x3 3x2 3x2
- square, diagonal of 1's, and 0's everywhere else
| 1 0 | . | a | = | a |
| 0 1 | | b | | b |
Say you have a triangle. Each time you create the triangle, draw_line() must be called 3 times.
To store this triangle, you could create a list of the pairs of points.
- [ p0, p1, p2 ] --> edge list
- [ p0 p1 p2 ] [ p1, p2, p0 ]
- [ p0, p1, p2, p0 ]
- one list for points, another list for connections between points
List of points in the image where every 2 points determines a line.
[ p0 , p1 , p2 , p3 ... pn ]
-->
( p0 , p1 ), (p2 , p3) , (pn-1 , pn )
Triangle ABC --> [ A, B, B, C, C, A ]
This is essentially a two dimensional array.
y = mx + b - This is nice, but won't work in graphics because everything is in pixels, and therefore INTEGERS.
When using a line algorithm, we are approximating the line (except with horizontal/vertical lines).
Find the pixels that best approximate a target line.
- ( x0 , y0 ) -> ( x1 , y1 ) are all integers (endpoints exist)
- Only lines in octant I -
0 < m < 1 - x0 < x1 - always start in the left and move to the right
__ __ __ __ __ __
|__|__|__|__|__|__|
|__|__|__|__|__|██|
|__|__|__|__|__|__|
|__|1_|__|__|__|__|
|██|2_|__|__|__|__|
In picking the line between the two selected endpoints, we have 2 options for the next pixel:
( x + 1 , y + 1 )( x + 1 , y )
To pick, use the midpoint ( x + 1 , y + 0.5 ).
- If the midpoint is above the line, draw the lower pixel.
- If the midpoint is below the line, draw the upper pixel.
- If the midpoint is on the line, draw one or the other.
y = mx + b
0 = mx - y + b
m = Δy / Δx
0 = (Δy / Δx) x - y + b
= xΔy -yΔx + bΔx
A = Δy, B = -Δx, C = bΔx
0 = Ax + By + C
f(x,y) = Ax + By + C
- When the midpoint is directly on the line,
f(x,y) = 0(this is not really that important). - When the midpoint is above the line,
f(x,y) < 0. - When the midpoint is below the line,
f(x,y) > 0.
x = x0 , y = y1
d = f ( x + 1 , y + 0.5)
while x <= x1
plot ( x , y )
x++
if d > 0 : y++
d = f ( x + 1 , y + 0.5 )
if x++ : d += A
if y++ : d += B
d = f( x + 1 , y + 0.5)
while x <= x1
plot ( x , y )
if d > 0
y++
d += B
x++
d += A
d = f ( x0 + 1 , y0 + 0.5 )
= A ( x0 + 1 ) + B ( y0 + 0.5 ) + C
= A x0 + B y0 + A + 0.5 B
= f (x0, y0) + A + 0.5B
= A + 0.5B
d = 2A + B
while x <= x1
plot(x,y)
if d > 0
y++
d += 2B
x++
d += 2A
__ __ __ __ __ __
|__|__|__|██|__|__|
|__|__|__|__|__|__|
|__|__|__|__|__|__| // 1. ( x , y + 1 )
|1_|2_|__|__|__|__| // 2. ( x + 1 , y + 1 )
|██|__|__|__|__|__|
MIDPOINT: ( x + 0.5 , y + 1 )
d = 2A + B --> d = A + 2B
while x <= x1 --> y <= y1
plot(x,y)
if d > 0 --> d < 0
x++ // swapped with
d += 2A
y++
d += 2B
__ __
|██|1_| // ( x + 1 , y )
|__|2_| // ( x + 1 , y - 1 )
MIDPOINT : ( x + 1 , y - 0.5 )
d0 : f ( x0 + 1 , y0 - 0.5 )
2A - B
if d > 0
ctrl-C to convert between image and code
Program thing for ppm.
$ display image.ppm$ convert image.ppm image.png
The amount of data used to represent a single pixel.
| SIZE | COLOR OPTIONS |
|---|---|
| 1 bit | 1 color; on/off |
| 2 bit | 1 color; on/off, plus 1 bit for intensity |
| 3 bit | Red, Green, Blue; on/off |
| 4 bit | RGB with intensity |
| 6 bit | RGB, each color with its own intensity (each one color has 2 bits) |
| 3 byte | 1 byte for each color; each with 256 possible intensities (~16 million combinations) |
We mostly just use RGB.
- RGBA: Red, Green, Blue + Alpha (transparency)
- HSB: Hue, Saturation, Brightness (cone diagram)
Vector: represent images as a series of drawing instructions
- They are infinitely scalable.
- SVG (Scalable Vector Graphics)
Raster: represent images as a grid of pixels
Uncompressed: contain data for each pixel
- (i.e. BMP, TIFF, RAW)
Compressed: use a compression algorithm to minimize file size
- lossless - contain enough information to exactly recreate the original image
- PNG (Portable Network Graphics), GIF (Graphics Interchange Format)
- lossy - do not retain all the details of the original image
- JPEG (Joint Photographic Experts Group)
Uncompressed raster format.
Pixel data is represented by RGB triplets in either ASCII or binary.
All whitespace is equivalent.
P3 // file type
4 3 // dimensions
255 // maximum color value
// each group of 3 numbers is a single pixel
255 0 0 255 0 0 255 0 0 255 0 0
0 255 0 0 255 0 0 255 0 0 255 0
0 0 255 0 0 255 0 0 255 0 0 255
Number 1 Rule of Graphics:
DO NOT UPLOAD PPM FILES TO GITHUB