diff --git a/www/css/shader-style.css b/www/css/shader-style.css index 2e87d83..9b39002 100644 --- a/www/css/shader-style.css +++ b/www/css/shader-style.css @@ -1,8 +1,11 @@ .gl-canvas-bg { + display:block; + width: 100vw; + height: 100vh; + position: fixed; left: 0; top: 0; - width: 100%; - height: 100%; + z-index: -1; } diff --git a/www/js/draw-scene.js b/www/js/draw-scene.js index 6405916..0df76cc 100644 --- a/www/js/draw-scene.js +++ b/www/js/draw-scene.js @@ -1,4 +1,7 @@ function drawScene(gl, programInfo, buffers, time) { + // Tell WebGL how to convert from clip space to pixels + gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); + gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque gl.clearDepth(1.0); // Clear everything gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); @@ -21,8 +24,8 @@ function drawScene(gl, programInfo, buffers, time) { // Viewport resolution in pixels gl.uniform2f( programInfo.uniformLocations.resolution, - gl.drawingBufferWidth, - gl.drawingBufferHeight, + gl.canvas.width, + gl.canvas.height, ); { diff --git a/www/js/webgl-demo.js b/www/js/webgl-demo.js index d300cc5..10deb74 100644 --- a/www/js/webgl-demo.js +++ b/www/js/webgl-demo.js @@ -3,10 +3,6 @@ import { drawScene } from "./draw-scene.js"; main(); -/* XXX: TODO: Avoid using alerts! Check return values instead, - * XXX: TODO: or create/use a Result like object. - */ - // Initialize a shader program, so WebGL knows how to draw our data function initShaderProgram(gl, vsSource, fsSource) { const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); @@ -43,73 +39,14 @@ function loadShader(gl, type, source) { // See if it compiled successfully if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { - alert( - `An error occurred compiling the shaders: ${gl.getShaderInfoLog(shader)}`, - ); gl.deleteShader(shader); - return null; + throw new Error(`An error occurred compiling the shaders: ${gl.getShaderInfoLog(shader)}`); } return shader; } -function main() { - const canvas = document.querySelector("#gl-canvas"); - // Initialize the GL context - const gl = canvas.getContext("webgl"); - - // Only continue if WebGL is available and working - if (gl === null) { - alert( - "Unable to initialize WebGL. Your browser or machine may not support it.", - ); - return; - } - - // Vertex shader program - const vsSource = ` - attribute vec4 aVertexPosition; - - void main() { - gl_Position = aVertexPosition; - } - `; - - // const fsSource = ` - // // is highp wasteful for this shader? - // #ifdef GL_FRAGMENT_PRECISION_HIGH - // precision highp float; - // #else - // precision mediump float; - // #endif - - // // shadertoy-like parameters - // // uniform vec2 uResolution; - // uniform float uTime; - - // void main() { - // vec2 uv = gl_FragCoord.xy/vec2(300, 300).xy; - - // // Time varying pixel color - // vec3 col = 0.5 + 0.5*cos(uTime+uv.xyx+vec3(0,2,4)); - - // // Output to screen - // gl_FragColor = vec4(col,1.0); - - // // gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); - // } - // `; - const fsSource = (async () => { - const res = await fetch("../shaders/segfault.glsl"); - if (!res.ok) { - const error = `Failed to load fragment shader source ${url}: ${res.status}`; - alert(error); - throw new Error(error); - } - - return await res.text() - })(); - +function renderShader(gl, vsSource, fsSource) { const shaderProgram = initShaderProgram(gl, vsSource, fsSource); // Collect all the info needed to use the shader program. @@ -121,8 +58,8 @@ function main() { vertexPosition: gl.getAttribLocation(shaderProgram, "aVertexPosition"), }, uniformLocations: { - resolution: context.getUniformLocation(program, "uResolution"), - time: gl.getUniformLocation(shaderProgram, "uTime"), + resolution: gl.getUniformLocation(shaderProgram, "u_resolution"), + time: gl.getUniformLocation(shaderProgram, "u_time"), }, }; @@ -145,5 +82,47 @@ function main() { requestAnimationFrame(render); } requestAnimationFrame(render); + + // XXX: TODO: read this guide it's great! https://stackoverflow.com/questions/56998225/why-is-rendering-blurred-in-webgl + // window.addEventListener('resize', render); +} + +function fetchShader(name) { + return fetch(`../shaders/${name}`) + .then(res => { + if (!res.ok) throw new Error(`Failed to load fragment shader source ${url}: ${res.status}`); + return res.text(); + }); + +} + +function main() { + const canvas = document.querySelector("#gl-canvas"); + // Initialize the GL context + const gl = canvas.getContext("webgl"); + + // XXX: TODO: use `window.addEventListener('resize', ...);` + canvas.setAttribute('width', window.innerWidth); + canvas.setAttribute('height', window.innerHeight); + + // Only continue if WebGL is available and working + if (gl === null) { + throw new Error("Unable to initialize WebGL. Your browser or machine may not support it."); + } + + // Vertex shader program + const vsSource = ` + attribute vec4 aVertexPosition; + + void main() { + gl_Position = aVertexPosition; + } + `; + + // Fetch fragment shader program + fetchShader("segfault.glsl") + .then(fsSource => { + renderShader(gl, vsSource, fsSource); + }); } diff --git a/www/shaders/segfault.glsl b/www/shaders/segfault.glsl index 78e9766..a3d73a8 100644 --- a/www/shaders/segfault.glsl +++ b/www/shaders/segfault.glsl @@ -4,16 +4,20 @@ * View this shader on shadertoy: https://www.shadertoy.com/view/t3tSRj# */ -#ifdef GL_ES -precision highp float; +// is highp wasteful for this shader? +#ifdef GL_FRAGMENT_PRECISION_HIGH + precision highp float; +#else + precision mediump float; #endif -uniform float uTime; -uniform vec2 uResolution; +uniform float u_time; +uniform vec2 u_resolution; /* ==== Text Colouring ==== */ #define PHOSPHOR_COL vec4(1, 1., 1., 1.) -#define BG_COL vec4(0.2, 0.0, 0.2, 0.) +// #define BG_COL vec4(0.2, 0.0, 0.2, 0.5) +#define BG_COL vec4(0.0, 0.0, 0.0, 1.) /* ======= Text Size ======= */ #define FONT_SIZE vec2(10.,20.) #define ROWCOLS vec2(80., 24.) @@ -151,9 +155,9 @@ float roundLine(vec2 p, vec2 a, vec2 b) { b -= a + vec2(1.0,0.); p -= a; float f = length(p-clamp(dot(p,b)/dot(b,b),0.0,1.0)*b); - if (uResolution.y < 320.) // attempt to get rid of aliasing on small resolution + if (u_resolution.y < 320.) // attempt to get rid of aliasing on small resolution return smoothstep(1.0, 0.9, f); - else if (uResolution.y < 720.) + else if (u_resolution.y < 720.) return smoothstep(0.75, 0.5, f); else return smoothstep(1., 0., f); @@ -301,7 +305,7 @@ float vt220Font(vec2 p, float c) { // https://www.shadertoy.com/view/MsdGWn // float textLines(vec2 uvG) { - float wt = 5. * (uTime + 0.5*sin(uTime*1.4) + 0.2*sin(uTime*2.9)); // wobbly time + float wt = 5. * (u_time + 0.5*sin(u_time*1.4) + 0.2*sin(u_time*2.9)); // wobbly time vec2 uvGt = uvG + vec2(0., floor(wt)); float ll = rand(vec2(uvGt.y, - 1.)) * ROWCOLS.x; // line length @@ -346,33 +350,22 @@ float smokeNoise(vec3 v) { return fbm5(v) / 2. + 0.5; } -// =================================================================== -// Graphical Styling / Effects -// -float bloom(vec2 uv2) { - // TODO - c += (textureLod(iChannel0, uvC, 3.) + - textureLod(iChannel0, uvC, 4.) + - textureLod(iChannel0, uvC, 5.)) - * smoothstep(0., -SMOOTH*20., stdRS(uvC, -0.02)) * 0.5; -} - void main() { - vec2 uv = vec2(gl_FragCoord.x, uResolution.y - gl_FragCoord.y); - vec2 uvT = ROWCOLS * FONT_SIZE * uv / uResolution.xy; - vec2 uvG = floor(ROWCOLS * uv / uResolution.xy); - vec2 uvC = gl_FragCoord.xy / uResolution; + vec2 uv = vec2(gl_FragCoord.x, u_resolution.y - gl_FragCoord.y); + vec2 uvT = ROWCOLS * FONT_SIZE * uv / u_resolution.xy; + vec2 uvG = floor(ROWCOLS * uv / u_resolution.xy); + vec2 uvC = gl_FragCoord.xy / u_resolution.xy; - vec2 uvNoise = uvC; + vec2 uvNoise = gl_FragCoord.xy / u_resolution.xy; uvNoise = ceil(uvNoise * ROWCOLS) / ROWCOLS; float val; - if (uTime < 2.0) + if (u_time < 2.0) val = textLines(uvG); - else if (uTime < 2.3) - val = rand(uvG * uTime) * 17.; + else if (u_time < 2.3) + val = rand(uvG * u_time) * 17.; else { - float noise = smokeNoise(vec3(uvNoise * noiseScale, uTime * noiseTimeScale)); + float noise = smokeNoise(vec3(uvNoise * noiseScale, u_time * noiseTimeScale)); // Noise is fed through a sigmoid function, then quantised to integer range 0-17 val = (exp(noise) / 2.71828); // increase contrast (normalised 0.0 - 1.0) val = 1.0 / val; @@ -381,6 +374,6 @@ void main() { val = pow(18.0, val) - 1.0; // TODO: try changing 18.0 to 200.0 and you'll notice some pretty changes :) } - gl_FragColor = vt220Font(uvT - uvG * FONT_SIZE, val) * PHOSPHOR_COL + BG_COL; + gl_FragColor = vt220Font(uvT - uvG * FONT_SIZE, val) * PHOSPHOR_COL + BG_COL; } diff --git a/www/shaders/trivial.glsl b/www/shaders/trivial.glsl new file mode 100644 index 0000000..22464ae --- /dev/null +++ b/www/shaders/trivial.glsl @@ -0,0 +1,18 @@ +// is highp wasteful for this shader? +#ifdef GL_FRAGMENT_PRECISION_HIGH + precision highp float; +#else + precision mediump float; +#endif + +uniform float u_time; +uniform vec2 u_resolution; + +void main() { + vec2 uv = gl_FragCoord.xy / u_resolution; + + vec3 col = 0.5 + 0.5 * cos(u_time + uv.xyx + vec3(0, 2, 4)); + + gl_FragColor = vec4(col,1.0); +} +