Creative Coding with WebGL

Hi! I'm Lea Rosema

Slides to my talk

What is WebGL?

How to WebGL?

WebGL pipeline

(roughly)

  1. Buffers
  2. Vertex shader (processes buffer data)
  3. Rasterization (demo)
  4. Fragment shader (like: tixyland)
  5. Pixels on Screen 🟥 🟩 🟦

GL Shader Language

Vertex shader code

attribute vec4 position;

void main() {
  gl_Position = position;
}

Fragment shader code

precision highp float;

void main() {
  vec2 p = gl_FragCoord.xy;
  gl_FragColor = vec4(1.0, 0.5, 0, 1.0);
}

Passing Data from JS

JS

Get the WebGL Context

const gl = canvas.getContext('webgl');

...just like initializing a 2D canvas

JS: Compile Shaders

const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentCode);
gl.compileShader(fragmentShader);
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexCode);
gl.compileShader(vertexShader);

Like in C++, compile your shaders first.

JS: Create the program

const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram();

Like in C++, link. Together they form a WebGLProgram.

JS: Defining attributes for the vertex shader

const positionLoc = gl.getAttribLocation(program, 'position');
gl.enableVertexAttribArray(positionLoc);

Activate your attribute via enableVertexAttribArray

Assign a buffer to the attribute

// provide 2D data for a triangle
const data = [-1, -1, -1, 1, 1, -1];
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);

Create a buffer and provide data in a Float32Array

Set the attribute pointer

gl.vertexAttribPointer(
  positionLoc, // pointer to attribute
  2, // record size
  gl.FLOAT, // type
  false, // normalized
  0,
  0
);

This points the attribute to your data buffer

Passing uniform variables

const uTime = gl.getUniformLocation(program, 'time');
gl.uniform1f(uTime, tickCount);

Running it in JS

Draw

gl.drawArrays(gl.TRIANGLES);

Examples

Get pixel coordinates

uniform vec2 resolution;
vec2 p = (gl_FragCoord.xy / resolution - .5) * 2.;

// aspect ratio correction
float aspect = resolution.x / resolution.y;
p.x *= aspect;

Coordinates from varying

Vertex shader

attribute vec4 position;
varying vec4 vPosition;
vposition = position;

Fragment Shader

// contains interpolated values
varying vec4 vPosition;

Making 2D shapes with fragment shaders

SDF distance functions

float sdCircle(vec2 p, float radius) {
  return length(p) - radius;
}

float scene(vec2 p) {
  return sdCircle(p, 1.);
}

Combining shapes

float merge(float a, float b) {
  return min(a, b);
}

float substract(float a, float b) {
  return max(-a, b);
}

float symmetricDiff(float a, float b) {
  return max(min(a, b), -max(a, b));
}

Demos

Transforming coordinates

Rotate

vec2 rotate(vec2 p, float a) {
  return vec2(p.x * cos(a) - p.y * sin(a),
              p.x * sin(a) + p.y * cos(a));
}

Transforming coordinates

Repeat

vec2 repeat(in vec2 p, in vec2 c) {
  return mod(p, c) - 0.5 * c;
}

Thank you 👩‍💻

Further Resources