This page does not represent the most current semester of this course; it is present merely as an archive.
This assignment is unlike MP1 in two keys ways.
It was unanimously identified as the easiest MP of the semester by last semester’s students.
The primary goal is for you to explore. Other MPs have specific algorithms to implement and tasks to accomplish; this MP is mostly about trying things out. Tasks are vague and grade at full credit if they make a reasonable approximation of their description.
Largely the goal is familiarity with writing Javascript, WebGL2 and how they work together. Later MPs will test you ability to do specific things with WebGL2.
You will submit a webpage that has
As with the WebGL warmup, you’ll submit
an HTML file and any number of js, css, glsl, and json files. No image
files are permitted for this assignment. Also submit a file named
implemented.txt
listing any optional parts you believe you
completed.
You are welcome to use a JavaScript math library, such as the one used in in-class examples or others you might know.
As with most assignments, this is divided into required and optional parts. For full credit, you need all the required parts of each assignment and an accumulated average of 50 optional points per assignment.
This assignment, like all other assignments, is governed by the common components of MPs.
Your HTML file must contain
DOCTYPE
,
lang
, charset
, and title
.script
element in the head
,
and none outside the head
canvas
element.radio
button for the required part, plus more for
optional parts, all with the same name
attribute.label
for each radio
button briefly
describing what it will display. These should be a label from this page
(such as Required
or
GPU-based vertex movement
) followed by a colon and a short
description of what will be displayed (such as
Required: The "I" logo spinning clockwise while moving along a counterclockwise path
).radio
button defaults to being selected.We have an example HTML file that meets these requirements you might find a useful reference or starting point: radios.html.
requestAnimationFrame
Each call to requestAnimationFrame
queues a function to
be called in the future and returns a handle to that function. To have
an animation, we need to ensure there is always one (and only one)
pending function requested by requestAnimationFrame
. If we
have fewer than one, we don’t get an animation. If we have more than
one, the browser tries to do several things at once and will slow down
and eventually freeze.
When we have different animations we could use, we have to approaches to ensure the only-one rule:
When switching, cancelAnimationFrame
what used to be
pending and requestAnimationFrame
the new callback
function draw1(milliseconds) {
// ...
window.pending = requestAnimationFrame(draw1)
}function draw2(milliseconds) {
// ...
window.pending = requestAnimationFrame(draw2)
}function radioChanged() {
let chosen = document.querySelector('input[name="example"]:checked').value
cancelAnimationFrame(window.pending)
window.pending = requestAnimationFrame(window['draw'+chosen])
}
or
Have just one function that requestAnimationFrame
s
itself and dispatches to the right function for the current
animation
function draw1(milliseconds) {
// ... does not call requestAnimationFrame
}function draw2(milliseconds) {
// ... does not call requestAnimationFrame
}function timestep(milliseconds) {
let chosen = document.querySelector('input[name="example"]:checked').value
if (chosen == 1) draw1(milliseconds)
else if (chosen == 1) draw2(milliseconds)
requestAnimationFrame(timestep)
}
Either approach can work, but you can’t mix-and-match: pick one or the other.
Your javascript must break work into reasonable chunks, either as functions with meaningful names (preferred) or using whitespace and comments to separate parts of computation.
Comments should follow the Google JS commenting style. At a minimum, have a comment preceding each function, preferably in JSDoc style; additional comments are also encouraged.
Make a reasonable polygonal approximation of the University of Illinois logo. No need to model the curves where the vertical bar meets the serifs.
According to the Office of Strategic Marketing and Branding, the logo has RGB 255/95/5, which converted to 0–1 colors used by WebGL is (r,g,b) = (1, 0.373, 0.02). Sometimes the logo has a blue outline; if you wish to do that it should be RGB 19/41/75 meaning (r,g,b) = (0.075, 0.16, 0.292).
You should create this model by hand, such as by drawing the logo on graph paper. The by-hand requirement isn’t something we’ll even try to enforce or grade, but doing it that way will help build your brain’s ability to convert between shapes and coordinates, a valuable skill in graphics.
WebGL displays things between -1 and
+1 in x and y. If
you modeled the logo with different bounds, you can adjust it in the
vertex shader as e.g. by finding a scaling factor s
and
offsets dx
and dy
and using them as
gl_Position = vec4(vert.x*s + dx, vert.y*s + dy, 0, 1);
The logo should move about the canvas because its vertex positions are being modified by a matrix that varies with time. This means
requestAnimationFrame(
itself)
uniform mat4
in your vertex shaderYour animation should look smooth (no sudden jumps from one image to another) and should do at least two of
Do not squish or stretch the logo: any size changes should be uniformly applied to all dimensions.
You should have one radio button for the required part, and one radio button for each optional part you implement (labeled so that we can tell which part it was). Each animation (other than the required one) should show off a different optional part.
Add an animation that uses the I logo but changes the vertex positions in the buffer every frame. They should change individually in a way not achievable using a matrix (i.e. not just rotate, scale, translate, or perspective).
This involves two parts: some Javascript code that creates or updates
the list of positions in a Float32Array
in CPU memory and
connecting sending it to the GPU.
gl.createBuffer()
the buffer that will have
changing data, store the result in a global variable or another place
you can get at it again.gl.bufferData(..., gl.DYNAMIC_DRAW)
instead of the usual
gl.STATIC_DRAW
gl.bindVertexArray
and before
gl.drawElements
repeat the gl.bindBuffer
and
gl.bufferData
calls with the updated
Float32Array
.Add an animation that uses the I logo but changes the vertex positions in the buffer every frame. They should change individually in a way not achievable using a matrix (i.e. not just rotate, scale, translate, or perspective).
This involves a vertex shader that uses gl_VertexID
and
a time-varying uniform
as well as the model-specific
position attributes to position each vertex. Only the vertex shader
needs to change from the required portion.
Draw a quad that covers the whole canvas and use a fragment shader to create some kind of colorful pulsing pattern.
Two example animations are shown here; yours needn’t look like either of them but should similarly change with time, fill the screen with color, and vary nonlinearly across the screen instead of being interpolated from vertex colors.
Have the motion respond to the mouse in a sensible way: a logo moves towards or away from the mouse, follows a path similar to the mouse’s path, etc.
If the response is immediate (for example, if jerky mouse motions results in jerky animations) then this is worth 10 points. If the response uses the mouse but also has some sense of momentum or gradual change, it is worth 20 points.
mat4
each frame