webgl实现canvas无限循环动画效果代码
代码语言:html
所属分类:动画
代码描述:webgl实现canvas无限循环动画效果代码
下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开
<html lang="en"><head> <meta charset="UTF-8"> <style> body { background: #666; margin: 0; overflow: hidden; } canvas { height: 100vh; width: 100vw; object-fit: contain; touch-action: none; } .osc { left: 0px; position: fixed; top: 0px; } .button { position: fixed; z-index: 10; right: 0; bottom: 0; } .controls { position: fixed; z-index: 10; left: 0; bottom: 0; } .playpause { background: #AAB; padding: 10px; } .playpause label { display: block; box-sizing: border-box; width: 0; height: 20px; cursor: pointer; border-color: transparent transparent transparent #202020; transition: 100ms all ease; will-change: border-width; border-style: double; border-width: 0px 0 0px 20px; } .playpause input[type='checkbox'] { visibility: hidden; } .playpause.checked label { border-style: double; border-width: 0px 0 0px 20px; } .playpause label { border-style: solid; border-width: 10px 0 10px 20px; } /* } */ #container { display: grid; justify-content: center; align-content: center; } </style> </head> <body translate="no"> <script id="vertexShader_buffer" type="x-shader/x-vertex">attribute vec4 a_position; uniform mat4 u_modelViewMatrix; uniform mat4 u_projectionMatrix; void main() { gl_Position = a_position; } </script> <script id="fragmentShader_buffer" type="x-shader/x-vertex"> precision highp float; uniform vec2 u_resolution; uniform float u_time; uniform vec2 u_mouse; uniform sampler2D u_noise; uniform sampler2D b_loop; vec2 getScreenSpace() { vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution.xy) / min(u_resolution.y, u_resolution.x); return uv; } float hash21(vec2 p) { p = fract(p * vec2(233.34, 851.74)); p += dot(p, p + 23.45); p = fract(p); return texture2D(u_noise, p).x; } void main() { vec2 uv = abs(getScreenSpace()); float t = u_time * .5; float s = sin(t); float c = cos(t); mat2 m = mat2(c, s, -s, c-s*.1); vec2 u = abs(uv-.5); vec2 samp = gl_FragCoord.xy / u_resolution; float n = hash21(samp+u_time); vec4 a = texture2D(b_loop, (fract(samp)*1.05*(m)-.5)) * (.99 - n*.05); float cl = smoothstep(0.0, 0.005, max(uv.x, uv.y) - .485); float al = clamp(cl + (smoothstep(.03, .0, min(u.y, u.x)) * (1.+n*.5)), 0., 1.); vec4 b = vec4(vec3(cl), al); gl_FragColor = mix(a,b,b.w); } </script> <script id="fragmentShader_under" type="x-shader/x-vertex"> precision highp float; uniform vec2 u_resolution; uniform float u_time; uniform vec2 u_mouse; uniform sampler2D u_noise; uniform sampler2D b_loop; vec2 getScreenSpace() { vec2 uv = (gl_FragCoord.xy - 0.5 * u_resolution.xy) / min(u_resolution.y, u_resolution.x); return uv; } void main() { vec2 uv = abs(getScreenSpace()); gl_FragColor = vec4( mix( texture2D(b_loop, gl_FragCoord.xy / u_resolution.xy).rgb, vec3(abs(sin(u_time*5.))), 0. //smoothstep(0.0, 0.002, max(uv.x,uv.y) - .47) ), 1.); } </script> <script type="module"> function _defineProperty(obj, key, value) {if (key in obj) {Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true });} else {obj[key] = value;}return obj;}function _classPrivateFieldSet(receiver, privateMap, value) {var descriptor = privateMap.get(receiver);if (!descriptor) {throw new TypeError("attempted to set private field on non-instance");}if (descriptor.set) {descriptor.set.call(receiver, value);} else {if (!descriptor.writable) {throw new TypeError("attempted to set read only private field");}descriptor.value = value;}return value;}function _classPrivateFieldGet(receiver, privateMap) {var descriptor = privateMap.get(receiver);if (!descriptor) {throw new TypeError("attempted to get private field on non-instance");}if (descriptor.get) {return descriptor.get.call(receiver);}return descriptor.value;}function _classStaticPrivateFieldSpecGet(receiver, classConstructor, descriptor) {if (receiver !== classConstructor) {throw new TypeError("Private static access of wrong provenance");}if (descriptor.get) {return descriptor.get.call(receiver);}return descriptor.value;}import { Vec2, Vec3, Mat2, Mat3, Mat4, Quat } from 'https://cdn.skypack.dev/wtc-math'; import gifJs from 'https://cdn.skypack.dev/gif.js'; console.clear(); const setup = function () { const px = 1; const dimensions = [1024, 1024]; const canvas = document.createElement('canvas'); const c = document.createElement('div'); c.id = 'container'; document.body.appendChild(c); c.appendChild(canvas); const renderer = new Renderer(canvas, { width: dimensions[0], height: dimensions[1], alpha: false, premultipliedAlpha: true, preserveDrawingBuffer: true, pxRatio: 1 }); const ctx = renderer.ctx; let drawing = new Float32Array([-1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0]); const drawBuffer = new Buffer(ctx, drawing); const vertexShader_buffer = document.getElementById('vertexShader_buffer').innerText; const programMain = new Program(ctx, vertexShader_buffer, document.getElementById('fragmentShader_under').innerText, { clearColour: [.15, .15, 0.25, 1.], renderType: Program.RENDER_STRIP }); const programBuffer = new Program(ctx, vertexShader_buffer, document.getElementById('fragmentShader_buffer').innerText, { clearColour: [.15, .15, 0.25, 1.], renderType: Program.RENDER_STRIP }); const loopBuffer = new FrameBuffer(renderer, 'loop', { width: dimensions[0], height: dimensions[1], tiling: Texture.IMAGETYPE_TILE, texdepth: FrameBuffer.TEXTYPE_FLOAT, pxRatio: 1 }); window.loopBuffer = loopBuffer; console.log(loopBuffer.texture, renderer.width); const time = new Uniform(ctx, 'time', Uniform.TYPE_FLOAT, 100); const uDelta = new Uniform(ctx, 'delta', Uniform.TYPE_FLOAT, 100); const mouse = new Uniform(ctx, 'mouse', Uniform.TYPE_V2, [0., 0.]); const noise = new Texture(ctx, 'noise', { textureType: Texture.IMAGETYPE_TILE, url: '//repo.bfw.wiki/bfwrepo/image/5dfd8574f13f0.png' }); noise.preload().then(n => { requestAnimationFrame(run); }); let pointerdown = false; let lastPos = new Vec2(); window.addEventListener('pointerdown', e => { pointerdown = true; lastPos = new Vec2(e.x, e.y); }); window.addEventListener('pointerup', e => { pointerdown = false; }); window.addEventListener('pointermove', e => { if (pointerdown) { let newPos = new Vec2(e.x, e.y); mouse.value = newPos.array; } }); let playing = true; const setPlaying = value => { playing = value; }; let autoTransitionTimer = 0; let timeToTransition = 0; const setupValues = i => { dimensions[0] = window.innerWidth; dimensions[1] = window.innerHeight; time.value = -100; }; setupValues(0); let timeout; const opx = renderer.pxRatio; let then = 0; // let framenum = 0; // let framesPerFrame = 10; // let gif = new gifJs({ // workers: 2, // quality: 10 // }); // gif.on('finished', function(blob) { // console.log('ss') // window.open(URL.createObjectURL(blob)); // }); // const offscreenCanvas = document.createElement("canvas"); // offscreenCanvas.className = 'osc'; // offscreenCanvas.width = canvas.width; // offscreenCanvas.height = canvas.height; // const osctx = offscreenCanvas.getContext("2d"); // document.body.appendChild(offscreenCanvas); let gifDone = false; const run = delta => { // if(framenum < 10 * framesPerFrame) { // if(framenum % framesPerFrame == 0) { // // gif.addFrame(canvas, {delay: 100}); // osctx.drawImage(canvas,0,0); // gif.addFrame(offscreenCanvas, {copy: true, delay: 100}); // // gif.addFrame(ctx, {copy: true}); // } // framenum++; // } else if(gifDone === false) { // console.log(framenum) // gif.render(); // window.gif = gif; // gifDone = true; // } let now = Date.now() / 1000; let _delta = now - then; then = now; if (_delta > 1000) { requestAnimationFrame(run); return; } if (playing) { uDelta.value = Math.min(_delta, 0.5); time.value += _delta * .05; renderer.setViewport(); renderer.setupProgram(programBuffer, [drawBuffer], [], [time, mouse, loopBuffer]); loopBuffer.render(4); renderer.setupProgram(programMain, [drawBuffer], [], [time, mouse, loopBuffer]); renderer.render(4); requestAnimationFrame(run); } }; }; // Determine whether a number is a power of 2 function powerOf2(v) { return v && !(v & v - 1); } // Return the next greatest power of 2 function nextPow2(v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } // Update a provided image to the nearest power of 2 in size. const pow2Image = c => { const newWidth = powerOf2(c.width) ? c.width : nextPow2(c.width); const newHeight = powerOf2(c.height) ? c.height : nextPow2(c.height); const _c = document.createElement('canvas'); const ctx = _c.getContext('2d'); _c.width = newWidth; _c.height = newHeight; ctx.drawImage(c, 0, 0, newWidth, newHeight); return _c; }; const asyncImageLoad = function (img, src) { return new Promise((resolve, reject) => { img.onload = () => resolve(img); img.onerror = reject; img.src = src; }); }; const glEnumToString = function () { const haveEnumsForType = {}; const enums = {}; function addEnums(gl) { const type = gl.constructor.name; if (!haveEnumsForType[type]) { for (const key in gl) { if (typeof gl[key] === 'number') { const existing = enums[gl[key]]; enums[gl[key]] = existing ? `${existing} | ${key}` : key; } } haveEnumsForType[type] = true; } } return function glEnumToString(gl, value) { addEnums(gl); return enums[value] || (typeof value === 'number' ? `0x${value.toString(16)}` : value); }; }(); const addExtensions = ctx => { // Set up the extensions ctx.getExtension('OES_standard_derivatives'); ctx.getExtension('EXT_shader_texture_lod'); ctx.getExtension('OES_texture_float'); ctx.getExtension('WEBGL_color_buffer_float'); ctx.getExtension('OES_texture_float_linear'); ctx.getExtension('EXT_color_buffer_float'); }; function createContext(c, opt_attribs, params) { const ctx = c.getContext("webgl", params) || this._el.getContext("experimental-webgl", params); addExtensions(ctx); return ctx; } const quatToMat4 = q => { if (q.array) q = q.array; // This just transforms a provided vector into to an array. if (q instanceof Array && q.length >= 4) { const [x, y, z, w] = q; const [x2, y2, z2] = q.map(x => x * 2.); const xx = x * x2, yx = y * x2, yy = y * y2, zx = z * x2, zy = z * y2, zz = z * z2, wx = w * x2, wy = w * y2, wz = w * z2; return new Mat4( 1 - yy - zz, yx - wz, zx + wy, 0, yx + wz, 1 - xx - zz, zy - wx, 0, zx - wy, zy + wx, 1 - xx - yy, 0, 0, 0, 0, 1); } };var _blending = new WeakMap();var _blendingEnabled = new WeakMap();var _buffers = new WeakMap(); class Renderer { constructor(canvas, options) {_defineProperty(this, "isWebgl2", false);_blending.set(this, { writable: true, value: void 0 });_blendingEnabled.set(this, { writable: true, value: false });_buffers.set(this, { writable: true, value: [] }); options = Object.assign({}, _classStaticPrivateFieldSpecGet(Renderer, Renderer, _defaultOptions), options); this.width = options.width; this.height = options.height; this.pxRatio = options.pxRatio; this.clearing = options.clearing; this.depthTesting = options.depthTesting; this.canvas = canvas || document.createElement('canvas'); this.canvas.width = this.width * this.pxRatio; this.canvas.height = this.height * this.pxRatio; this.premultipliedAlpha = options.premultipliedAlpha; this.ctx = this.canvas.getContext("webgl", options) || this.canvas.getContext("experimental-webgl", options); this.ctx.viewportWidth = this.canvas.width; this.ctx.viewportHeight = this.canvas.height; this.uniformResolution = new Uniform(this.ctx, 'resolution', Uniform.TYPE_V2, [this.canvas.width, this.canvas.height]); this.addExtensions(); } resize(w, h, ratio) { this.width = w; this.height = h; this.pxRatio = ratio || this.pxRatio; this.canvas.width = this.width * this.pxRatio; this.canvas.height = this.height * this.pxRatio; this.ctx.viewportWidth = this.canvas.width; this.ctx.viewportHeight = this.canvas.height; this.uniformResolution = new Uniform(this.ctx, 'resolution', Uniform.TYPE_V2, [this.canvas.width, this.canvas.height]); } setViewport(dimensions) { let w = this.width * this.pxRatio; let h = this.height * this.pxRatio; if (dimensions) { w = dimensions[0]; h = dimensions[1]; } this.ctx.viewport(0, 0, w, h); this.uniformResolution = new Uniform(this.ctx, 'resolution', Uniform.TYPE_V2, [w, h]); } addExtensions() { this.ctx.getExtension('OES_standard_derivatives'); this.ctx.getExtension('EXT_shader_texture_lod'); this.ctx.getExtension('OES_texture_float'); this.ctx.getExtension('WEBGL_color_buffer_float'); this.ctx.getExtension('OES_texture_float_linear'); this.ctx.getExtension('EXT_color_buffer_float'); } linkBuffer(buffer) { let hasBuffer = false; _classPrivateFieldGet(this, _buffers).forEach(b => { if (buffer === b) hasBuffer = true; }); if (!hasBuffer) { this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, buffer.buffer); this.ctx.bufferData( this.ctx.ARRAY_BUFFER, buffer.data, buffer.drawType); } buffer.link(this.currentProgram.program); } setupProgram(program, buffers, attributes, uniforms) { this.currentProgram = program; this.ctx.useProgram(program.program); this.premultiplied = program.premultiplied; this.depthTesting = program.depthTesting; if (program.blending === Program.BLENDING_NORMAL && program.transparent === false) { this.blending = Program.BLENDING_OFF; } else { this.blending = program.blending; } this.clearColour = program.clearColour; const a = this.clearColour[3]; // console.log('prem', this.premultipliedAlpha) if (this.premultipliedAlpha) this.clearColour = this.clearColour.map((c, i) => c * a); this.ctx.clearColor(...this.clearColour); // TODO: Unlink unused buffers during this setup phase as well. buffers.forEach(buffer => { this.linkBuffer(buffer); }); // this.ctx.enable(ctx.DEPTH_TEST); if (this.depthTesting) this.ctx.enable(this.ctx.DEPTH_TEST);else this.ctx.disable(this.ctx.DEPTH_TEST); uniforms.forEach(uniform => { uniform.bind(program.program); }); this.uniformResolution.bind(program.program); } render(points, buffer) { this.ctx.bindFramebuffer(this.ctx.FRAMEBUFFER, (buffer === null || buffer === void 0 ? void 0 : buffer.fb) || null); if (this.clearing) { this.ctx.clear(this.ctx.COLOR_BUFFER_BIT); if (this.depthTesting) this.ctx.clear(this.ctx.DEPTH_BUFFER_BIT); } switch (this.currentProgram.renderType) { case Program.RENDER_TRIANGLES: this.ctx.drawArrays(this.ctx.TRIANGLES, 0, points); break; case Program.RENDER_STRIP: this.ctx.drawArrays(this.ctx.TRIANGLE_STRIP, 0, points); break; case Program.RENDER_LINES: this.ctx.drawArrays(this.ctx.LINE_STRIP, 0, points); break; case Program.RENDER_LINELOOP: this.ctx.drawArrays(this.ctx.LINE_LOOP, 0, points); break; case Program.RENDER_POINTS: this.ctx.drawArrays(this.ctx.POINTS, 0, points); break;} } /* SETTERS AND GETTERS */ get blending() { return _classPrivateFieldGet(this, _blending) || Program.BLENDING_NORMAL; } set blending(blending) { if (blending === Renderer.BLENDING_DEBUG) { if (!this.breakLog) { console.log(blending, Renderer.BLENDING_OFF, this.premultiplied); this.breakLog = true; } _classPrivateFieldSet(this, _blending, blending); this.ctx.enable(this.ctx.BLEND); this.ctx.blendFuncSeparate(this.ctx.ONE, this.ctx.ONE_MINUS_SRC_ALPHA, this.ctx.ONE, this.ctx.ONE_MINUS_SRC_ALPHA); return; } _classPrivateFieldSet(this, _blending, blending); if (blending === Renderer.BLENDING_OFF) { this.ctx.disable(this.ctx.BLEND); _classPrivateFieldSet(this, _blendingEnabled, false); return; } if (_classPrivateFieldGet(this, _blendingEnabled) === false) { this.ctx.enable(this.ctx.BLEND); // this.ctx.alphaFunc(this.ctx.GL_GREATER, 0.5); // this.ctx.enable(this.ctx.GL_ALPHA_TEST); _classPrivateFieldSet(this, _blendingEnabled, true); } if (this.premultiplied) { switch (this.blending) { case Renderer.BLENDING_NORMAL: this.ctx.blendFuncSeparate(this.ctx.ONE, this.ctx.ONE_MINUS_SRC_ALPHA, this.ctx.ONE, this.ctx.ONE_MINUS_SRC_ALPHA); break; case Renderer.BLENDING_ADDITIVE: this.ctx.blendFunc(this.ctx.ONE, this.ctx.ONE); break; case Renderer.BLENDING_SUBTRACTIVE: this.ctx.blendFuncSeparate(this.ctx.ZERO, this.ctx.ZERO, this.ctx.ONE_MINUS_SRC_COLOR, this.ctx.ONE_MINUS_SRC_ALPHA); break; case Renderer.BLENDING_MULTIPLY: this.ctx.blendFuncSeparate(this.ctx.ZERO, this.ctx.SRC_COLOR, this.ctx.ZERO, this.ctx.SRC_ALPHA); break;} } else { switch (this.blending) { case Renderer.BLENDING_NORMAL: this.ctx.blendFuncSeparate(this.ctx.SRC_ALPHA, this.ctx.ONE_MINUS_SRC_ALPHA, this.ctx.ONE, this.ctx.ONE_MINUS_SRC_ALPHA); break; case Renderer.BLENDING_ADDITIVE: this.ctx.blendFunc(this.ctx.SRC_ALPHA, this.ctx.ONE); break; case Renderer.BLENDING_SUBTRACTIVE: this.ctx.blendFunc(this.ctx.ZERO, this.ctx.ONE_MINUS_SRC_COLOR); break; case Renderer.BLENDING_MULTIPLY: this.ctx.blendFunc(this.ctx.ZERO, this.ctx.SRC_COLOR); break;} } }}var _defaultOptions = { writable: true, value: { width: 512, height: 512, pxRatio: Math.min(window.devicePixelRatio, 2), clearing: true, depthTesting: true, premultipliedAlpha: true } };_defineProperty(Renderer, "BLENDING_DEBUG", -1);_defineProperty(Renderer, "BLENDING_NORMAL", 1);_defineProperty(Renderer, "BLENDING_ADDITIVE", 2);_defineProperty(Renderer, "BLENDING_SUBTRACTIVE", 4);_defineProperty(Renderer, "BLENDING_MULTIPLY", 8);_defineProperty(Renderer, "BLENDING_OFF", 16); class Buffer { constructor(ctx, data, options) { this.ctx = ctx; this.name = name; options = Object.assign({}, _classStaticPrivateFieldSpecGet(Buffer, Buffer, _defaults), options); this.attributes = options.attributes.map(a => Object.assign({}, _classStaticPrivateFieldSpecGet(Buffer, Buffer, _defaultAttribute), a)); this.normalized = options.normalized; this.drawType = options.drawType; this.type = options.type; if (data instanceof Array) data = new Float32Array(data); this.data = data; this.buffer = ctx.createBuffer(); } link(program, hasBuffer = false) { let location = this.ctx.getAttribLocation(program, `a_${this.name}`); this.attributes.forEach(attribute => { const location = this.ctx.getAttribLocation(program, `a_${attribute.name}`); this.ctx.vertexAttribPointer(location, attribute.numComponents, this.type, this.normalized, attribute.stride, attribute.offset); this.ctx.enableVertexAttribArray(location); }); } get length() { return this.data.length; }}var _defaultAttribute = { writable: true, value: { numComponents: 2, offset: 0, stride: 0 } };var _defaults = { writable: true, value: { attributes: [{ name: 'position' }], normalized: false, drawType: window.WebGLRenderingContext.STATIC_DRAW, type: window.WebGLRenderingContext.FLOAT } };var _vShader = new WeakMap();var _fShader = new WeakMap();var _p = new WeakMap();var _renderType = new WeakMap(); class Program { constructor(ctx, vertexShaderSource, fragmentShaderSource, options = {}) {_vShader.set(this, { writable: true, value: void 0 });_fShader.set(this, { writable: true, value: void 0 });_p.set(this, { writable: true, value: void 0 });_renderType.set(this, { writable: true, value: void 0 }); options = Object.assign({}, _classStaticPrivateFieldSpecGet(Program, Program, _defaultOptions2), options); this.ctx = ctx; this.renderType = options.renderType; this.clearColour = options.clearColour; this.blending = options.blending; this.premultiplied = options.premultiplied; this.transparent = options.transparent; this.depthTesting = options.depthTesting; // Create the shaders this.vShader = Program.createShaderOfType(this.ctx, this.ctx.VERTEX_SHADER, vertexShaderSource); this.fShader = Program.createShaderOfType(this.ctx, this.ctx.FRAGMENT_SHADER, fragmentShaderSource); // Create the program and link the shaders _classPri.........完整代码请登录后点击上方下载按钮下载查看
网友评论0