Presentation is loading. Please wait.

Presentation is loading. Please wait.

Advanced Graphics Algorithms Ying Zhu Georgia State University

Similar presentations


Presentation on theme: "Advanced Graphics Algorithms Ying Zhu Georgia State University"— Presentation transcript:

1 Advanced Graphics Algorithms Ying Zhu Georgia State University
Transformations, View, and Projection

2 Outline 3D transformations Concatenation of transformations
Transformations in Blender Transformations in OpenGL Anatomy of a simple OpenGL program

3 Geometric transformation
In 3D graphics, animation is achieved by transforming geometric objects in the virtual world Transforming geometric objects means transforming points (vertices) and vectors Transformation of points and vectors can be conveniently achieved by matrix multiplications Introduced by Larry Roberts in 1966

4 Linear Algebra Review Matrix: An array of elements
Vectors: A n x 1 matrix is called a column vector In computer graphics, vertex coordinates and vectors (directed line segments) and are represented by column vectors.

5 Linear Algebra Review Matrix-column vector multiplication

6 Linear Algebra Review Matrix-matrix multiplication V1 V2 V3

7 Linear Algebra Review Homogeneous representation
A vertex coordinate representation has 1 as the 4th component. OpenGL uses homogeneous coordinates for all vertices. (x, y, z) => (x, y, z, 1) Why use homogeneous coordinates? So that all transformations can be defined by matrix multiplication.

8 Affine Matrix An affine matrix is a 4x4 matrix with last low [0 0 0 1]
Why include the bottom row of (0,0,0,1)? It’ll be useful in perspective transforms. Affine matrix is used to define affine transformation.

9 Affine Transformation
Any transformation preserving collinearity (i.e., all points lying on a line initially still lie on a line after transformation) ratios of distances (e.g., the midpoint of a line segment remains the midpoint after transformation). Affine transformations preserve parallel lines. We can transform vertices with affine transformation. Affine transformations are what we use all the time in computer graphics.

10 Other Transformations
Isometry (preserves distance) Similarity (preserves angles) Collineation (lines remains lines) Perspective projections Non-linear (lines become curves) Twists, bends, etc. Can be used for deformation.

11 Elementary Affine Transformations
Four elementary affine transformations Translation Scaling Rotation (about an axis) Shear Translation, scaling and rotation are the most commonly used transformations.

12 Translation Translation P’ P d x y

13 Scaling Scaling

14 Rotation Rotation about the origin
Counter-clock wise rotation by 90 degree.

15 2D Transformation Summary
Translation Scaling Rotation

16 Composition of Affine Transforms
Most of the time we want to apply more than one transformation. Example: Translate by vector t Rotate by r degree about the origin This can be represented by multiple matrix multiplications:

17 Composition of Affine Transforms
Matrix multiplication is associative: We can group the combined transformation as The affine matrices are applied right side first. We can also apply all the transformations to form a single matrix This called composing or concatenation of transforms. The result is another affine transformation.

18 Composition of Affine Transforms
It is important to always remember that matrix multiplication is not commutative The order of transforms DOES matter E.g. scaling then rotating is usually different than rotating then scaling Keep this in mind when you write OpenGL program – you need to know in which order your OpenGL transform calls are executed

19 Why use matrix formalisms?
Can’t I just hardcode transforms rather than use the matrix multiplication? Yes. But it’s harder to derive, harder to debug, and not any more efficient All current graphics APIs use this matrix formalism Must understand this to use OpenGL or other graphics libraries The beauty of matrix formalism: the accumulated effect of many transformations can be stored in a single matrix

20 3D Affine Transformations
3D affine transformations are an extension of the 2D transformations. Use 4x4 matrices instead of 3x3 matrices in 2D transformations. For example 3D scaling

21 3D Translation 3D translation matrix
OpenGL function: glTranslatef(tx, ty, tz)

22 3D Scaling 3D scaling matrix OpenGL Function: glScalef(Sx, Sy, Sz)

23 3D Rotation About the Origin
3D rotation is more complicated than 2D rotation. Rotation about X axis Rotation about Y axis Rotation about Z axis

