From 4a1c77121be1b358813d7853384b4bac025c0ae5 Mon Sep 17 00:00:00 2001 From: EpicKiwi Date: Tue, 21 Apr 2026 01:08:11 +0200 Subject: [PATCH] Ajout du noise --- v1-com-officielle/glitcher/glitcher.js | 12 ++- v1-com-officielle/glitcher/lib/base.frag | 1 + v1-com-officielle/glitcher/lib/glitch.frag | 9 +- v1-com-officielle/glitcher/lib/glitch.vert | 7 +- v1-com-officielle/glitcher/lib/glitcher.js | 105 +++++++++++++++++++-- v1-com-officielle/glitcher/noise-power.png | Bin 0 -> 10552 bytes 6 files changed, 121 insertions(+), 13 deletions(-) create mode 100644 v1-com-officielle/glitcher/noise-power.png diff --git a/v1-com-officielle/glitcher/glitcher.js b/v1-com-officielle/glitcher/glitcher.js index 17a9e49..99bd0dc 100644 --- a/v1-com-officielle/glitcher/glitcher.js +++ b/v1-com-officielle/glitcher/glitcher.js @@ -14,7 +14,7 @@ async function updateFromForm(){ } else { UI_GLITCHER.clearImage() } - + UI_GLITCHER.render() } @@ -46,7 +46,7 @@ CANVAS.addEventListener("pointerdown", e => { let now = Date.now() if(lastCommit === null || now - lastCommit > 100){ - let width = 20; + let width = 50; if(e.pointerType == "touch"){ width *= 2 } @@ -55,8 +55,8 @@ CANVAS.addEventListener("pointerdown", e => { let deltaY = lastY !== undefined ? y - lastY : 0 width += Math.max(Math.abs(deltaX), Math.abs(deltaY)) + width += Math.random()*2 - //UI_GLITCHER.addGlitch(x + (Math.random() * width) - width/2, y + (Math.random() * width) - width/2, width) UI_GLITCHER.addGlitch(x, y, width) UI_GLITCHER.render() @@ -82,5 +82,11 @@ CANVAS.addEventListener("pointerdown", e => { applyGlitch(e) }) +/*function startRendering(){ + UI_GLITCHER.render() + requestAnimationFrame(startRendering) +} +startRendering()*/ +setInterval(() => UI_GLITCHER.render(), 500) updateFromForm() \ No newline at end of file diff --git a/v1-com-officielle/glitcher/lib/base.frag b/v1-com-officielle/glitcher/lib/base.frag index a5db85e..b497e6e 100644 --- a/v1-com-officielle/glitcher/lib/base.frag +++ b/v1-com-officielle/glitcher/lib/base.frag @@ -1,5 +1,6 @@ varying highp vec2 vTextureCoord; uniform sampler2D uImageSampler; +uniform highp vec2 uWindowSize; void main() { gl_FragColor = texture2D(uImageSampler, vTextureCoord); diff --git a/v1-com-officielle/glitcher/lib/glitch.frag b/v1-com-officielle/glitcher/lib/glitch.frag index 45da7f3..7d89115 100644 --- a/v1-com-officielle/glitcher/lib/glitch.frag +++ b/v1-com-officielle/glitcher/lib/glitch.frag @@ -1,3 +1,10 @@ +uniform highp vec2 uWindowSize; +uniform sampler2D uVideoSampler; +uniform sampler2D uNoiseSampler; +uniform lowp vec4 uRandom; + void main() { - gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); + gl_FragColor = texture2D(uVideoSampler, gl_FragCoord.xy/uWindowSize.xy) + + (texture2D(uNoiseSampler, (gl_FragCoord.xy/vec2(128, 128))+uRandom.zw) - 0.75) + * texture2D(uNoiseSampler, (gl_FragCoord.xy/vec2(128, 128))+uRandom.xw); } \ No newline at end of file diff --git a/v1-com-officielle/glitcher/lib/glitch.vert b/v1-com-officielle/glitcher/lib/glitch.vert index 847dc61..52b3582 100644 --- a/v1-com-officielle/glitcher/lib/glitch.vert +++ b/v1-com-officielle/glitcher/lib/glitch.vert @@ -1,9 +1,13 @@ -attribute vec4 aVertexPosition; +attribute vec2 aVertexPosition; +attribute vec2 aTextureCoord; uniform vec2 uGlitchPosition; uniform float uGlitchWidth; +uniform lowp vec4 uRandom; uniform vec2 uImageRatio; +varying highp vec2 vTextureCoord; + void main() { vec2 vertex = aVertexPosition.xy; @@ -11,6 +15,7 @@ void main() { vertex *= 0.001; vertex *= uGlitchWidth; vertex += uGlitchPosition; + vertex += uRandom.xy*0.01; gl_Position = vec4(vertex, -1.0, 1.0); } diff --git a/v1-com-officielle/glitcher/lib/glitcher.js b/v1-com-officielle/glitcher/lib/glitcher.js index 4ead33d..fbfb691 100644 --- a/v1-com-officielle/glitcher/lib/glitcher.js +++ b/v1-com-officielle/glitcher/lib/glitcher.js @@ -1,5 +1,7 @@ import resizeToFit from 'intrinsic-scale' +import noiseUrl from "../noise-power.png" + import baseVertSource from "./base.vert?raw" import baseFragSource from "./base.frag?raw" @@ -11,17 +13,25 @@ export class Glitcher { /** @type {HTMLCanvasElement} */ canvas - gliches = [] + glitches = [] /** * @param {HTMLCanvasElement} canvas */ constructor(canvas) { this.canvas = canvas + this.videoCanvas = new OffscreenCanvas(10, 10); + this.video = document.createElement("video") this.init() } init(){ + this.video.muted = true + this.video.loop = true + this.video.autoplay = true + this.video.src = "/background.webm" + this.video.addEventListener("canplay", e => this.video.play()) + this.ctx = this.canvas.getContext("webgl") if(!this.ctx){ throw new Error("WebGL isn't supported") @@ -56,13 +66,13 @@ export class Glitcher { aVertexPosition: this.ctx.getAttribLocation(baseProgram, "aVertexPosition"), aTextureCoord: this.ctx.getAttribLocation(baseProgram, "aTextureCoord"), uImageSampler: this.ctx.getUniformLocation(baseProgram, "uImageSampler"), + uWindowSize: this.ctx.getUniformLocation(baseProgram, "uWindowSize"), imageTexture: imageTexture, panelUVBuffer: panelUVBuffer, - panelPositionsBuffer: panelPositionBuffer + panelPositionsBuffer: panelPositionBuffer, } this.clearImage(); - let glitchPositionBuffer = this.ctx.createBuffer() this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, glitchPositionBuffer) const glitchPosition = [ @@ -72,8 +82,41 @@ export class Glitcher { 1.0, -1.0, ] this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(glitchPosition), this.ctx.STATIC_DRAW) - + let glitchProgram = createProgram(this.ctx, glitchVertSource, glitchFragSource) + let videoFrameTexture = this.ctx.createTexture() + + let noiseTexture = this.ctx.createTexture() + this.ctx.bindTexture(this.ctx.TEXTURE_2D, noiseTexture) + this.ctx.texImage2D( + this.ctx.TEXTURE_2D, + 0, + this.ctx.RGBA, + 1, 1, + 0, + this.ctx.RGBA, + this.ctx.UNSIGNED_BYTE, + new Uint8Array([0, 0, 0, 0]) + ) + + let noiseImage = new Image() + noiseImage.addEventListener("load", () => { + this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.glitch.noiseTexture) + this.ctx.texImage2D( + this.ctx.TEXTURE_2D, + 0, + this.ctx.RGBA, + this.ctx.RGBA, + this.ctx.UNSIGNED_BYTE, + noiseImage + ) + + this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_S, this.ctx.REPEAT); + this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_T, this.ctx.REPEAT); + this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MIN_FILTER, this.ctx.NEAREST); + this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MAG_FILTER, this.ctx.NEAREST); + }) + noiseImage.src = noiseUrl this.glitch = { program: glitchProgram, @@ -81,12 +124,20 @@ export class Glitcher { uGlitchPosition: this.ctx.getUniformLocation(glitchProgram, "uGlitchPosition"), uGlitchWidth: this.ctx.getUniformLocation(glitchProgram, "uGlitchWidth"), uImageRatio: this.ctx.getUniformLocation(glitchProgram, "uImageRatio"), - glitchPositionBuffer: glitchPositionBuffer + uWindowSize: this.ctx.getUniformLocation(glitchProgram, "uWindowSize"), + uVideoSampler: this.ctx.getUniformLocation(glitchProgram, "uVideoSampler"), + uNoiseSampler: this.ctx.getUniformLocation(glitchProgram, "uNoiseSampler"), + uRandom: this.ctx.getUniformLocation(glitchProgram, "uRandom"), + glitchPositionBuffer: glitchPositionBuffer, + videoFrameTexture: videoFrameTexture, + noiseTexture: noiseTexture } + + console.log(this.glitch) } clearImage(){ - this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.imageTexture) + this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.base.imageTexture) this.ctx.texImage2D( this.ctx.TEXTURE_2D, 0, @@ -192,14 +243,39 @@ export class Glitcher { this.canvas.width = resize.width this.canvas.height = resize.height + this.videoCanvas.width = this.canvas.width + this.videoCanvas.height = this.canvas.height this.ctx.viewport(0, 0, this.canvas.width, this.canvas.height); } + updateVideoTexture(){ + let vctx = this.videoCanvas.getContext("2d") + vctx.clearRect(0, 0, this.videoCanvas.width, this.videoCanvas.height) + vctx.drawImage(this.video, 0, 0, this.videoCanvas.width, this.videoCanvas.height); + + this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.glitch.videoFrameTexture) + this.ctx.texImage2D( + this.ctx.TEXTURE_2D, + 0, + this.ctx.RGBA, + this.ctx.RGBA, + this.ctx.UNSIGNED_BYTE, + this.videoCanvas + ) + + this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_S, this.ctx.CLAMP_TO_EDGE); + this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_WRAP_T, this.ctx.CLAMP_TO_EDGE); + this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MIN_FILTER, this.ctx.LINEAR); + this.ctx.texParameteri(this.ctx.TEXTURE_2D, this.ctx.TEXTURE_MAG_FILTER, this.ctx.LINEAR); + } + render(){ if(!this.ctx){ throw new Error("Glitcher not initialized, please run init()") } + this.updateVideoTexture() + this.ctx.clearColor(0.0, 0.0, 0.0, 0.0) this.ctx.clearDepth(1.0) this.ctx.enable(this.ctx.DEPTH_TEST) @@ -233,7 +309,8 @@ export class Glitcher { this.ctx.activeTexture(this.ctx.TEXTURE0) this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.base.imageTexture) - + + this.ctx.uniform2f(this.base.uWindowSize, this.canvas.width, this.canvas.height) this.ctx.uniform1i(this.base.uImageSampler, 0) this.ctx.drawArrays( @@ -254,8 +331,17 @@ export class Glitcher { 0, // (stride) how many bytes to get from one set of values to the next 0, // Start offset ) - this.ctx.enableVertexAttribArray(this.glitch.aVertexPosition) + + this.ctx.activeTexture(this.ctx.TEXTURE0) + this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.glitch.videoFrameTexture) + this.ctx.uniform1i(this.glitch.uVideoSampler, 0) + + this.ctx.activeTexture(this.ctx.TEXTURE1) + this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.glitch.noiseTexture) + this.ctx.uniform1i(this.glitch.uNoiseSampler, 1) + + this.ctx.uniform2f(this.glitch.uWindowSize, this.canvas.width, this.canvas.height) if(this.canvas.width > this.canvas.height){ this.ctx.uniform2f(this.glitch.uImageRatio, 1.0, this.canvas.width/this.canvas.height) } else { @@ -263,6 +349,9 @@ export class Glitcher { } for(let glitch of this.glitches) { + this.ctx.uniform4f(this.glitch.uRandom, + Math.random(), Math.random(), Math.random(), Math.random() + ) this.ctx.uniform2f(this.glitch.uGlitchPosition, glitch.x, glitch.y) this.ctx.uniform1f(this.glitch.uGlitchWidth, glitch.width) this.ctx.drawArrays( diff --git a/v1-com-officielle/glitcher/noise-power.png b/v1-com-officielle/glitcher/noise-power.png new file mode 100644 index 0000000000000000000000000000000000000000..47a02c51e1a2bea31b03045517679efb4b9e6ef8 GIT binary patch literal 10552 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