Last active
June 22, 2025 13:11
-
-
Save benc-uk/a69abf99ad8c9e3254b4dfd5dd5bdb65 to your computer and use it in GitHub Desktop.
shader-demo.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as twgl from 'twgl.js' | |
const canvas = document.querySelector('canvas') | |
const gl = canvas.getContext('webgl2') | |
const vertShader = ` | |
#version 300 es | |
precision highp float; | |
in vec2 position; | |
out vec2 uv; | |
void main() { | |
gl_Position = vec4(position, 0.0, 1.0); // Set the position of the vertex | |
uv = position.xy * 0.5 + 0.5; // Convert from [-1, 1] to [0, 1] | |
} | |
` | |
// Shader that outputs pretty coloured grid | |
const frag1Shader = ` | |
#version 300 es | |
precision highp float; | |
uniform vec2 window_size; // Size of the window | |
in vec2 uv; | |
out vec4 color; | |
const float scale = 20.0; // Scale factor for the grid | |
float rand(vec2 co) { | |
// Mad bullshit that acts like a random function | |
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); | |
} | |
void main() { | |
vec2 pixel_pos = uv * window_size; // Convert UV to pixel coordinates | |
vec2 grid_pos = floor(pixel_pos / scale); // Create a grid every 10 pixels | |
float r = rand(grid_pos + 0.0); // Random value for red | |
float g = rand(grid_pos + 37.0); // Random value for red | |
float b = rand(grid_pos + 123.0); // Random value for red | |
if (r > 0.5) { | |
r = 1.0; // Set red to 1 if condition is met | |
} else { | |
r = 0.0; // Set red to 0 otherwise | |
} | |
color = vec4(r, g, b, 1.0); // Output the color | |
}` | |
// Shader that renders the input texture to the screen | |
// with a slight adjustment to the red channel for visibility | |
const frag2Shader = ` | |
#version 300 es | |
precision highp float; | |
in vec2 uv; | |
uniform sampler2D image; | |
out vec4 color; | |
void main() { | |
color = texture(image, uv); | |
color.r = color.r * 0.2 + 0.8; // Adjust red channel for visibility | |
} | |
` | |
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height) | |
const quadBuffers = twgl.createBufferInfoFromArrays(gl, { | |
position: { | |
numComponents: 2, | |
data: [-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1], | |
}, | |
}) | |
const prog1 = twgl.createProgramInfo(gl, [vertShader, frag1Shader]) | |
const prog2 = twgl.createProgramInfo(gl, [vertShader, frag2Shader]) | |
// Create a framebuffer to render the first pass into | |
const framebuffer = twgl.createFramebufferInfo(gl, undefined, gl.canvas.width, gl.canvas.height) | |
// ==== Pass 1: Render the grid ==== | |
gl.useProgram(prog1.program) | |
twgl.setBuffersAndAttributes(gl, prog1, quadBuffers) | |
twgl.setUniforms(prog1, { | |
window_size: [gl.canvas.width, gl.canvas.height], | |
}) | |
// This tells WebGL to render to the framebuffer instead of the screen | |
twgl.bindFramebufferInfo(gl, framebuffer) | |
twgl.drawBufferInfo(gl, quadBuffers, gl.TRIANGLES) | |
// ==== Pass 2: Render the framebuffer to the screen ==== | |
gl.useProgram(prog2.program) | |
twgl.setBuffersAndAttributes(gl, prog2, quadBuffers) | |
twgl.setUniforms(prog2, { | |
// This is the main trick: we use the texture from the framebuffer | |
image: framebuffer.attachments[0], | |
}) | |
// This tells WebGL to render to the screen | |
twgl.bindFramebufferInfo(gl, null) | |
twgl.drawBufferInfo(gl, quadBuffers, gl.TRIANGLES) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment