CPSC 424 Computer Graphics Fall 2025

CPSC 424 Lab 3: Viewing and Geometry

Due: Tue 9/23 at the start of lab

This lab adds the viewing pipeline to the programmable pipeline structure from lab 2, and also introduces the indexed face representation for polygonal meshes.

Successful completion of this lab means that you:

Collaboration and Use of AI

This is an individual lab. You may get technical help from others, but the effort and ideas that go into producing solutions should be yours.

You may use AI as outlined in the Use of AI policy — Copilot's inline coding suggestions, explain, fix, review and comment features but not code generation from English prompts. Also:

Handin

Hand in your work by copying your ~/cs424/lab3 folder into your handin folder (/classes/cs424/handin/username, where username is your username).

Check that the result is that your files are contained in /classes/cs424/handin/username/lab3 — if not, fix it!


Preliminaries

Setup

Make sure that your lab3 and lib directories are named exactly like that and are at the same level in your workspace directory. This is important so that the relative path names used to access common files remain the same so your program doesn't break when you hand it in.

Provided Code

The lib directory contents that you copied contains several new files:

Live Preview Workaround Update

Live Preview now appears to be working on all Linux platforms! (VDI, Demarest 002, Lansing 310) Please let me know if you encounter issues.

Live Preview now appears to be working on the dual-boot computers in Demarest 002 (as well as in the VDI), but for now you'll need to continue using the workaround (start up Firefox manually) in Lansing 310.

Reference

Your best reference sources are the slides from class (which pull out and organize the key points), the examples from class (which put all the pieces together), and the textbook.


Exercises

Viewing Pipeline

The provided viewing.html file contains a WebGL program displaying a multicolored cube. It includes support for viewing and projection transforms.

Projections

In this exercise you'll modify the camera and/or projection transforms in order to implement specific projection types.

The provided projection.html file contains the same scene as viewing.html, but the code has been restructured slightly to accommodate runtime selection of a specific projection type.

projection.html contains everything needed for the scene...except for the camera and projection setup. Your task is to fill in the various cases of the two switch statements in the setView function to implement standard projection types: multiview orthographic (front, side, plan); axonometric (isometric, dimetric, trimetric); one-, two-, and three-point perspective; and oblique perspective. Note that cavalier, cabinet, and military projections are optional!

In many cases, there is more than one way to specify a projection of a given type. Choose settings where the effect of the projection is clear e.g. you can easily tell which axes have the same scaling and which are different for axonometric projections. Also, to best illustrate the effect of an oblique perspective projection, define it to use the same near clipping plane as your one-point perspective projection and position the camera so that the central cube in the scene is in approximately the same place for both projections.

For extra credit, also implement cavalier, cabinet, and military projections. This addresses oblique projections (including cavalier and cabinet) on pages 21-23. Some notes:

Military involves both the camera and an oblique projection — a hint is to think about which face is undistorted (still a square with 90 degree corners), the orientation of that face, and the scale factor of that face compared to the others.

Additional technical notes for oblique projections:

IFS Objects

basic-object-models-IFS.js and teapot-model-IFS.js provide definitions for many of the standard shapes: cube, sphere, torus, cylinder, cone, and teapot. (There's also a less-common one: ring.) Vertex positions and the face indices are defined explicitly for the teapot — access them as teapotModel.vertexPositions and teapotModel.indices as shown in the comment at the beginning of that file. (The normal vectors and texture coordinates aren't needed at this point.) The other shapes are defined as functions which return an object with the same information, for example

  let cubemodel = cube(1);

You can then use cubemodel.vertexPositions and cubemodel.indices. Look for the other function definitions in basic-object-models-IFS.js to find out the names and parameters for each shape. Note that in JavaScript it's not necessary to provide values for the rightmost parameters, so, for example, if you want a sphere of radius 1 with the default number of slices and stacks, you can write uvSphere(1).

An important note is that while these are defined as IFS objects — faces reference vertices by index — the format of the vertexPositions and indices arrays is not the same as the example discussed in class — these are triangular meshes rather than polygonal meshes and so the coordinates and faces lists are flat (1D) arrays of values rather than arrays-of-arrays (2D). Also, the arrays are already the typed arrays needed for passing as shader parameters rather than plain JavaScript arrays. This is easiest to see for the teapot model.

Other technical notes:

The provided geometry.html file is set up to draw one instance of one of the basic shapes at a time. It allows for solid and/or wireframe views. Your task, as detailed in the steps below, is to fill in the actual drawing-of-the-model part.

Modeling Transformations and Hierarchical Modeling

The provided scene1.html file is set up with same structure as geometry.html. In addition, a modelview stack (called stack) has been defined and there are push and pop statements in the "draw scene" section illustrating how to use it (along with an example of applying a modeling transform).

Technical notes:

The "museum of objects" scene should include at least four of the objects defined in basic-object-models-IFS.js and teapot-model-IFS.js, the house on page 5 of Monday's "specifying geometry" slides, and one object of your own where the vertex geometry is specified using an indexed face set representation. (For the latter, your object should include at least 10 faces.) Note that the house is a polygonal mesh and uses an array-of-arrays representation! (It will take slightly different handling to get data in the right format for passing to the shaders. Also, the syntax given on the slides is Java — translate it to JavaScript arrays.) You are encouraged to use a similar representation for your own object. Choose a different color for each object.

As in a real museum, each object should be displayed sitting atop a pedestal. Scale/orient each object in an interesting way — they shouldn't all be uniformly scaled and axis-aligned. Your pedestals can be as simple as tall boxes and/or cylinders, or you can get fancier. The pedestals should all be sitting on the floor, but they can be of varying size and height if you wish. Make all the pedestals the same color.

The pedestals should be solid (filled polygons). The objects can be solid or wireframe. For any solid objects (including the pedestals), also draw a wireframe outline version in a contrasting color to outline the edges of each face. (Otherwise the 3D shape isn't apparent in the absence of lighting.)

Utilize hierarchical modeling — position an object on its pedestal and then place the object+pedestal in the world rather than placing object and pedestal separately. Also, if you have any complex objects (such as a pedestal that is more than just a tall box or cylinder), create a function to place and draw all the shapes making up that object rather than repeating code for each instance of the complex object.

As you add more objects to a scene, the amount of code needed to link shader arguments and draw primitives quickly adds up. Create helper functions to pull all this repetition out of draw — you might consider, for example, a setView function which sets the shader parameters for the modelview and projection, a setModel function which sets the shader parameters for the geometry, and a drawModel which draws the primitives. (Up to you to decide is where to handle setting the color, and how to handle solid vs wireframe if you have both in your scene.)

Design your scene within a world coordinates box ranging from 0 to 20 in each dimension, then position and orient a camera to give a good view of the scene. You can use either an orthographic or a perspective projection. The provided scene1.html is set up with a simple rotator for the camera and a too-big view volume so that you can get your scene set up without having to worry about things not being visible. Adjust the projection at any point as you are building your scene, and set a fixed camera view (instead of the mouse rotator) at the end.

Articulated Structures

An articulated structure is made up of individual segments connected by joints, so that the segments can move relative to each other. Think of a leg or a crane or Pixar's cute little desk lamp as examples. There is typically a hierarchy present in these structures — bending your knee, for example, causes both your lower leg and foot to move, while flexing your ankle moves only your foot.

For extra credit, edit scene2.html to create an articulated structure with at least two joints and three segments. Each segment must involve at least two shapes and the structure must be modeled so that moving one joint affects everything beyond that. Animate each joint in some way to illustrate this property.

Technical notes: