Lab 7

Lab 7: Vertex Shader: Morphing

Due: Thursday, November 11th, 2010 (before lab session ends)

In this lab assignment, you will create a vertex shader that allows you to morph between two models at runtime to create a pose-based keyframe animation. You are given base code which loads in two models from disk, of the same character in two different poses, and displays the base pose. You must create a vertex shader that accepts the vertices for the second pose as attributes, and a uniform variable that controls the interpolation between the two poses (which should be based on time or user input). Your vertex shader will then interpolate between poses appropriately, and your fragment shader will just pass on the fragment color straight to screen.

NOTE: GLSL can be very touchy, and the best way to save time debugging is to avoid mistakes in the first place. Some big stumbling blocks:

The base code can be found at lab_07.tar.gz.

Note

You are expected to place your name in the main.cpp source file and comment any code you add or modify.

Input

The code you are given will have functions to read PLY files of the appropriate format; included with the base code are two models in PLY format, where each vertex contains:

These files do not contain texture coordinates, so your PLY readers from program 6 will not work. If a PLY reader for (position, normal, color) is needed for future assignments, you may reuse the PLY loader function given here.

Simple Shaders

Your first goal is to create basic shader programs and get them attached and running. You must include the GLEW headers, and call glewInit() after you've obtained an OpenGL context. You must create a shader program, and then load, create, and attach a very basic vertex and fragment shader. Remember that the key output of the vertex shader is the transformed vertex position, ``gl_Position``, and the key output of the fragment shader is the fragment color, ``gl_FragColor``. Your vertex shader should just transform the vertex into clip space, and store the color in a vec4 so that the fragment shader can access it:

varying vec4 theColor;
void main()
{
    theColor = gl_Color;
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

Your fragment shader base is even simpler; it should just pass the color through to the framebuffer:

varying vec4 theColor;
void main()
{
    gl_FragColor = theColor;
}

For the first part of the assignment, load in these simple vertex and fragment shaders and compile and link them appropriately (hint: refer to the shading lecture slides). The base code provides a function, readTextFile(), to help you out, since the glShaderSource() function expects your shader code as one single, large string. Once your basic shaders are loaded and compile correctly, make sure to use the shader program before rendering with glUseProgam(), and you should see the following:

lab_07/waddles_basic.png

Uniforms

Once you have your basic shader programs in place, add a uniform variable (a float) to your vertex shader and pass it the elapsed time with glutGet(GLUT_ELAPSED_TIME). To test that it is working, try modifying the Y value of gl_Vertex based on sin(time). Remember that to get a uniform working correctly with your shader, you must:

Make sure that you've bound the current shader program (with glUseProgram()) before attempting to query the uniform location or set the uniform's value. (Again, consult the lecture notes if you get stuck.)

Attributes

Now that you can pass uniforms into your shader program, add vertex attributes as well. Remember that attributes are per-vertex: you only need one attribute in your vertex shader, e.g.:

attribute vec4 morphTarget;

And then you must specify the value of this attribute before each call to glVertex3f(), e.g.:

glAttribute4f(attribLocation, morphTargets[i][0], morphTargets[i][1], morphTargets[i][2], 1.0f);
glVertex3f(verts[i][0], verts[i][1], verts[i][2]);

And you obtain the location of an attribute in the same way as you would with uniforms (i.e., glGetAttribLocation).

Your morph shader should work as follows: for each vertex in your base model, pass the corresponding vertex from the morphed model as an attribute. Then, inside your vertex shader, use linear interpolation to morph between the two (e.g. change the value that gets transformed to become gl_Position). When your interpolant is 0, your vertex shader will transform the base model, and when it is 1, your shader will transform the final pose. Your program should change the interpolant based on sin(time). Note that since sin(time) ranges from -1.0 to 1.0, you will have to scale it so that it is in the range of 0.0 to 1.0.

When this works, you are done; your model should smoothly interpolate between the poses below:

lab_07/waddles_base.png lab_07/waddles_wacky.png

Submission

Demonstrate the executable to the instructor or TA before the lab session is over. Once verified, place the source code and Makefile in your course dropbox:

/afs/nd.edu/coursefa.10/cse/cse40166.01/dropbox/<afsid>/lab7