24 Arbitrary 3D Rotations Normal practice is to decompose an arbitrary 3D rotation into rotations about x, y, z axes. Create a single transformation matrix by composing.

25 Rotation in OpenGL glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) Equivalent to multiplying the following matrix

26 Rotation in OpenGL Rotations are specified by a rotation axis and an angle glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) Angle is in degrees x, y, and z are coordinates of a vector, the rotation axis Normal practice is to decompose an arbitrary 3D rotation into rotations about x, y, z axes Achieved by 3 glRotatef() calls

27 Rotation About a Fixed Point
The order is important: Translate to the origin Rotate about the origin Translate back Example: To rotate by r about z around

28 Rotation About a Fixed Point
E.g. Rotate around point (9, -5, 3), 30 degree about z axis, then 45 degree about y axis glTranslatef(9.0, -5.0, 3.0); glRotatef(45.0, 0.0, 1.0, 0.0); glRotatef(30.0, 0.0, 0.0, 1.0); glTranslatef(-9.0, 5.0, -3.0); glBegin(GL_POINTS); glVertex3f(…); glEnd();

29 Multiple 3D Transformations
By combining a scaling, rotation, and a translation matrix we get a single transformation matrix. Notice that in the single matrix, translation is separated from scaling and rotation part.

30 Transformations in OpenGL
Internally each vertex in OpenGL is represented as homogeneous points i.e. a column vector with 1 as the last item. However you don’t need to specify the 1 in your program Each vertex is transformed by the modelview matrix. There is always a current modelview matrix that is applied to all vertices. First tell OpenGL you’ll modify the modelview matrix by calling glMatrixMode(GL_MODELVIEW); You need to initialize the modelview matrix to the identity matrix through glLoadIdentity();

31 Model-view Matrix

32 Manipulating the Current Matrix
Two ways to modify the modelview matrix: Use OpenGL transformation calls (internally generate a matrix) Explicitly load pre-defined affine matrix as the current modelview matrix. We have to first tell OpenGL we’ll modify the modelview matrix by calling glMatrixMode(GL_MODELVIEW);

33 OpenGL Transformation Calls
Translation: glTranslatef(dx, dy, dz); Scaling: glScalef(sx, sy, sz); Rotation: glRotatef(angle, vx, vy, vz); Rotate object counter-clock wise around vector (vx, vy, vz) Angle is in degrees not radians. There is a matrix multiplication behind every transformation function call Current_MV_matrix  Current_MV_matrix * matrix

34 Example Pay attention to the patterns:
glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(10.0, 20.0, 0.0); glRotatef(30.0, 0.0, 1.0, 0.0); glBegin(GL_LINES); glVertex3d(0, 0, 0); glVertex3d(100, 120, 0); glEnd(); Pay attention to the patterns: Specify transformations first, and then draw 3D geometries. The order of transformations: rotate first, translate later. Transformations are applied in the reverse order – last transformation is applied first! This is what makes OpenGL difficult to learn.

35 Transformation in OpenGL
It is important to understand the relationship among Current modelview matrix, OpenGL transformation calls Vertices of 3D Object So you can understand why OpenGL code has to be arranged in certain way

36 What happens inside OpenGL during transformation?
Final modelview matrix Matrices created by OpenGL transformation calls Initial modelview matrix (initialized as an identity matrix) glMatrixMode(GL_MODELVIEW) glLoadIdentity() Vertex coordinates before transformation glVertex3f() Vertex coordinates after transformation

37 OpenGL matrix stack OpenGL maintains a matrix stack to temporarily store a “snapshot” of the current modelview matrix. We often need to back up the current modelview matrix, modify it, and then load the previous modelview matrix. glPushMatrix(); // Push the current matrix onto stack glPopMatrix(); // Pops the stack and load the top matrix // to be the current modelview matrix. This makes it possible to do hierarchical transformation glPushMatrix() and glPopMatrix() can be used to control the scope of transformation calls

