In this MP you will

  1. Fill the screen with fragments
  2. Compute a time- and space-varying color for each fragment

This MP is elective, with no core components. It assumes you have already completed the WebGL warmup.

1 Overview

You will submit a webpage that has

  • One canvas
  • A 2D animation of some kind of clearly non-polygonal moving color pattern

We supply everything you need except for the GLSL. You should not edit our provided code, only add GLSL to it.

2 Specification

2.1 HTML and Javascript (already done)

Use the HTML file psychedelic.html, which has built-in JavaScript. Do not edit this file: leave it exactly as it is. It depends on wrapWebGL2.js and should run with no errors or warnings in the console.

The JavaScript in that HTML file loads two GLSL files, vs.glsl and fs.glsl, which you’ll need to write.

2.2 Vertex Shader vs.glsl

This shader is invoked with no attribute data for six vertices, with gl_VertexID 0 through 5 inclusive. IDs 0, 1, and 2 will be combined into one triangle; 3, 4, and 5 will be combined into another.

Your implementation must set gl_Position based on gl_VertexID such that the entire canvas is covered in fragments.

Avoiding warp-breaking

The most direct way of writing the vertex shader would be

void main() {
  if (gl_VertexID == 0) {
    gl_Position = (-1,-1,0,1);
  } else if (gl_VertexID == 1) {
    gl_Position = ( 1,-1,0,1);
  } else if (gl_VertexID == 2) {
  // ...

but this has branches on inputs (in this case the built-in input gl_VertexID) which breaks warp parallelism and so is not appropriate.

One way to bypass this is to use expressions like ?: or || instead of if. Another is to come up with some function which, when given the inputs 0, 1, and 2, or the inputs 3, 4, and 5, or both outputs 2D points that, if filled with a triangle, completely cover the (-1,-1)-to-(1,1) square.

Your implementation should also create and set an out variable that indicates where on the screen each fragment is. You’ll need that information for the fragment shader to work properly. This could literally be a copy of gl_Position in an out variable if you wish.

Why copy gl_Position?

The (x,y) coordinates in gl_Position at the end of the vertex shader will be modified by the viewport transformation before being available as gl_FragCoord in the fragment shader, meaning that their values (and thus the appearance of anything drawn based on them) will depend on the size of the display. If we copy the gl_Position into a different out variable in the vertex shader, that out variable won’t have a viewport applied to it and will be available as an in in the fragment shader.

Technically we could pass the size of the screen in as a uniform and recover the original (x,y) from gl_FragCoord in the fragment shader by inverting the viewport transformation, but that would mean extra work every fragment, not a good use of GPU resources.

2.3 Fragment Shader fs.glsl

This should fill the screen with colorful moving curved shapes. There should be no detectable polygonal artifacts or sharp color transitions.

You should add a uniform float seconds; to this shader. It will be provided with the seconds since the animation began.

The easiest way to achieve the desired results is to take the x and y position of each fragment (passed from an out variable in the vertex shader to an in variable in the fragment shader) and the seconds uniform and put them through some kind of polynomial, likely with a sine or cosine on the seconds to make it repeat.

3 Evaluating your results

On both your development machine and when submitted to the submission server and then viewed by clicking the HTML link, the resulting animation should show moving colored patterns filling the screen with no visible polygonal boundaries. Changing the window size should change how large the image is, not how much of it is visible.

Two examples follow. Your submission should be different from both.

A video of an example result. Created by defining a 4th-order polynomial P_r(x,y), then setting the red channel to \sin(P_r(x,y) * t); and similarly with different polynomials for green and blue.
A video of an example result. Created summing various terms with the general form \sin(x c_1 \cos(c_2 t + c_3) + c_4) for various constants c_i, then modifying the result by a different simple function for each of red, green, and blue.