Ajout d'entités "glitch" a la scene

This commit is contained in:
2026-04-20 23:11:05 +02:00
parent f1229866d1
commit 3d5bef4153
5 changed files with 231 additions and 69 deletions
+60 -2
View File
@@ -1,14 +1,16 @@
import { Glitcher } from "./lib/glitcher"; import { Glitcher } from "./lib/glitcher";
const UI_GLITCHER = new Glitcher(document.getElementById("ui-canvas")) const CANVAS = document.getElementById("ui-canvas")
const UI_GLITCHER = new Glitcher(CANVAS)
const FORM = document.getElementById("glitcher-form") const FORM = document.getElementById("glitcher-form")
async function updateFromForm(){ async function updateFromForm(){
let data = new FormData(FORM) let data = new FormData(FORM)
let image = data.get("image") let image = data.get("image")
if(image){ if(image.size > 0){
await UI_GLITCHER.setImage(image) await UI_GLITCHER.setImage(image)
UI_GLITCHER.clearGlitch()
} else { } else {
UI_GLITCHER.clearImage() UI_GLITCHER.clearImage()
} }
@@ -25,4 +27,60 @@ FORM.addEventListener("submit", e => {
updateFromForm() updateFromForm()
}) })
CANVAS.addEventListener("pointerdown", e => {
let pointerId = e.pointerId;
let lastX = undefined;
let lastY = undefined;
let lastCommit = null;
function applyGlitch(e){
if(e.pointerId != pointerId){
return
}
let canvasRect = CANVAS.getBoundingClientRect()
let x = e.clientX - canvasRect.left
let y = e.clientY - canvasRect.top
let now = Date.now()
if(lastCommit === null || now - lastCommit > 100){
let width = 20;
if(e.pointerType == "touch"){
width *= 2
}
let deltaX = lastX !== undefined ? x - lastX : 0
let deltaY = lastY !== undefined ? y - lastY : 0
width += Math.max(Math.abs(deltaX), Math.abs(deltaY))
//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()
lastCommit = now
}
lastX = x
lastY = y
}
function endGlitch(e){
if(e.pointerId != pointerId){
return
}
window.removeEventListener("pointermove", applyGlitch)
window.removeEventListener("pointerup", endGlitch)
}
window.addEventListener("pointerup", endGlitch)
window.addEventListener("pointermove", applyGlitch)
applyGlitch(e)
})
updateFromForm() updateFromForm()
+2 -2
View File
@@ -1,9 +1,9 @@
attribute vec4 aVertexPosition; attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord; attribute vec2 aTextureCoord;
varying highp vec2 vTextureCoord; varying highp vec2 vTextureCoord;
void main() { void main() {
gl_Position = aVertexPosition; gl_Position = vec4(aVertexPosition, 0.0, 1.0);
vTextureCoord = aTextureCoord; vTextureCoord = aTextureCoord;
} }
@@ -0,0 +1,3 @@
void main() {
gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
}
@@ -0,0 +1,17 @@
attribute vec4 aVertexPosition;
uniform vec2 uGlitchPosition;
uniform float uGlitchWidth;
uniform vec2 uImageRatio;
void main() {
vec2 vertex = aVertexPosition.xy;
vertex *= uImageRatio;
vertex *= 0.001;
vertex *= uGlitchWidth;
vertex += uGlitchPosition;
gl_Position = vec4(vertex, -1.0, 1.0);
}
+149 -65
View File
@@ -1,12 +1,18 @@
import resizeToFit from 'intrinsic-scale'; import resizeToFit from 'intrinsic-scale'
import baseVertSource from "./base.vert?raw" import baseVertSource from "./base.vert?raw"
import baseFragSource from "./base.frag?raw" import baseFragSource from "./base.frag?raw"
import glitchVertSource from "./glitch.vert?raw"
import glitchFragSource from "./glitch.frag?raw"
export class Glitcher { export class Glitcher {
/** @type {HTMLCanvasElement} */ /** @type {HTMLCanvasElement} */
canvas canvas
gliches = []
/** /**
* @param {HTMLCanvasElement} canvas * @param {HTMLCanvasElement} canvas
*/ */
@@ -21,35 +27,7 @@ export class Glitcher {
throw new Error("WebGL isn't supported") throw new Error("WebGL isn't supported")
} }
let baseVertShader = this.ctx.createShader(this.ctx.VERTEX_SHADER) let baseProgram = createProgram(this.ctx, baseVertSource, baseFragSource);
this.ctx.shaderSource(baseVertShader, baseVertSource)
this.ctx.compileShader(baseVertShader)
if (!this.ctx.getShaderParameter(baseVertShader, this.ctx.COMPILE_STATUS)) {
throw new Error(`failed to compile base.vert: ${this.ctx.getShaderInfoLog(baseVertShader)}`)
}
let baseFragShader = this.ctx.createShader(this.ctx.FRAGMENT_SHADER)
this.ctx.shaderSource(baseFragShader, baseFragSource)
this.ctx.compileShader(baseFragShader)
if (!this.ctx.getShaderParameter(baseFragShader, this.ctx.COMPILE_STATUS)) {
throw new Error(`failed to compile base.frag: ${this.ctx.getShaderInfoLog(baseFragShader)}`)
}
let baseShaderProgram = this.ctx.createProgram()
this.ctx.attachShader(baseShaderProgram, baseVertShader)
this.ctx.attachShader(baseShaderProgram, baseFragShader)
this.ctx.linkProgram(baseShaderProgram)
if (!this.ctx.getProgramParameter(baseShaderProgram, this.ctx.LINK_STATUS)) {
throw new Error(`failed to link base shaders: ${this.ctx.getProgramInfoLog(baseShaderProgram)}`)
}
this.baseProgram = baseShaderProgram;
this.aVertexPosition = this.ctx.getAttribLocation(baseShaderProgram, "aVertexPosition")
this.aTextureCoord = this.ctx.getAttribLocation(baseShaderProgram, "aTextureCoord")
this.uImageSampler = this.ctx.getUniformLocation(baseShaderProgram, "uImageSampler")
let panelPositionBuffer = this.ctx.createBuffer() let panelPositionBuffer = this.ctx.createBuffer()
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, panelPositionBuffer) this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, panelPositionBuffer)
@@ -60,7 +38,6 @@ export class Glitcher {
1.0, -1.0, 1.0, -1.0,
] ]
this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(positions), this.ctx.STATIC_DRAW) this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(positions), this.ctx.STATIC_DRAW)
this.panelPositionsBuffer = panelPositionBuffer
let panelUVBuffer = this.ctx.createBuffer() let panelUVBuffer = this.ctx.createBuffer()
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, panelUVBuffer) this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, panelUVBuffer)
@@ -71,11 +48,41 @@ export class Glitcher {
1.0, 1.0, 1.0, 1.0,
] ]
this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(uv), this.ctx.STATIC_DRAW) this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(uv), this.ctx.STATIC_DRAW)
this.panelUVBuffer = panelUVBuffer
let imageTexture = this.ctx.createTexture() let imageTexture = this.ctx.createTexture()
this.imageTexture = imageTexture;
this.base = {
program: baseProgram,
aVertexPosition: this.ctx.getAttribLocation(baseProgram, "aVertexPosition"),
aTextureCoord: this.ctx.getAttribLocation(baseProgram, "aTextureCoord"),
uImageSampler: this.ctx.getUniformLocation(baseProgram, "uImageSampler"),
imageTexture: imageTexture,
panelUVBuffer: panelUVBuffer,
panelPositionsBuffer: panelPositionBuffer
}
this.clearImage(); this.clearImage();
let glitchPositionBuffer = this.ctx.createBuffer()
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, glitchPositionBuffer)
const glitchPosition = [
-1.0, 1.0,
1.0, 1.0,
-1.0, -1.0,
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)
this.glitch = {
program: glitchProgram,
aVertexPosition: this.ctx.getAttribLocation(glitchProgram, "aVertexPosition"),
uGlitchPosition: this.ctx.getUniformLocation(glitchProgram, "uGlitchPosition"),
uGlitchWidth: this.ctx.getUniformLocation(glitchProgram, "uGlitchWidth"),
uImageRatio: this.ctx.getUniformLocation(glitchProgram, "uImageRatio"),
glitchPositionBuffer: glitchPositionBuffer
}
} }
clearImage(){ clearImage(){
@@ -92,6 +99,22 @@ export class Glitcher {
) )
} }
clearGlitch(){
this.glitches = []
}
addGlitch(x, y, width=1){
let computedStyle = window.getComputedStyle(this.canvas)
let clientWidth = parseFloat(computedStyle.width)
let clientHeight = parseFloat(computedStyle.height)
this.glitches.push({
x: ((x*2)/clientWidth)-1,
y: (((y*2)/clientHeight)-1)*-1,
width
})
}
/** /**
* @param {File} imageFile * @param {File} imageFile
*/ */
@@ -132,7 +155,7 @@ export class Glitcher {
this.currentImageFile = imageFile this.currentImageFile = imageFile
this.currentImage = image this.currentImage = image
this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.imageTexture) this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.base.imageTexture)
this.ctx.texImage2D( this.ctx.texImage2D(
this.ctx.TEXTURE_2D, this.ctx.TEXTURE_2D,
0, 0,
@@ -184,38 +207,99 @@ export class Glitcher {
this.ctx.clear(this.ctx.COLOR_BUFFER_BIT | this.ctx.DEPTH_BUFFER_BIT) this.ctx.clear(this.ctx.COLOR_BUFFER_BIT | this.ctx.DEPTH_BUFFER_BIT)
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.panelPositionsBuffer) { // Base rendering
this.ctx.vertexAttribPointer( this.ctx.useProgram(this.base.program)
this.aVertexPosition, this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.base.panelPositionsBuffer)
2, // N components per iteration this.ctx.vertexAttribPointer(
this.ctx.FLOAT, this.base.aVertexPosition,
false, //Normalize, 2, // N components per iteration
0, // (stride) how many bytes to get from one set of values to the next this.ctx.FLOAT,
0, // Start offset false, //Normalize,
) 0, // (stride) how many bytes to get from one set of values to the next
this.ctx.enableVertexAttribArray(this.aVertexPosition) 0, // Start offset
)
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.panelUVBuffer) this.ctx.enableVertexAttribArray(this.base.aVertexPosition)
this.ctx.vertexAttribPointer(
this.aTextureCoord, this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.base.panelUVBuffer)
2, this.ctx.vertexAttribPointer(
this.ctx.FLOAT, this.base.aTextureCoord,
false, 2,
0, this.ctx.FLOAT,
0, false,
) 0,
this.ctx.enableVertexAttribArray(this.aTextureCoord) 0,
)
this.ctx.enableVertexAttribArray(this.base.aTextureCoord)
this.ctx.activeTexture(this.ctx.TEXTURE0) this.ctx.activeTexture(this.ctx.TEXTURE0)
this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.imageTexture) this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.base.imageTexture)
this.ctx.uniform1i(this.uImageSampler, 0)
this.ctx.uniform1i(this.base.uImageSampler, 0)
this.ctx.drawArrays(
this.ctx.TRIANGLE_STRIP,
0, // Vertex offset
4, // Total vertex
);
}
this.ctx.useProgram(this.baseProgram) { // Glitch rendering
this.ctx.useProgram(this.glitch.program)
this.ctx.drawArrays( this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.glitch.glitchPositionBuffer)
this.ctx.TRIANGLE_STRIP, this.ctx.vertexAttribPointer(
0, // Vertex offset this.glitch.glitchPositionBuffer,
4, // Total vertex 2, // N components per iteration
); this.ctx.FLOAT,
false, //Normalize,
0, // (stride) how many bytes to get from one set of values to the next
0, // Start offset
)
this.ctx.enableVertexAttribArray(this.glitch.aVertexPosition)
if(this.canvas.width > this.canvas.height){
this.ctx.uniform2f(this.glitch.uImageRatio, 1.0, this.canvas.width/this.canvas.height)
} else {
this.ctx.uniform2f(this.glitch.uImageRatio, this.canvas.height/this.canvas.width, 1.0)
}
for(let glitch of this.glitches) {
this.ctx.uniform2f(this.glitch.uGlitchPosition, glitch.x, glitch.y)
this.ctx.uniform1f(this.glitch.uGlitchWidth, glitch.width)
this.ctx.drawArrays(
this.ctx.TRIANGLE_STRIP,
0, // Vertex offset
4, // Total vertex
);
}
}
} }
}
function createProgram(gl, vertSource, fragSource){
let vertShader = gl.createShader(gl.VERTEX_SHADER)
gl.shaderSource(vertShader, vertSource)
gl.compileShader(vertShader)
if (!gl.getShaderParameter(vertShader, gl.COMPILE_STATUS)) {
throw new Error(`failed to compile vertex: ${gl.getShaderInfoLog(vertShader)}`)
}
let fragShader = gl.createShader(gl.FRAGMENT_SHADER)
gl.shaderSource(fragShader, fragSource)
gl.compileShader(fragShader)
if (!gl.getShaderParameter(fragShader, gl.COMPILE_STATUS)) {
throw new Error(`failed to compile fragment: ${gl.getShaderInfoLog(fragShader)}`)
}
let program = gl.createProgram()
gl.attachShader(program, vertShader)
gl.attachShader(program, fragShader)
gl.linkProgram(program)
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
throw new Error(`failed to link shader: ${gl.getProgramInfoLog(program)}`)
}
return program
} }