A Second Set of
Sample Shader-Based Programs

We really can do more than just triangles!

All the source code for the examples on this page (minus some portions left as exercises) can be obtained from SampleProgramSet2_SourceCode.tar.gz during semesters that EECS 672 is being taught. It will uncompress into the indicated directory structure. Notice that we have added two new "utility" directories: cryphutil and fontutil. The former will be introduced in our first example below; the latter will be introduced in the second.

Nothing has changed in either the glslutil or the mvcutil directories.

♦ ♦
  1. colorwheel
    NEW: Runtime Geometry Computation; Use of cryph point & vector arithmetic tools; HSV to RGB

    Runtime Geometry Computation

    Now that you have explored color space issues using the Color Spaces tool, this example shows you how to build your own color wheel. We model a color wheel as a single circle filled in with colors of all hues. We create a piecewise linear (PWL) approximation of the circle, determining at runtime how many points to generate around the wheel. Doing so requires slightly more general calls to glBufferData. Since we want our color wheel to be solid, we use the points of our PWL approximation to define a triangle fan. The first vertex is at the center of the circle, and the rest are around the perimeter.

    Cryph point and vector arithmetic tools

    We will study points and vectors in affine and projective spaces more formally later, but we introduce here some simple tools for performing arithmetic that require only the most basic understanding of points and vectors. Creating models as the previous examples have illustrated (i.e., simply populating arrays of coordinates to be passed to glBufferData) is practical for only the simplest of scenes. It is often much more efficient to algorithmically compute coordinates. The cryph tools introduced here can simplify such geometric computations and make it easier to create many types of models.

    Before studying the use of the cryph tools in this example, study the examples on this page for a quick idea behind the toolkit.

    This more complete documentation for the cryph toolkit is also available.

    Notice how the main program in colorwheel.c++ creates the center point, orientation vectors, and radius that get passed to the ModelView constructor and used by ModelView::createColoredCircle. The first for loop in ModelView::createColoredCircle creates each point in turn around the perimeter of the circle, placing the coordinates into a vec3 array similar to what you saw in pwlApproxOfParabola and sendToGPU_AsVec3Array. The only difference is that here we add each point in turn to the vec3 array, rather than first buffering all points into an AffPoint array.

    The benefits may seem marginal here, but you will get a much better appreciation for how they can simplify geometry construction when you see in the next example how easy it is to change the orientation of our "bowtie".

    HSV to RGB

    To get the desired coloring, we could pass relevant information to the fragment shader, letting it determine the colors. Here we illustrate an alternative technique that exploits the way that per-vertex attributes work by associating explicit colors with the various vertices of our PWL approximation. To start, we associate pure white with the center of the circle (the first point of our "triangle fan" PWL approximation). The colors associated with the vertices around the perimeter of the circle are assigned a color based on an HSV value in which the hue is computed directly from the angle around the circle, and the saturation and value are each fixed at 1.0. The implementation of the HSV to RGB algorithm in this example was derived from the one presented on www.easyrgb.com.

    Note the changes to the calls to glBufferData and the required dynamic allocation and deletion of the client-side per-vertex attribute arrays.

  2. bowtie
    NEW: Distorted Circle; Text; Simple Animation

    Simple Geometry Deformation

    We can oftentimes use simple tricks to create interesting shapes. In addition to using theta to compute a hue, we also use it here to continuously modify the radius of the circle as: r = radius * (cos2(theta) + a)/(1 + a)". The arbitrary small positive constant, a, keeps the radius from ever reaching zero, and then dividing by (1+a) ensures that the original diameter does not change.

    This simple trick results in a "bowtie" sort of shape. This example reinforces the value of the cryph tools; look at how similar this version of createColoredCircle is to that in the previous example. Notice also how easy it is to adjust the angle at which our tilted bowtie appears.

    Text

    Text can be added to our scenes using an approach based on a modified version of some utilities written by George Sealy. (At one time, his description and code could be accessed here, however this link seems to be broken now.)

    My modified version of Sealy's utilities are in the fontutil directory. As you can see in the ModelView.c++ file here, the approach is fairly simple, although it does require some additional coordination between the client and the GLSL shaders. Specifically:

    1. ModelView constructor:
      1. Use CFont to retrieve a font definition. I have created several fonts in several sizes for your use in projects for this class. They are stored in the fontutil/fonts directory. Sealy's page (if you can find it) includes a description of how you can create additional fonts that can be used by these utilities. Notice that the font file to use in this example program comes from the main program (in main.c++), either from a command line parameter, or a default.
      2. Use CGLString to create the desired text string. Optionally use its various methods to size, place, and orient the string.
    2. ModelView::renderTheString (as called from ModelView::render):
      1. Set required uniform variables.
      2. Use the CGLString::renderString method to actually draw the text string.
      3. Indicate font rendering is complete by resetting the renderingFontString uniform variable.
    3. Fragment shader:
      1. The expected uniform variables are declared and used in a straightforward way. This code is easily migrated to other fragment shaders.

    Simple Animation

    Pressing the 'a' key turns on (or off) an animation in which the text rotates around the bowtie. Animations such as this require some sort of time interface. In GLFW, you can query the current "time" in seconds. By default, this will be the number of seconds since GLFW was initialized. The time is returned as a double precision value in seconds. The accuracy is system-dependent, but it is typically sufficiently accurate for basic animation.

    To use the GLFW approach to animation, you must instruct the GLFWController::run method not to wait for events, but instead to immediately invoke your display callback even if there are no pending events. (See calls to setRunWaitsForAnEvent in ModelView and the implementation of GLFWController::run.) During display callbacks (specifically during calls to ModelView::render), you can then check the current time to see whether you should make some change to the scene for your animation. In this example, that logic is implemented in ModelView::maybeRotateLabel which is called from ModelView::render.

    Full documentation on the GLFW timer interface can be found in the GLFW documentation on the GLFW web site.