Spaces:
Running
Running
| ; | |
| 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(); | |