38 Why use glPushMatrix()/glPopMatrix()?
glRotatef(theta[0], 0.0, 1.0, 0.0); // apply to all subsequent objects Draw_torso(); glPushMatrix(); glTranslatef(0.0, HEADX, 0.0); // only apply to head glRotatef(theta[1], 1.0, 0.0, 0.0); // only apply to head Draw_head(); glPopMatrix(); glRotatef(theta[3], 1.0, 0.0, 0.0); // apply to left_upper_arm and left_lower_arm Draw_left_upper_arm(); glTranslatef(0.0, LLAY, 0.0); // apply only to left_lower_arm glRotatef(theta[4], 1.0, 0.0, 0.0); // apply only to left_lower_arm Draw_left_lower_arm(); Draw_somthing_else(); // which transform(s) call is applied to this one?

39 Anatomy of an OpenGL program
/* * planet.c * This program shows how to composite modeling transformations * to draw translated and rotated models. * Interaction: pressing the d and y keys (day and year) * alters the rotation of the planet around the sun. */ #include <GL/glut.h> #include <stdlib.h> static int year = 0, day = 0; void init(void) { glClearColor (0.0, 0.0, 0.0, 0.0); // initialize frame buffer to black color (background) glShadeModel (GL_FLAT); // choose shading model }

40 Anatomy of an OpenGL program
void display(void) { glClear (GL_COLOR_BUFFER_BIT); glColor3f (1.0, 1.0, 1.0); glPushMatrix(); // current modelview matrix is the view matrix glutWireSphere(1.0, 20, 16); // draw sun, internally call glVertex3f() glRotatef ((GLfloat) year, 0.0, 1.0, 0.0); // Multiply modelview matrix with a // rotation matrix glTranslatef (2.0, 0.0, 0.0); // Multiply current modelview matrix with a // translation matrix glRotatef ((GLfloat) day, 0.0, 1.0, 0.0); // Multiply current modelview matrix with a // rotation matrix glutWireSphere(0.2, 10, 8); // draw smaller planet, internally call glVertex3f() glPopMatrix(); // current modelview matrix is view matrix again glutSwapBuffers(); }

41 Anatomy of an OpenGL program
void reshape (int w, int h) { glViewport (0, 0, (GLsizei) w, (GLsizei) h); // adjust image size based on // window size glMatrixMode (GL_PROJECTION); glLoadIdentity (); // initialize projection matrix gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // initialize modelview matrix to identity matrix // place camera, internally generates a view matrix and // then multiply the current modelview matrix with the view matrix gluLookAt (0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); }

42 Anatomy of an OpenGL program
void keyboard (unsigned char key, int x, int y) { switch (key) { case 'd': day = (day + 10) % 360; glutPostRedisplay(); // force a call to display() break; case 'D': day = (day - 10) % 360; glutPostRedisplay(); case 'y': year = (year + 5) % 360; case 'Y': year = (year - 5) % 360; case 27: exit(0); default: }

43 Anatomy of an OpenGL program
int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize (500, 500); glutInitWindowPosition (100, 100); glutCreateWindow (argv[0]); init (); // clear frame buffer and select shading model glutDisplayFunc(display); // register event handlers glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutMainLoop(); // Start the event loop return 0; }

44 Summary Affine transformations are used for transforming 3D objects.
Three elementary affine transformations: Translation Scaling Rotation 3D rotation is the most complicated among the three. If we use homogeneous coordinates, then all affine transformations can be represented by affine matrix multiplication. Notice the pattern of transformation in OpenGL programming.

45 OpenGL Tutorial Programs
Download OpenGL transformation tutorial program from

46 Readings OpenGL Programming Guide: Chapter 3 Sample programs
Cube.c, planet.c, robot.c

47 Outline Camera and view transformation
View volume and projection transformation Orthographic projection Perspective projection View port transformation

48 Outline

49 3D Scenes and Camera In computer graphics, we often use terms that are analog to theatre or photography A virtual world is a “scene” Objects in the scene are “actors” There is a “camera” to specify viewing position and viewing parameters. Viewing and projection is about how to position the camera and project the 3D scene to a 2D screen.

50 Camera in computer graphics
Computer graphics uses the pinhole camera model This results in perfectly sharp images No depth of field or motion blur Real cameras use lenses with variable aperture sizes This causes depth-of-field: out-of-focus objects appear blurry

51 Depth of field Depth of view is an important part of the storytelling process Direct viewer’s eyes to a certain area of the scene Depth of view can be faked in CG, but needs extra work

52 Motion Blur Camera in computer graphics does not generate motion blur either Again, it can be faked in CG but performance may suffer

53 Aspects of Viewing There are three aspects of the viewing process
Positioning the camera Setting the model-view matrix Selecting a lens Setting the projection matrix Clipping Setting the view volume

54 Positioning the camera
Each camera has a location and a direction The purpose of viewing transformation is to position the camera. In OpenGL, by default the camera is located at origin and points in the negative z direction

55 Positioning the camera
What if your camera is not at the origin, facing –Z axis? You use gluLookAt() function call to specify camera location and orientation Note the need for setting an up direction Example: glMatrixMode(GL_MODELVIEW): glLoadIdentity(); gluLookAt(1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0., );

56 Viewing Transformation in OpenGL
gluLookAt(eyex, eyey, eyez, atx, aty, atz, upx, upy, upz) atx, aty, atz will be mapped to the center of the 2D window

57 Moving the Camera No matter where the camera is initially, eventually it needs to be transformed to the origin, facing –Z axis in order to make the subsequent stages in the graphics pipeline very efficient But the picture taken by the (virtual) camera should be the same before and after the camera transformation How to achieve this? Construct a view transformation matrix to transform the camera to the origin, facing -Z Apply the view transformation matrix to every object in the virtual scene This view transformation is not visible Internally gluLookAt() creates such a matrix

58 What does gluLookAt() do?
gluLookAt(eyex, eyey, eyez, atx, aty, atz, upx, upy, upz) is equivalent to glMultMatrixf(M); // post-multiply M with current model-view matrix glTranslated(-eyex, -eyey, -eyez); Where M = u, n, v are unit vectors.

59 What does gluLookAt() do?
Internally OpenGL creates a view matrix and post-multiplies it with the current model-view matrix (normally an identity matrix) void reshape (int w, int h) { glViewport (0, 0, (GLsizei) w, (GLsizei) h); glMatrixMode (GL_PROJECTION); glLoadIdentity (); gluPerspective(65.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0., ); } Current_MV_matrix  current_MV_matrix * view_matrix

60 gluLookAt() gluLookAt() should be called before any OpenGL model transformation function calls That’s why gluLookAt() is normally placed in reshape() because reshape() is called before display() Normally model transformation calls are placed in display() function If you place transforms calls in other functions, pay attention to the call sequence

61 gluLookAt() Why? Because the view transformation matrix needs to be applied to every object (vertices) in the virtual scene All the model transformations have to be finished before the view transformation. Otherwise the realism is lost

62 Projection Transformation
Viewing transformation transform the camera to the origin and look-at direction with negative Z axis. Before one can actually render a scene, all relevant objects in the scene must be projected to some kind of plane or into some kind of simple volume After that, clipping and rendering are performed Projection transformation maps all 3D objects onto the viewing plane to create a 2D image.

63 Projection transformation

64 View volume (frustum) We need to define a view volume for projection
Everything inside the view volume will be projected to the 2D plane Everything outside the view volume will be “clipped” and ignored The job of projection transformation is to transform the view volume (and everything in it) to a canonical view volume Canonical view volume: a unit cube centered at origin It has minimum corner of (-1, -1, -1) and maximum corner of (1, 1, 1)

65 View volume (frustum)

66 Canonical view volume The coordinates in this volume is called Normalized Device Coordinates (NDC) The reason for transforming into the canonical view volume is that clipping is more efficiently performed there especially in hardware implementation The canonical view volume can also be conveniently mapped to 2D window by view port transformation

67 Projection transformation
Two basic projections in OpenGL: Orthographic projections Useful in applications where relative length judgment are important Can also yield simplifications where perspective would be too expensive, e.g. in some medical visualization applications Perspective projections Used in most interactive 3D applications

68 Orthographic Projection
Projection lines are orthogonal to projection surface

69 Orthographic Projection
Projection plane parallel to principal face Usually form front, top, side views isometric (not multiview orthographic view) front in CAD and architecture, we often display three multiviews plus isometric side top

70 Orthographic Projection
Preserves both distances and angles Shapes preserved Can be used for measurements Building plans Manuals Cannot see what object really looks like because many surfaces hidden from view Often we add the isometric view

71 Orthographic Projection in OpenGL
Specify an orthographic view volume in OpenGL glOrtho(left,right,bottom,top,near,far) Anything outside the viewing frustum is clipped. near and far are distances from camera

72 Orthographic projection
The matrix created by glOrtho() will transform the orthographic view volume to canonical view volume A cube that extends from -1 to 1 in x, y, z. This is often called the clip space. The next steps are clipping, perspective division, and view port transformation We’ll discuss clipping later

73 Perspective Projection
Projectors converge at center of projection

74 Advantages & Disadvantages
Objects further from viewer are projected smaller than the same sized objects closer to the viewer Make images look realistic Equal distances along a line are not projected into equal distances Angles preserved only in planes parallel to the projection plane More difficult to construct by hand than orthographic projection (but not more difficult by computer)

75 Perspective Projection in OpenGL
We define the perspective view volume by calling glFrustum(left,right,bottom,top,near,far) Again, near and far are distances from camera However, with glFrustum it is often difficult to get the desired view

76 Perspective Projection in OpenGL
gluPerpective(fovy, aspect, near, far) often provides a better interface However, it assumes that viewing window is centered about the negative Z axis aspect = w/h

77 Perspective Projection
The job of perspective projection is to transform this view volume (and everything in it) to a canonical view volume Everything outside of this volume will be clipped and ignored

78 What does gluPerspective() do?
Internally OpenGL creates the following matrix The current projection matrix is multiplied by this matrix and the result replaces the current matrix

79 Perspective Projection in OpenGL
There is a current projection matrix in OpenGL Before calling gluPerspective(), you need to tell OpenGL that you are modifying the projection matrix The following code would normally be part of the initialization routine glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(fovy, aspect, near, far); glMatrixMode(GL_MODELVIEW);

80 Aspect Ratio The aspect ratio in gluPerspective should match the aspect ratio of the associated viewport Normally, gluPerspective(fov, w/h, near, far) W is the width of your window, h is the height of your window You want the aspect ratio of your perspective view volume to match the aspect ratio of your window Otherwise, your final image will look distorted E.g. if you set aspect ratio to be a constant value, then when you resize your window, your image will look distorted

81 Perspective projection
The matrix created by glFrustum() or gluPerspective() will transform the perspective view volume to a canonical view volume A cube that extends from -1 to 1 in x, y, z. This is often called the clip space. The next steps are clipping, perspective division, and view port transformation

82 The journey of a vertex so far
The vertex will be transformed first by the model-view matrix and then by the projection matrix p’ = P x V x M x p E.g. glTranslatef(); glRotatef(); E.g. gluLookAt() E.g. gluPerspective()

83 After the perspective projection
As the result of the these transformations, vertex p is transformed to p’ After perspective projection, the 4th element of the homogenous coordinates of p’ may not be 1 any more We have to homogenize it to get the real coordinates Divide every element by the 4th element (called w) Normalized Device Coordinates After perspective transformation

84 Perspective division This operation is also called perspective division The homogenized coordinates are called Normalized Device Coordinates This is the coordinates for point p in the canonical view volume Now we are ready to map this point to the window

85 View port transformation

86 View port transformation
View port transformation transforms x and y from normalized device coordinates to window coordinates Note that z value of the normalized device coordinates are not transformed in this stage Because z axis is orthogonal to the window (2D plane) and Z values have no effect in the mapping Remember our eye is looking down negative Z But this z value is not lost. It will be passed down to the rasterizer stage Will be used in scan conversion and depth buffer test

87 View port transformation in OpenGL
glViewport(GLint x, GLint y, GLsizei width, GLsizei height) x, y: Specify the lower left corner of the viewport rectangle, in pixels. The initial value is (0,0). width, height: Specify the width and height of the viewport.

88 What does glViewport() do?
Let (xnd, ynd) be normalized device coordinates. Then the window coordinates (xw, yw) are computed as follows: xw = (xnd + 1)(width / 2) + x yw = (ynd + 1)(height / 2) + y It’s a matrix multiplication too. Now we know where to place this particular vertex in the final 2D image

89 glViewport() glViewport(GLint x, GLint y, GLsizei width, GLsizei height) X and y are normally set to (0, 0) Width and height are normally set to window width and height This means your 2D image size matches your window size You can make your image smaller or bigger than the window by adjusting those values E.g. put multiple images in one window

90 The journey of a vertex

91 Why do we spend so much time on transformations?
Because that’s where many people find OpenGL programs hard to understand You need to know those low level details when you write shader programs Your vertex shader, not OpenGL, are supposed to handle the model, view, and projection transformations In your vertex shader, you’ll have to deal directly with transformation matrices and matrix multiplications

92 Example /* planet.c*/ #include <GL/glut.h>
static int year = 0, day = 0; void init(void) { // Specify the color (black) used to clear the frame buffer (but not actually do it) glClearColor (0.0, 0.0, 0.0, 0.0); glShadeModel (GL_FLAT); // choose shading model }

93 Example void display(void) {
glClear (GL_COLOR_BUFFER_BIT); // Actually clear the frame buffer glColor3f (1.0, 1.0, 1.0); // set the color for each vertex glPushMatrix(); // current modelview matrix is the view matrix glutWireSphere(1.0, 20, 16); // draw sun, internally call glVertex3f() glRotatef ((GLfloat) year, 0.0, 1.0, 0.0); // Multiply modelview matrix with a // rotation matrix glTranslatef (2.0, 0.0, 0.0); // Multiply current modelview matrix with a // translation matrix glRotatef ((GLfloat) day, 0.0, 1.0, 0.0); // Multiply current modelview matrix // with a rotation matrix glutWireSphere(0.2, 10, 8); // draw smaller planet, internally call // glVertex3f() glPopMatrix(); // current modelview matrix is view matrix again glutSwapBuffers(); // swap double buffers }

94 Example void reshape (int w, int h) {
glViewport (0, 0, (GLsizei) w, (GLsizei) h); // Define view port transformation glMatrixMode (GL_PROJECTION); // We’re going to modify current projection matrix glLoadIdentity (); // initialize projection matrix to the identity matrix // create perspective projection matrix and post-multiply it with the current // projection matrix gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0); glMatrixMode(GL_MODELVIEW); // We’re going to modify current model-view matrix glLoadIdentity(); // initialize modelview matrix to identity matrix // place camera, internally generates a view matrix and // then post-multiply the current modelview matrix with the view matrix gluLookAt (0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); }

95 Example void keyboard (unsigned char key, int x, int y) {
switch (key) { case 'd': day = (day + 10) % 360; glutPostRedisplay(); // force a call to display() break; case 'D': day = (day - 10) % 360; glutPostRedisplay(); case 'y': year = (year + 5) % 360; case 'Y': year = (year - 5) % 360; case 27: exit(0); default: }

96 Example int main(int argc, char** argv) { glutInit(&argc, argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize (500, 500); glutInitWindowPosition (100, 100); glutCreateWindow (argv[0]); init (); // clear frame buffer and select shading model glutDisplayFunc(display); // register event handlers glutReshapeFunc(reshape); glutKeyboardFunc(keyboard); glutMainLoop(); // Start the event loop return 0; }

97 Summary Viewing and projection is about how to position the camera and project the 3D scene to a 2D screen. In OpenGL, view transformation is conducted by manipulating modelview matrix. Projection transformation is conducted by manipulating projection matrix. Viewport transformation is conducted by glViewport()

98 Readings OpenGL Programming Guide: chapter 3 OpenGL FAQ on Viewing


Download ppt "Advanced Graphics Algorithms Ying Zhu Georgia State University"

Similar presentations


Ads by Google