In this MP you will
This MP is elective, with no core components. It assumes you have already completed the WebGL warmup.
You will submit a webpage that has
We supply everything you need except for the GLSL. You should not edit our provided code, only add GLSL to it.
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.
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.
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.
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.