Where: Room 1.05 in CSSE
and online (active in consultation hour)
You can complete this lab under Linux. You may need to reboot
to choose the operating system. If you use the Linux platform, use
the .cpp and .glsl files supplied under the LINUX_VERSIONS subdirectory.
lab1
folder to lab2
- e.g., via:
cd ~/cits3003/labs-examples
cp -r lab1 lab2
q3triangleScene.cpp
to q1square.cpp
and remove the other questions
from lab2
(keep them in lab1
!)
mv q3triangleScene.cpp q1square.cpp
rm q1circle.cpp q2pointsScene.cpp
const int NumTriangles = 2; const int NumVertices = 3 * NumTriangles; vec3 points[NumVertices] = { vec3(-0.5, -0.5, 0.0), vec3(-0.5, 0.5, 0.0), vec3(0.5, -0.5, 0.0), vec3(0.5, 0.5, 0.0), vec3(0.5, -0.5, 0.0), vec3(-0.5, 0.5, 0.0) };
Here we use 3D coordinates just to avoid changing it later - for now all z-coordinates are 0.0 (which is
what OpenGL defaults to anyway if you draw using vec2's).
You'll need to change the second argument in the call to glVertexAttribPointer
from
2
to 3
. This is the number of components per vertex - see the
OpenGL reference page for this
function (for other OpenGL functions, see the
OpenGL 3.3 Reference Pages on the left of this web page).
Use the code above to initialize the triangles and build the program via make
and run it.
Compare with:
points
from above):
vec3 colors[NumVertices] = { vec3(0.0, 1.0, 0.0) /* Green, but choose any 6 colours */, ..., ..., vec3(/* ... */), ..., ... };
init
function from example4.cpp
, starting from the comment:
// Create a vertex array object
glEnable( GL_DEPTH_TEST );
init
function and the previous one is that it also sends the
color
array to the OpenGL buffer. Then vshader24.glsl
uses this
color information via the vColor
shader input variable.
What colours do you get in between the vertices? Experiment with different colours.
Instead we can just pass the angle of rotation to the vertex shader, and have it rotate all the vertex
positions by this angle. In fact we can just pass the time (since the program started) to the shader and have
it increase the angle over time.
q1square.cpp
to q2sqrotate.cpp
.
vshader24.glsl
to vrotate2d.glsl
. In the C++ program
q2sqrotate.cpp
, we will use vrotate2d.glsl
and
fshader24.glsl
together. In the vertex shader, add
the time variable:uniform float time; /* in milliseconds */
to the top of the file (above main
)
main
function in this vertex shader so that the position is set instead via:
float angle = 0.001*time; gl_Position = vec4(vPosition.x*cos(angle) - vPosition.y*sin(angle), vPosition.x*sin(angle) + vPosition.y*cos(angle), 0.0, 1.0);
[Why are there four components in gl_Position? Because OpenGL actually uses 4D
homogeneous coordinates - we'll come to this in lectures soon. For now the last component should
always be 1.0
.]
Here the angle is in radians, so the square rotates roughly every 6.28 seconds. Why sin
and
cos
here? Well, basically because that's what sin
and cos
were
invented for!
cos
tells you how much the original (x or y) coordinate affects the same coordinate when rotated.
sin
tells you how much the original (x or y) coordinate affects the other coordinate when rotated.
(It's negative in the line for the x coordinate above because the y-axis when rotated clockwise moves towards
the negative x-direction.)
See 2D rotation.
InitShader
to use your new vertex shader.
GLint
global variable called timeParam
to store
the location of the new shader variable time
. Set this global variable
in the init
function
(after the shaders have been loaded and "used") via:
timeParam = glGetUniformLocation(program, "time");
[This is a uniform variable which means that it doesn't change during the drawing of a primitive,
hence no array/buffer is needed unlike vPosition
and vColor
.]
time
variable before the call to glDrawArrays
via:
glUniform1f( timeParam, glutGet(GLUT_ELAPSED_TIME) );
[glUniform1f
sets a uniform parameter containing a single float to a value. Here the value is
that returned by glutGet(GLUT_ELAPSED_TIME)
which is the time (in milliseconds) since
glutInit was called. See glutGet
in the freeglut API (now in the links to the left of this page).
]
void idle(void)
that just calls glutPostRedisplay()
. Then
add a call to glutIdleFunc
in main
to set the idle function
to this function.
[Calling glutPostRedisplay
tells GLUT that the window needs to be redisplayed.
Here we call it in the idle function because there is constant motion, so we want to redisplay
whenever GLUT/OpenGL has nothing else to do and is idle. The actual redisplay will happen at some point after
the idle function returns, when GLUT is ready to redraw the window.
More generally, if you only require redrawing when a mouse event causes an object to move, or similar,
you should only call glutPostRedisplay()
when such a change occurs. Calling it multiple times
is fine if multiple changes occur before a redraw happens - it just sets a variable that GLUT uses to
remember that a redraw is required.]
GLUT_DOUBLE
to the glutInitDisplayMode
call (using bitwise-or, i.e., vertical bar),
and replace glFlush()
with glutSwapBuffers()
. This causes double buffering, which prevents flickering.
Then again, modern graphics hardware often automatically double buffers, so you may not see any difference, including
when you're using the lab machines.