"use strict"; function main() { // Get A WebGL context /** @type {HTMLCanvasElement} */ const canvas = document.querySelector("#canvas"); const gl = canvas.getContext("webgl2"); if (!gl) { return; } const vs = `#version 300 es // an attribute is an input (in) to a vertex shader. // It will receive data from a buffer in vec4 a_position; // all shaders have a main function void main() { // gl_Position is a special variable a vertex shader // is responsible for setting gl_Position = a_position; } `; const fs = `#version 300 es precision highp float; uniform vec2 iResolution; uniform vec2 iMouse; uniform float iTime; // we need to declare an output for the fragment shader out vec4 outColor; vec3 palette( float t ) { vec3 a = vec3(0.5, 0.5, 0.5); vec3 b = vec3(0.5, 0.5, 0.5); vec3 c = vec3(1.0, 1.0, 1.0); vec3 d = vec3(0.263,0.416,0.557); return a + b*cos( 6.28318*(c*t+d) ); } //https://www.shadertoy.com/view/mtyGWy void mainImage( out vec4 fragColor, in vec2 fragCoord ) { vec2 uv = (fragCoord * 2.0 - iResolution.xy) / iResolution.y; vec2 uv0 = uv; vec3 finalColor = vec3(0.0); for (float i = 0.0; i < 4.0; i++) { uv = fract(uv * 1.5) - 0.5; float d = length(uv) * exp(-length(uv0)); vec3 col = palette(length(uv0) + i*.4 + iTime*.4); d = sin(d*8. + iTime)/8.; d = abs(d); d = pow(0.01 / d, 1.2); finalColor += col * d; } fragColor = vec4(finalColor, 1.0); } void main() { mainImage(outColor, gl_FragCoord.xy); } `; // setup GLSL program const program = webglUtils.createProgramFromSources(gl, [vs, fs]); // look up where the vertex data needs to go. const positionAttributeLocation = gl.getAttribLocation(program, "a_position"); // look up uniform locations const resolutionLocation = gl.getUniformLocation(program, "iResolution"); const mouseLocation = gl.getUniformLocation(program, "iMouse"); const timeLocation = gl.getUniformLocation(program, "iTime"); // Create a vertex array object (attribute state) const vao = gl.createVertexArray(); // and make it the one we're currently working with gl.bindVertexArray(vao); // Create a buffer to put three 2d clip space points in const positionBuffer = gl.createBuffer(); // Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer) gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); // fill it with a 2 triangles that cover clip space gl.bufferData( gl.ARRAY_BUFFER, new Float32Array([ -1, -1, // first triangle 1, -1, -1, 1, -1, 1, // second triangle 1, -1, 1, 1, ]), gl.STATIC_DRAW ); // Turn on the attribute gl.enableVertexAttribArray(positionAttributeLocation); // Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER) gl.vertexAttribPointer( positionAttributeLocation, 2, // 2 components per iteration gl.FLOAT, // the data is 32bit floats false, // don't normalize the data 0, // 0 = move forward size * sizeof(type) each iteration to get the next position 0 // start at the beginning of the buffer ); const playpauseElem = document.querySelector(".playpause"); const inputElem = document.querySelector(".divcanvas"); inputElem.addEventListener("mouseover", requestFrame); inputElem.addEventListener("mouseout", cancelFrame); let mouseX = 0; let mouseY = 0; function setMousePosition(e) { const rect = inputElem.getBoundingClientRect(); mouseX = e.clientX - rect.left; mouseY = rect.height - (e.clientY - rect.top) - 1; // bottom is 0 in WebGL } inputElem.addEventListener("mousemove", setMousePosition); inputElem.addEventListener( "touchstart", (e) => { e.preventDefault(); playpauseElem.classList.add("playpausehide"); requestFrame(); }, { passive: false } ); inputElem.addEventListener( "touchmove", (e) => { e.preventDefault(); setMousePosition(e.touches[0]); }, { passive: false } ); inputElem.addEventListener( "touchend", (e) => { e.preventDefault(); playpauseElem.classList.remove("playpausehide"); cancelFrame(); }, { passive: false } ); let requestId; function requestFrame() { if (!requestId) { requestId = requestAnimationFrame(render); } } function cancelFrame() { if (requestId) { cancelAnimationFrame(requestId); requestId = undefined; } } let then = 0; let time = 0; function render(now) { requestId = undefined; now *= 0.001; // convert to seconds const elapsedTime = Math.min(now - then, 0.1); time += elapsedTime; then = now; webglUtils.resizeCanvasToDisplaySize(gl.canvas); // Tell WebGL how to convert from clip space to pixels gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); // Tell it to use our program (pair of shaders) gl.useProgram(program); // Bind the attribute/buffer set we want. gl.bindVertexArray(vao); gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height); gl.uniform2f(mouseLocation, mouseX, mouseY); gl.uniform1f(timeLocation, time); gl.drawArrays( gl.TRIANGLES, 0, // offset 6 // num vertices to process ); requestFrame(); } requestFrame(); requestAnimationFrame(cancelFrame); } main();