WebGL 2 shader

This commit is contained in:
Christian Lawson-Perfect 2025-05-16 06:32:01 +00:00
commit 61faec2e92
4 changed files with 194 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.make.*

42
fragshader.glsl Normal file
View file

@ -0,0 +1,42 @@
#version 300 es
#define PI 3.1415926538
#define maxIterations 100
precision mediump float;
uniform float t;
uniform vec2 screen_size;
in vec4 v_pos;
out vec4 fragColor;
vec2 squareImaginary(vec2 number){
return vec2(
pow(number.x,2.)-pow(number.y,2.),
2.*number.x*number.y
);
}
float iterateMandelbrot(vec2 coord) {
vec2 z = vec2(0.,0.);
for(int i=0; i<maxIterations; i++) {
float l = length(z);
z = squareImaginary(z) + coord;
if(l > 2.) {
return float(i)/float(maxIterations);
}
}
return 1.;
}
void main(void) {
float aspect = screen_size.x / screen_size.y;
mat2 squash = mat2(aspect, 0., 0., 1.);
vec2 pos = squash * v_pos.xy;
float r = length(pos.xy);
vec2 centre = vec2(0.367,0.6955);
float bounce = 1.;//(cos(t*0.1) + 1.)/2.;
float zoom = 0.5 / (1. + 200.*(1.-bounce));
//float zoom = 1.;
float m = iterateMandelbrot(pos * zoom + centre);
float c = m==1. ? 1. : m*0.8;
vec4 color = vec4(c, c, c, 1.0);
fragColor = color;
}

23
index.html Normal file
View file

@ -0,0 +1,23 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>A thing made by CLP</title>
<script type="module" src="script.js"></script>
<style>
body {
margin: 0;
}
canvas {
width: 100svw;
height: 100svh;
}
</style>
</head>
<body>
<canvas id="glCanvas" width="600" height="450"></canvas>
<p><a href="https://www.khronos.org/files/webgl/webgl-reference-card-1_0.pdf">WebGL reference card</a></p>
<pre id="errors"></pre>
</body>
</html>

128
script.js Normal file
View file

@ -0,0 +1,128 @@
console.clear();
function error(...msgs) {
const msg = msgs.join('\n');
console.error(msg);
document.getElementById('errors').textContent = msg;
}
function loadShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
error('An error occurred compiling the shaders:', gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
function initShaderProgram(gl, vs, fs) {
const shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vs);
gl.attachShader(shaderProgram, fs);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
error('Unable to initialize the shader program:', gl.getProgramInfoLog(shaderProgram));
return null;
}
return shaderProgram;
}
function initBuffers(gl) {
const vertices = new Float32Array([
-1, 1,
1, 1,
-1, -1,
-1, -1,
1, 1,
1, -1
]);
const vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
return vertexBuffer;
}
async function init() {
const vsSource = `#version 300 es
in vec4 aVertexPosition;
out vec4 v_pos;
void main(void) {
gl_Position = aVertexPosition;
v_pos = gl_Position;
}
`;
const fsSource = await (await fetch('fragshader.glsl')).text();
console.log(fsSource);
function set_canvas_size() {
const dpr = window.devicePixelRatio;
const {clientWidth: width, clientHeight: height} = canvas;
canvas.width = Math.round(width * dpr);
canvas.height = Math.round(height * dpr);
}
function drawScene() {
gl.clear(gl.COLOR_BUFFER_BIT);
const vertexPosition = gl.getAttribLocation(shaderProgram, 'aVertexPosition');
const tLoc = gl.getUniformLocation(shaderProgram, "t");
const screen_sizeLoc = gl.getUniformLocation(shaderProgram, "screen_size");
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
const t = (new Date() - t1) / 1000;
gl.uniform1f(tLoc, t);
gl.uniform2fv(screen_sizeLoc, [canvas.width, canvas.height]);
gl.vertexAttribPointer(vertexPosition, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(vertexPosition);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.useProgram(shaderProgram);
gl.drawArrays(gl.TRIANGLES, 0, 6);
requestAnimationFrame(drawScene);
}
let mx = 0, my = 0;
const canvas = document.getElementById('glCanvas');
set_canvas_size();
const gl = canvas.getContext('webgl2');
if (!gl) {
throw new Error('Unable to initialize WebGL. Your browser may not support it.');
}
if (gl) {
gl.clearColor(0.0, 0.0, 0.0, 1.0);
}
const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
const shaderProgram = initShaderProgram(gl, vertexShader, fragmentShader);
const vertexBuffer = initBuffers(gl);
const t1 = new Date();
drawScene();
const resize_observer = new ResizeObserver((entries) => set_canvas_size());
resize_observer.observe(canvas);
}
init();