Write a program, using OpenGL, that draws a spiral shape, using transformations.
The spiral can be made up of lines, triangles, or anything else.
It can be static, or dynamic. It could be an object moving in spiral path.
Whatever suits your fancy.
Both are passed to graphics hardware via function calls
To draw something, the necessary state attributes (e.g. color) are set first
Then, the geometry (e.g. triangle data) is passed
State is retained until changed
State changes do not affect any geometry already drawn
glColor3f(1, 1, 0) # Set current color glBegin(GL_TRIANGLES) glVertex2f(0.0, 0.0) # This triangle is yellow glVertex2f(0.4, 0.0) glVertex2f(0.8, 0.8) glColor3f(1, 0, 1) # Change current color glVertex2f(0.0, 0.0) # This triangle is magenta glVertex2f(-0.4, 0.0) glVertex2f(-0.8, -0.8) glEnd()
A transformation is a function that converts a set of (X,Y,Z) coordinates into another set of (X,Y,Z) coordinates.
(So far we're working in 2D - (X,Y) coordinates - but OpenGL treats this as 3D coordinates with Z=0)
There are three common types of transformations:
Translation | Rotation | |
---|---|---|
Scaling |
A transformation is considered part of OpenGL's state.
Transformations are defined before the geometry that they should affect.
When drawing, vertex data is transformed by the currently active transformation.
glBegin(GL_TRIANGLES) glVertex2f(0.0, 0.0) glVertex2f(0.5, 0.0) glVertex2f(0.25, 0.5) glEnd() |
| |
glTranslatef(0.5, -0.2, 0.0) glBegin(GL_TRIANGLES) glVertex2f(0.0, 0.0) glVertex2f(0.5, 0.0) glVertex2f(0.25, 0.5) glEnd() |
|
Moves subsequent objects by (x, y, z) units.
Note: there is no 2D glTranslate function; for a 2D translation, pass 0 for z.
Example: basicXform.py
Rotates objects around the axis (x, y, z), by angle degrees.
In 2D, use 0, 0, 1 as the axis.
OpenGL rotations are right-handed - a positive angle rotates counter-clockwise (in 2D).
Rotations are around the origin, not necessarily the center of an object.
glBegin(GL_TRIANGLES) glVertex2f(0.5, 0.0) glVertex2f(0.8, 0.0) glVertex2f(0.65, 0.5) glEnd() |
| |
glRotatef(90.0, 0.0, 0.0, 1.0) glBegin(GL_TRIANGLES) glVertex2f(0.5, 0.0) glVertex2f(0.8, 0.0) glVertex2f(0.65, 0.5) glEnd() |
|
Scales objects by the factor x in the X direction, y in the Y direction, and z in the Z direction.
In 2D, pass 1 for z.
A uniform scale - changing the size of an object without distorting it - is done with x = y = z.
As with rotation, scaling is relative to the origin.
glBegin(GL_TRIANGLES) glVertex2f(0.5, 0.0) glVertex2f(0.8, 0.0) glVertex2f(0.65, 0.5) glEnd() |
| |
glScalef(0.25, 0.5, 1.0) glBegin(GL_TRIANGLES) glVertex2f(0.5, 0.0) glVertex2f(0.8, 0.0) glVertex2f(0.65, 0.5) glEnd() |
|
Individual transformations can be combined.
Each transformation call accumulates with all the previous transformations.
glColor3f(0, 0, 1) drawTriangle() glTranslatef(0.25, 0.25, 0.0) glColor3f(0, 1, 0) drawTriangle() glTranslatef(-0.25, -1.0, 0.0) glColor3f(1, 0, 0) drawTriangle() |
|
An identity transformation does nothing - it transforms a point (x,y,z) back into itself.
glLoadIdentity() will clear the current transformation back to identity.
Example: interactiveXform.py
Transformations are not, in general, commutative.
This means that applying the same transformations in different orders can produce different results.
no transformation | glTranslatef(0.5, 0, 0) glRotatef(45, 0, 0, 1) | glRotatef(45, 0, 0, 1) glTranslatef(0.5, 0, 0) |
Multiple transformations are effectively applied in reverse order.
i.e., the last transformation (the one immediately before the geometry
function calls) is the first one applied to the raw vertex data.
glTranslatef(0.5, 0, 0) glRotatef(45, 0, 0, 1) drawTriangle() | |
An object can be animated by adding transformations over time
i.e. making further calls to glTranslate / glRotate / glScale without
doing glLoadIdentity between frames
This does not work well in general - only for a single object
Best to keep track of a position & orientation (& size) for each object, and apply transformations from scratch on each frame
Example: animXform.py
The previously completed image is displayed while the new one is drawn.
The frame buffer is split into two buffers - front buffer (displayed) and back buffer (drawn into).
Useful for smooth animation.
Pass GLUT_DOUBLE flag to glutInitDisplayMode, instead of GLUT_SINGLE
Call glutSwapBuffers() at end of frame (replaces glFlush())
def draw(): glClear(GL_COLOR_BUFFER_BIT) ... glutSwapBuffers() glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB)