0% found this document useful (0 votes)
2 views

GameProg_lect4 2dArt

The document outlines the design documentation for a 2D graphics game, detailing its unique character, genre influences, and storyline elements. It covers aspects of game programming such as control schemes, graphic design elements, color theory, and algorithms for drawing lines and polygons. Additionally, it discusses clipping techniques, moving objects, and the mathematical transformations necessary for game object manipulation.

Uploaded by

abdo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views

GameProg_lect4 2dArt

The document outlines the design documentation for a 2D graphics game, detailing its unique character, genre influences, and storyline elements. It covers aspects of game programming such as control schemes, graphic design elements, color theory, and algorithms for drawing lines and polygons. Additionally, it discusses clipping techniques, moving objects, and the mathematical transformations necessary for game object manipulation.

Uploaded by

abdo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPT, PDF, TXT or read online on Scribd
You are on page 1/ 100

Construct 2D Graphics game

GAME PROGRAMMING
write up a design documentation
for your upcoming game
• describe the unique character of the
game
• Genre: what games does your game
draw from and expand upon?
• What is the story of the game? Name
basic elements - a character, a place, a
mission. Describe the scenes. Does
sound play a role in your game? If so, in
what way (e.g. dramatizing effects)?
• Control scheme: how will the user
interact with the game? Mouse?
Keyboard? Other (e.g. line-in,
microphone)? What button will do what?
If there is any type of GUI or Heads-
upDisplay, please provide a sample
screenshot of the GUI (hint: you can use
a mockup of your screen,
Elements of Graphic Design

Q
• Line
• Shape
Q
• Space - + or -
Line Shape Space Texture Size

• Texture – look and feel


• Size – use to create
perspective, sense of
importance
• Color
Color in Games
Three-Color Theory
•Human visual system has two types
of sensors
– Rods:
•monochromatic, night vision
– Cones:
•Color sensitive
•Three types of cone
•Only three values
•(the tristimulusvalues) are sent to
the brain
Additive / Subtractive color

Y M

Additive Color Subtractive Color

LCD, projector Printer


RGB color space

Green

Black
(0, 0,
0)
Red

Blue
rgb color cube
in another view
HSV color space

• HSV
– hue,
– Saturation,
– Value,

HSV UI
Pixel-based primitives

•Bitmaps
– 2D array of bit masks for pixels
•update pixel color based on current
color
•Images
– 2D array of pixel color information
•complete color information for each
pixel
Game Models
• Geometry
– Position / vertex normals / vertex colors / texture
coordinates
• Topology
– Primitive

18
•Lines / triangles / surfaces / …
• Property
– Materials
– Textures
• Motion / Animation
Geometry Data
 Vertex position
– (x, y, z, w)
– In model space or screen spane
 Vertex normal
– (nx, ny, nz)

19
 Vertex color
– (r, g, b) or (diffuse, specular)
 Texture coordinates on vertex
– (u1, v1), (u2, v2), …
 Skin weights
– (bone1, w1, bone2, w2, …)
DRAWING LINE/PLYLINES
Bresenhem’s Algorithm Drawing line

• Starts at x0, y0
• Plots x0, y0
• xi = xi + 1

• When advancing yi decide between plotting (xi+1, yi) or


(xi+1,yi +1)
• Note: Algorithm treats separately
• m < 1 (<45°) and m > 1 (>45°)
23
Draw_Line( )

int Draw_Line(int x0, int y0, // starting position


int x1, int y1, // ending position
UCHAR color, // color index
UCHAR *vb_start, int lpitch) // video buffer & memory pitch
{
// function draws a line from xo,yo to x1,y1 using differential error
// terms (based on Bresenahams work)
int dx, // difference in x's
dy, // difference in y's
dx2, // dx,dy * 2
dy2,
x_inc, // amount in pixel space to move during drawing
y_inc, // amount in pixel space to move during drawing
error, // the discriminant i.e. error i.e. decision
variable
index; // used for looping
// pre-compute first pixel address in video buffer
vb_start = vb_start + x0 + y0*lpitch;

25
Draw_Line( )
// compute horizontal and vertical deltas
dx = x1-x0;
dy = y1-y0;
// test which direction the line is going in i.e. slope angle
if (dx>=0)
{
x_inc = 1;
} // end if line is moving right
else
{
x_inc = -1;
dx = -dx; // need absolute value
} // end else moving left

26
DrawLine( )
// now based on which delta is greater we can draw the line
if (dx > dy)
{
error = dy2 - dx; // initialize error term
for (index=0; index <= dx; index++) // draw the line
{
*vb_start = color; // set the pixel
// test if error has overflowed
if (error >= 0)
{
error-=dx2; // move to next line
vb_start+=y_inc;
} // end if error overflowed
error+=dy2; // adjust the error term
vb_start+=x_inc; // move to next pixel
} // end for
} // end if |slope| <= 1

27
DrawLine( )
else
{
error = dx2 - dy; // initialize error term
for (index=0; index <= dy; index++) // draw the line
{
*vb_start = color; // set the pixel
if (error >= 0) // test if error overflowed
{
error-=dy2;
vb_start+=x_inc; // move to next line
} // end if error overflowed
error+=dx2; // adjust the error term
vb_start+=y_inc; // move to next pixel
} // end for
} // end else |slope| > 1
return(1); // return success
} // end Draw_Line

28
DrawLine( )
// test y component of slope
if (dy>=0)
{
y_inc = lpitch;
} // end if line is moving down
else
{
y_inc = -lpitch;
dy = -dy; // need absolute value
} // end else moving up
// compute (dx,dy) * 2
dx2 = dx << 1;
dy2 = dy << 1;

29
Polygons
•Defined by vertices
•Closed (all line connected)
•Simply draw them one line at a time
•Can be convex or concave
•Attributes:
Number of vertices, color, position,
list of vertices (x, y) form

31
Draw_Polygon2D( )

if (poly->state) // test if the polygon is visible


{
// loop thru and draw a line from vertices 1 to n
for (int index=0; index < poly->num_verts-1; index++)
{
// draw line from ith to ith+1 vertex
Draw_Clip_Line(poly->vlist[index].x+poly->x0, poly->vlist[index].y+poly->y0,
poly->vlist[index+1].x+poly->x0, poly->vlist[index+1].y+poly->y0,
poly->color, vbuffer, lpitch);
} // end for
// draw line from last vertex to 0th
Draw_Clip_Line(poly->vlist[0].x+poly->x0, poly->vlist[0].y+poly->y0,
poly->vlist[index].x+poly->x0, poly->vlist[index].y+poly->y0,
poly->color, vbuffer, lpitch);
return(1);
} // end if
else
return(0);

41
Filling Polygons

•Many systems draw 2D and 3D


objects as collections of triangles
•If we can fill a triangle, we are in
pretty good shape
•The general idea will be to break
up figures into triangles whose
base is parallel to the x-axis and
the draw lots of horizontal lines
43
Triangle Filling

P0 (x0 , y0)

P2 (x2 , y 2) P1 (x1 , y 1)

44
Triangle Filling

1. Compute dx/dy for left and right sides


(basically 1/slope) call them dxy_left
and dxy_right
2. Starting at top vertex (x0 , y0) set
xs=xl= x0 and y= y0
3. Add dxy_left to xs and add dxy_right
to xl
4. Draw lines (xs,y) to (xl,y)
5. Increment y and go to step 3
47
Drawing Filled Triangles
void Draw_Top_TriFP(int x1,int y1,int x2, int y2, int x3,int y3,
int color, UCHAR *dest_buffer, int mempitch)
// function draws a triangle that has a flat top using fixed point math

void Draw_Bottom_TriFP(int x1,int y1, int x2,int y2, int x3,int y3,
int color, UCHAR *dest_buffer, int mempitch)
// function draws triangle that has flat bottom using fixed point math

void Draw_Top_Tri(int x1,int y1, int x2, int y2, int x3,int y3,
int color, UCHAR *dest_buffer, int mempitch)
// this function draws a triangle that has a flat top

void Draw_Bottom_Tri(int x1,int y1, int x2, int y2, int x3,int y3,
int color, UCHAR *dest_buffer, int mempitch)
// this function draws a triangle that has a flat bottom

48
Drawing Filled Triangles
void Draw_TriangleFP_2D(int x1,int y1,int x2, int y2, int x3,int y3,
int color, UCHAR *dest_buffer, int mempitch)
// function draws triangle on the destination buffer using fixed point
// it decomposes all triangles into a pair of flat top, flat bottom

void Draw_Triangle_2D(int x1,int y1, int x2, int y2, int x3,int y3,
int color, UCHAR *dest_buffer, int mempitch)
// this function draws a triangle on the destination buffer
// it decomposes all triangles into a pair of flat top, flat bottom

49
Example Player Art
Clipping

•Goal is to only display the part of


the image that is really viewable
on the portion of the screen used
for drawing
•Approaches:
– Border
– Image space
– Object space
51
Image Space Clipping

• Image space (pixel level representation


of complete image)
• Test each point to see if it is in the
region before trying to draw it
• Easy to implement
• Works for all objects
• Works for sub regions
• Requires lots of computation for each
pixel
52
Cohen Sutherland

• Each region is assigned a bit code


• End points P1and P2
• If y < minY or y > maxY or x < minX or
x > maxX then save Sign bits of P1 and
P2

P2

53
Cohen Sutherland

•Accept the point if P1 + P2 = 0


•Reject the point if P1 & P2 != 0

Note: It is best to break up the


line so that line segment only
occupies a single region
54
Draw_Clip_Line( )
int Draw_Clip_Line(int x0,int y0, int x1, int y1, UCHAR color,
UCHAR *dest_buffer, int lpitch)
{
// this helper function draws a clipped line
int cxs, cys, cxe, cye;
// clip and draw each line
cxs = x0;
cys = y0;
cxe = x1;
cye = y1;
// clip the line
if (Clip_Line(cxs,cys,cxe,cye))
// use Bresenham like before
Draw_Line(cxs, cys, cxe,cye,color,dest_buffer,lpitch);
return(1); // return success
} // end Draw_Clip_Line

55
Clip_Line( )
int Clip_Line(int &x1,int &y1,int &x2, int &y2)
{
// function clips the line using globally defined clipping region
// internal clipping codes
#define CLIP_CODE_C 0x0000
#define CLIP_CODE_N 0x0008
#define CLIP_CODE_S 0x0004
#define CLIP_CODE_E 0x0002
#define CLIP_CODE_W 0x0001
#define CLIP_CODE_NE 0x000a
#define CLIP_CODE_SE 0x0006
#define CLIP_CODE_NW 0x0009
#define CLIP_CODE_SW 0x0005

int xc1=x1, yc1=y1, xc2=x2, yc2=y2;

int p1_code=0, p2_code=0;

56
Clip_Line( )
// determine codes for p1 and p2
if (y1 < min_clip_y)
p1_code|=CLIP_CODE_N;
else if (y1 > max_clip_y)
p1_code|=CLIP_CODE_S;
if (x1 < min_clip_x)
p1_code|=CLIP_CODE_W;
else if (x1 > max_clip_x)
p1_code|=CLIP_CODE_E;
if (y2 < min_clip_y)
p2_code|=CLIP_CODE_N;
else if (y2 > max_clip_y)
p2_code|=CLIP_CODE_S;
if (x2 < min_clip_x)
p2_code|=CLIP_CODE_W;
else if (x2 > max_clip_x)
p2_code|=CLIP_CODE_E;

57
Clip_Line( )
// try and trivially reject
if ((p1_code & p2_code))
return(0);
// test for totally visible, if so leave points untouched
if (p1_code==0 && p2_code==0)
return(1);

// determine end clip point for p1



// determine end clip point for p2

58
Clip_Line( )
// do bounds check
if ((xc1 < min_clip_x) || (xc1 > max_clip_x) ||
(yc1 < min_clip_y) || (yc1 > max_clip_y) ||
(xc2 < min_clip_x) || (xc2 > max_clip_x) ||
(yc2 < min_clip_y) || (yc2 > max_clip_y) )
{
return(0);
} // end if

// store vars back


x1 = xc1;
y1 = yc1;
x2 = xc2;
y2 = yc2;

return(1);
} // end Clip_Line

59
Example Enemy Art
Moving Objects

•If you move an object do you need to


change every vertex coordinate?
•Yes, if you use world coordinates
(meaning the screen coordinate
system)
•No, if you local coordinates (meaning
local to the object itself)
– Example: triangle at (4,0) might
have local vertex coordinates (0,1),
(-1,-1), (1,-1)
62
Translation (Moving)

•If you want to move a point (x 0 , y0)


to a new position (xt , yt)
•The transformation would be
xt = x0 + dx and yt = y0 + dy
•For motion dx and dy are the
components of the velocity vector
dx = cos v and dy = - sin v
63
Game_Main( )
// draw all the asteroids
for (int curr_index = 0; curr_index < NUM_ASTEROIDS; curr_index++)
{
// glow asteroids
asteroids[curr_index].color = rand()%256;
// do the graphics
Draw_Polygon2D(&asteroids[curr_index], (UCHAR *)ddsd.lpSurface, ddsd.lPitch);
// move the asteroid without matrix operations
asteroids[curr_index].x0+=asteroids[curr_index].xv;
asteroids[curr_index].y0+=asteroids[curr_index].yv;
// test for out of bounds
if (asteroids[curr_index].x0 > SCREEN_WIDTH+100)
asteroids[curr_index].x0 = - 100;
if (asteroids[curr_index].y0 > SCREEN_HEIGHT+100)
asteroids[curr_index].y0 = - 100
if (asteroids[curr_index].x0 < -100)
asteroids[curr_index].x0 = SCREEN_WIDTH+100;
if (asteroids[curr_index].y0 < -100)
asteroids[curr_index].y0 = SCREEN_HEIGHT+100;
} // end for curr_asteroid

64
Scaling (Changing Size)

•Multiply the coordinates of each


vertex by the scale factor
•Everything will expand from the
center
•The transformation would be
xt = x0 * scale and yt = y0 * scale

65
Scale_Polygon2D( )
int Scale_Polygon2D(POLYGON2D_PTR poly, float sx, float sy)
{
// this function scalesthe local coordinates of the polygon
// test for valid pointer
if (!poly)
return(0);
// loop and scale each point
for (int curr_vert = 0; curr_vert < poly->num_verts; curr_vert++)
{
// scale and store result back
poly->vlist[curr_vert].x *= sx;
poly->vlist[curr_vert].y *= sy;

} // end for curr_vert


// return success
return(1);
} // end Scale_Polygon2D

66
Game_Main( )
// do the graphics
Draw_Polygon2D(&asteroid, (UCHAR *)ddsd.lpSurface,
ddsd.lPitch);

// test for scale


if (KEYDOWN('A')) // scale up
Scale_Polygon2D(&asteroid, 1.1, 1.1);
else
if (KEYDOWN('S')) // scale down
Scale_Polygon2D(&asteroid, 0.9, 0.9);

// rotate the polygon by 5 degrees


Rotate_Polygon2D(&asteroid, 5);

67
Rotation (Turning)

• Spin and object around it centered


around the z-axis
• Rotate each point the same angle
– Positive angles are clockwise
– Negative angles are counterclockwise
– C++ uses radians (not degrees)
• The transformation would be
xt = x0 * cos() – y0 * sin()
yt = y0 * cos() + x0 * sin()

68
Rotate_Poloygon2D( )
int Rotate_Polygon2D(POLYGON2D_PTR poly, int theta)
{
// function rotates the local coordinates of the polygon – no matrices
// test for valid pointer
if (!poly)
return(0);
// loop and rotate each point, very crude, no lookup!!!
for (int curr_vert = 0; curr_vert < poly->num_verts; curr_vert++)
{
// perform rotation
float xr = (float)poly->vlist[curr_vert].x*cos_look[theta] -
(float)poly->vlist[curr_vert].y*sin_look[theta];
float yr = (float)poly->vlist[curr_vert].x*sin_look[theta] +
(float)poly->vlist[curr_vert].y*cos_look[theta];
// store result back
poly->vlist[curr_vert].x = xr;
poly->vlist[curr_vert].y = yr;
} // end for curr_vert
// return success
return(1);
} // end Rotate_Polygon2D
69
Matrix Operations

• Translation
| 1 0 0|
|x y 1| * | 0 1 0|
|dx dy 1|

• Scaling
|sx 0 0|
|x y 1| * | 0 sy 0|
| 0 0 1|

70
Matrix Operations

• Rotation |cos(a) -sin(a) 0|


|x y 1| * |sin(a) cos(a) 0|
| 0 0 1|

• (S * R) * T
| sx*cos(a) –sx*sin(a) 0|
|x y 1| * | sy*sin(a) sy*cos(a) 0|
| dx dy 1|

71
Mat_Init_3x2( )
inline int Mat_Init_3X2(MATRIX3X2_PTR ma,
float m00, float m01,
float m10, float m11,
float m20, float m21)
{
// fills a 3x2 matrix with the sent data in row major form
ma->M[0][0] = m00; ma->M[0][1] = m01;
ma->M[1][0] = m10; ma->M[1][1] = m11;
ma->M[2][0] = m20; ma->M[2][1] = m21;

// return success
return(1);
} // end Mat_Init_3X2

72
Mat_Mul1x2_3x2( )
int Mat_Mul1X2_3X2(MATRIX1X2_PTR ma, MATRIX3X2_PTR mb, MATRIX1X2_PTR mprod)
{
// function multiplies a 1x2 matrix against a 3x2 matrix - ma*mb and
// stores result using a dummy element for the 3rd element of the 1x2
for (int col=0; col<2; col++)
{
// compute dot product from row of ma and column of mb
float sum = 0; // used to hold result
for (int index=0; index<2; index++)
{
sum+=(ma->M[index]*mb->M[index][col]); // add next product pair
} // end for index
sum += mb->M[index][col]; // add in last element * 1
mprod->M[col] = sum; // insert resulting col element
} // end for col
return(1);
} // end Mat_Mul_1X2_3X2

73
Matrix Translation

int Translate_Polygon2D_Mat(POLYGON2D_PTR poly, int dx, int dy)


{
// function translates center polygon by using a matrix multiply
if (!poly) return(0); // test for valid pointer
MATRIX3X2 mt; // used to hold translation transform matrix
// initialize the matrix with translation values dx dy
Mat_Init_3X2(&mt,1,0, 0,1, dx, dy);
// create a 1x2 matrix to do the transform
MATRIX1X2 p0 = {poly->x0, poly->y0};
MATRIX1X2 p1 = {0,0}; // this will hold result
// now translate via a matrix multiply
Mat_Mul1X2_3X2(&p0, &mt, &p1);
// now copy the result back into polygon
poly->x0 = p1.M[0];
poly->y0 = p1.M[1];
// return success
return(1);
} // end Translate_Polygon2D_Mat
74
Matrix Scaling
int Scale_Polygon2D_Mat(POLYGON2D_PTR poly, float sx, float sy)
{
// this function scales the local coordinates of the polygon
if (!poly) return(0); // test for valid pointer
MATRIX3X2 ms; // used to hold scaling transform matrix
Mat_Init_3X2(&ms, sx, 0, 0, sy, 0, 0);
// loop and scale each point
for (int curr_vert = 0; curr_vert < poly->num_verts; curr_vert++)
{
// scale and store result back
MATRIX1X2 p0 = {poly->vlist[curr_vert].x, poly->vlist[curr_vert].y};
MATRIX1X2 p1 = {0,0}; // this will hold result
Mat_Mul1X2_3X2(&p0, &ms, &p1); // now scale via a matrix multiply

poly->vlist[curr_vert].x = p1.M[0]; // copy result back to vertex


poly->vlist[curr_vert].y = p1.M[1];
} // end for curr_vert
return(1);
} // end Scale_Polygon2D_Mat

75
Matrix Rotation
int Rotate_Polygon2D_Mat(POLYGON2D_PTR poly, int theta)
{
// function rotates the local coordinates of the polygon
if (!poly) return(0); // test for valid pointer

// test for negative rotation angle


if (theta < 0)
theta += 360;

MATRIX3X2 mr; // used to hold rotation transform matrix

Mat_Init_3X2(&mr,cos_look[theta],sin_look[theta],
-sin_look[theta],cos_look[theta], 0, 0);

76
Matrix Rotation

// loop and rotate each point, very crude, no lookup!!!


for (int curr_vert = 0; curr_vert < poly->num_verts; curr_vert++)
{
// create a 1x2 matrix to do the transform
MATRIX1X2 p0 = {poly->vlist[curr_vert].x, poly->vlist[curr_vert].y};
MATRIX1X2 p1 = {0,0}; // this will hold result
// now rotate via a matrix multiply
Mat_Mul1X2_3X2(&p0, &mr, &p1);
// now copy the result back into vertex
poly->vlist[curr_vert].x = p1.M[0];
poly->vlist[curr_vert].y = p1.M[1];
} // end for curr_vert
return(1);
} // end Rotate_Polygon2D_Mat

77
Title Screen Art
Transformation Example 1

79
Transformation Example 2

80
PROJECTION
Projection

World space

Orthographic Perspective
Classical Projections
First-Person perspective
• A graphical perspective rendered from the
viewpoint of the player character

doom Mirror's Edge


Second-person perspective

• Let players feel as if they are actually


the character they are controlling

Mario 64 Gear of war 3


Third-Person perspective

• From God view

simcity Star craft 2


3D models

• Simple objects from the world around us


can be depicted as “wire-frame models”
• We make a list of the “key” points (usually
corners) on the object’s outer surface and
a list of all the lines that connect them
• The “key” points are called “vertices”
• The connecting lines are called “edges”
• We create a file that contains this “data”
Example: the basic barn
10 corner-points (“vertices”)
15 line-segments (“edges”)
3D-coordinates

Front vertices Back vertices


V0=( 0.5, 0.5, 0.5 ) V1=( 0.5, 0.5, -0.5 )
V2=( 0.5, -0.5, 0.5 ) V3=( 0.5, -0.5, -0.5 )
V4=( -0.5, -0.5, 0.5 ) V5=( -0.5, -0.5, -0.5 )
V6=( -0.5, 0.5, 0.5 ) V7=( -0.5, 0.5, -0.5 )
V8=( 0.0, 1.0, 0.5 ) V9=( 0.0, 1.0, -0.5 )
Perspective Projection

•We imagine the computer display


screen is located between the
wireframe model and the eye of
someone who’s viewing it
•Each vertex is “projected” onto the
screen
•We use Bresenham’s algorithm to
draw line-segments that connect the
projections
The projection
Y-axis P(x,y,z)

P*(x*,y*,0)

X-axis
View-plane
Eye of viewer
Z-axis (0,0,D) D = distance of eye from view-plane
(0, 0) (W-1, 0) (Xmin, Ymax) (Xmax, Yma

Projection
Screen Plane
(0, W-1) (W-1, H-1) (Xmin, Ymin) (Xmax, Ymi

Screen Dimension
W x H
Screen Projection Plane
(0, 0) (W-1, 0) (Xmin, Ymax) (Xmax, Yma

(x, y) (X, Y)

(0, W-1) (W-1, H-1) (Xmin, Ymin) (Xmax, Ymi

Due to the same proportion:


x / (W-1) = (X – Xmin)/ (Xmax – Xmin)

y / (H-1) = (Ymax – Y)/ (Ymax – Ymin)


Screen Projection Plane
(0, 0) (1, 0) (Xmin, Ymax) (Xmax, Yma

(X, Y)

(x, y)

(0, -1) (1, -1) (Xmin, Ymin) (Xmax, Ymi

o the same proportion: Xmin = Ymin = -1


x / (W-1) = (X – Xmin)/ (Xmax – Xmin)
Xmax = Ymax = 1

y / (H-1) = (Y - Ymin)/ (Ymax – Ymin)


2 x’ = (X +1), X = 2x’ – 1
Similar Triangles
Corresponding sides have proportional lengths

C
c A
a

b B

a/A = b/B = c/C


Projection: side-view

By similar triangles:
y* / y = D / (D – z)
P(x,y,z)
So y* = y / ( 1 – z / D )

P*(x*,y*,0) y

y*
Eye
Z-axis
D z
View-plane
Projection: top-view
D z

Z-axis
x*
x
P*( x*, y*, 0 )
By similar triangles:
x* / x = D / ( D – z )
P( x, y, z )
So: x* = x / ( 1 – z / D )
The projection equations

• PointP( x, y, z ) in 3D-world is
“mapped” to pixel P*( x*, y* ) in the 2D-
viewplane:
x* = x / ( 1 –
z/D) y* = y /
(1–z/D)

• HereD is distance of eye from


viewplane
Any fixups needed?

•If the projected image is too small


or too big, it can be “rescaled”:
x’ = x*(scaleX); y’
= y*(scaleY);
•If the projected image is “off-
center”, it can be “shifted” (left or
right, up or down): x” =
x’+shiftX; y” = y’+shiftY;
Sample Background Art

You might also like