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

## Presentation on theme: "1 2D Graphics CIS 487/587 Bruce R. Maxim UM-Dearborn."— Presentation 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 (x 0, y 0 ) Advance x i = x i + 1 Advance y i = y i + 1 Plot (x i, y i ) Repeat

6 Bresenhem’s Algorithm 1.Starts at x 0, y 0 2.Plots x 0, y 0 3.x i = x i + 1 4.When advancing y i decide between plotting (x i, y i ) or (x i,y i - 1 ) Note: Algorithm treats separately 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 maxY 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 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;

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 max_clip_x) || (yc1 max_clip_y) || (xc2 max_clip_x) || (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 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 (x 0, y 0 ) to a new position (x t, y t ) The transformation would be x t = x 0 + dx and y t = y 0 + dy For motion dx and dy are the components of the velocity vector dx = cos v and dy = - sin v

28 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

29 Scaling (Changing Size) Multiply the coordinates of each vertex by the scale factor Everything will expand from the center The transformation would be x t = x 0 * scale and y t = y 0 * 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 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 be x t = x 0 * cos(  ) – y 0 * sin(  ) y t = y 0 * cos(  ) + x 0 * 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 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 (x 0, y 0 ) 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 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

41 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 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 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 P 0 (x 0, y 0 ) P 2 (x 2, y 2 ) P 1 (x 1, y 1 )

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 (x 0, y 0 ) set xs=xl= x 0 and y= y 0 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