1 2d graphics cis 487/587 bruce r. maxim um-dearborn

50
1 2D Graphics CIS 487/587 Bruce R. Maxim UM-Dearborn

Upload: harrison-aster

Post on 14-Dec-2015

222 views

Category:

Documents


4 download

TRANSCRIPT

1

2D Graphics

CIS 487/587

Bruce R. Maxim

UM-Dearborn

2

Vector Graphics

• Advantages– No jagged lines–  Can only draw what is on the screen– Control electron gun directly (very fast)– Store line end points only

• Disadvantages– Best for wire frames– Have to draw everything as lines including circles– Can’t use TV technology

3

Raster Graphics

• Advantages– Cheaper– Can easily draw solid surfaces– Can move blocks and images around– Can control individual pixels

• Disadvantages– Memory intensive– Aliasing problems

4

Jaggies

• Here’s the problem with raster graphics

• A diagonal line does not always pass through the center of the pixels

5

This won’t work

• Compute the slope of the line

• Plot (x0, y0)

• Advance xi = xi + 1

• Advance yi = yi + 1

• Plot (xi, yi)

• Repeat

6

Bresenhem’s Algorithm

1. Starts at x0, y0

2. Plots x0, y0

3. xi = xi + 1

4. When advancing yi decide between plotting

(xi, yi) or (xi,yi - 1)

Note: Algorithm treats separately

m < 1 (<45°) and m > 1 (>45°)

Quadrants II, III, IV derived from I by symmetry

7

LaMothe Examples

8

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;

9

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

10

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

11

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

12

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;

13

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

14

Border Clipping

• Create a border that is a wide as any movable screen object

• Only draw the object

• Still need to detect when object will be off-screen

• Requires more memory

15

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

16

Object Space Clipping

• Object space (representation of figures)• Change object to one that does not need to

be clipped (e.g. chop triangle into a trapezoid) • New object passed to graphics engine• Without testing for clipping• More efficient than image space clipping• Lines are easy• Concave objects are tough

17

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

18

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

19

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

20

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;

21

Clip_Line( )// determine codes for p1 and p2if (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;

22

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

23

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

24

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

25

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);

26

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)

27

Translation (Moving)

• If you want to move a point (x0 , y0) to a new position (xt , yt)

• The transformation would bext = x0 + dx and yt = y0 + dy

• For motion dx and dy are the components of the velocity vectordx = cos v and dy = - sin v

28

Game_Main( )// draw all the asteroidsfor (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

29

Scaling (Changing Size)

• Multiply the coordinates of each vertex by the scale factor

• Everything will expand from the center

• The transformation would bext = x0 * scale and yt = y0 * scale

30

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

31

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);

32

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 bext = x0 * cos() – y0 * sin()

yt = y0 * cos() + x0 * sin()

33

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

34

Summary

• Drawing polygons is simply a matter of drawing lines and connecting end points

• Clipping is used to remove images outside viewing area

• Center polygons at (x0 , y0) and use local coordinates rather than world coordinates for the vertices

35

World Coordinates

• When using world coordinates you always need to perform the following steps– Translate to origin– Perform rotation– Translate back to new position

• It’s always a good idea to put sin(x) and cos(x) values in a lookup table to avoid recomputing them

36

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|

37

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|

38

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

39

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

40

Matrix Translationint 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

41

Matrix Scalingint 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

42

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);

43

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

44

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

45

Triangle Filling

P0 (x0 , y0)

P2 (x2 , y2) P1 (x1 , y1)

46

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

Breaking Up Polygons

1. If number of vertices left to process is greater then 3 continue to step 2

2. Take first 3 vertices and create a triangle

3. Split off triangle and recursively process remaining vetices

50

Breaking Up Quadrilaterals

inline void Draw_QuadFP_2D(int x0,int y0,

int x1,int y1,

int x2,int y2,

int x3, int y3,

int color,

UCHAR *dest_buffer, int mempitch)

{

// this function draws a 2D quadrilateral

 

// simply call the triangle function 2x, let it do all the work

Draw_TriangleFP_2D(x0,y0,x1,y1,x3,y3,color,dest_buffer,mempitch);

Draw_TriangleFP_2D(x1,y1,x2,y2,x3,y3,color,dest_buffer,mempitch);

} // end Draw_QuadFP_2D