Custom Shaders in q5 WebGPU - q5js/q5.js GitHub Wiki

What's a shader?

WebGPU shaders are programs written in WGSL (WebGPU Shading Language) that draw shapes given an array of vertices. These shapes can be textured with an image or video frame. The color of pixels in the shape can also be determined by the result of an equation.

WGSL is a C-like, low-level, statically-typed shading language. It's a superior alternative to WebGL Shading Language (GLSL) because WebGPU provides more helpful error messages and better performance.

The primary components of a shader are:

  • the vertex function, which is run for every vertex
  • the fragment function, which computes the color of every pixel

Take Google's Tour of WGSL to learn more.

How does q5 make writing shaders easier?

q5's shader creation functions enable users to override the vertex and/or fragment functions in a copy of the respective default shader for drawing shapes, images, videos, or text. This makes it easier to get started with WGSL programming, without having to create the pipelines, data buffers, and all the other WebGPU stuff required to run shaders that render stuff.

Your custom shader code has access to the following data and helper function as part of every q5 default shader.

Data (updated every frame):

  • q: struct variable with q5 instance data members
    • width, height, halfWidth, halfHeight, pixelDensity, frameCount, time, deltaTime, mouseX, mouseY, mouseIsPressed, keyCode, keyIsPressed
  • transforms: array of 4x4 transformation matrices
  • colors: array of colors in [r, g, b, a] float format

Helper function:

  • transformVertex(pos: vec2f, matrixIndex: f32) -> vec4f applies a transformation matrix to a vertex, then converts it from canvas pixel coordinates to normalized device coordinates (NDC).

Shapes Shader

Source: https://github.com/q5js/q5.js/blob/main/src/shaders/shapes.wgsl

Vertex parameters:

  • vertexIndex: index of the vertex
  • pos: vertex position in canvas pixel coordinates
  • colorIndex: index of the color in the colors array
  • matrixIndex: index of the transformation matrix in the transforms array

Fragment parameters:

  • position: fragment position in NDC
  • color: fragment color in [r, g, b, a] float format

Image Shader

Source: https://github.com/q5js/q5.js/blob/main/src/shaders/image.wgsl

Vertex parameters:

  • vertexIndex: index of the vertex
  • pos: vertex position in canvas pixel coordinates
  • texCoord: texture coordinate in [u, v] float format
  • tintIndex: index of the tint color in the colors array
  • matrixIndex: index of the transformation matrix in the transforms array
  • imageAlpha: alpha value to apply to the image

Fragment parameters:

  • position: fragment position in NDC
  • texCoord
  • tintColor: tint color in [r, g, b, a] float format
  • imageAlpha

Data:

  • samp: sampler to use
  • tex: texture to sample

Functions:

  • applyTint(color: vec4f, tint: vec4f) -> vec4f: applies a tint to a color, using the tint's alpha as tinting strength

Text Shader

q5 WebGPU uses the MSDF technique to render text, more info:

https://github.com/q5js/q5.js/wiki/q5-WebGPU-renderer#text-rendering

Source: https://github.com/q5js/q5.js/blob/main/src/shaders/text.wgsl

Vertex parameters:

  • vertexIndex: index of the vertex
  • instanceIndex: index of the text character

Fragment parameters:

  • position: fragment position in NDC
  • texCoord: texture coordinate in [u, v] float format
  • fillColor: fill color in [r, g, b, a] float format
  • strokeColor: stroke color in [r, g, b, a] float format
  • strokeWeight: stroke weight

Data:

  • textChars: array of data for all the characters to render, stored in vec4f format
    • 0: x position
    • 1: y position
    • 2: index of the character in the text
    • 3: index of the text in textMetadata
  • textMetadata: array of Text text metadata
    • pos: position of the text
    • scale: scale of the text
    • fillIndex: index of the fill color in the colors array
    • strokeIndex: index of the stroke color in the colors array
    • strokeWeight: stroke weight
  • fontChars: array of Char font character data
    • size: size of the character
    • offset: offset of the character
    • texExtent: size of the character in the texture
    • texOffset: position of the character in the texture

Functions:

  • calcPos(i: u32, char: vec4f, fontChar: Char, text: Text) -> vec2f: calculates the vertex position in canvas pixel coordinates, given i the current vertex index
  • calcUV(i: u32, fontChar: Char) -> vec2f: calculates the UV mapping coordinates of the character in the font texture, given i the current vertex index
  • calcDist(texCoord: vec2f, edgeWidth: f32) -> f32: calculates the distance from the texture coordinate to the nearest edge of the glyph using the MSDF technique, larger edgeWidth values soften the edge of the glyph

Examples

Check out some examples! https://q5js.org/learn/#shadersSection