|
|
|
@@ -1,12 +1,18 @@
|
|
|
|
|
import resizeToFit from 'intrinsic-scale';
|
|
|
|
|
import resizeToFit from 'intrinsic-scale'
|
|
|
|
|
|
|
|
|
|
import baseVertSource from "./base.vert?raw"
|
|
|
|
|
import baseFragSource from "./base.frag?raw"
|
|
|
|
|
|
|
|
|
|
import glitchVertSource from "./glitch.vert?raw"
|
|
|
|
|
import glitchFragSource from "./glitch.frag?raw"
|
|
|
|
|
|
|
|
|
|
export class Glitcher {
|
|
|
|
|
|
|
|
|
|
/** @type {HTMLCanvasElement} */
|
|
|
|
|
canvas
|
|
|
|
|
|
|
|
|
|
gliches = []
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param {HTMLCanvasElement} canvas
|
|
|
|
|
*/
|
|
|
|
@@ -21,35 +27,7 @@ export class Glitcher {
|
|
|
|
|
throw new Error("WebGL isn't supported")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let baseVertShader = this.ctx.createShader(this.ctx.VERTEX_SHADER)
|
|
|
|
|
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 baseProgram = createProgram(this.ctx, baseVertSource, baseFragSource);
|
|
|
|
|
|
|
|
|
|
let panelPositionBuffer = this.ctx.createBuffer()
|
|
|
|
|
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, panelPositionBuffer)
|
|
|
|
@@ -60,7 +38,6 @@ export class Glitcher {
|
|
|
|
|
1.0, -1.0,
|
|
|
|
|
]
|
|
|
|
|
this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(positions), this.ctx.STATIC_DRAW)
|
|
|
|
|
this.panelPositionsBuffer = panelPositionBuffer
|
|
|
|
|
|
|
|
|
|
let panelUVBuffer = this.ctx.createBuffer()
|
|
|
|
|
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, panelUVBuffer)
|
|
|
|
@@ -71,11 +48,41 @@ export class Glitcher {
|
|
|
|
|
1.0, 1.0,
|
|
|
|
|
]
|
|
|
|
|
this.ctx.bufferData(this.ctx.ARRAY_BUFFER, new Float32Array(uv), this.ctx.STATIC_DRAW)
|
|
|
|
|
this.panelUVBuffer = panelUVBuffer
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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(){
|
|
|
|
@@ -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
|
|
|
|
|
*/
|
|
|
|
@@ -132,7 +155,7 @@ export class Glitcher {
|
|
|
|
|
this.currentImageFile = imageFile
|
|
|
|
|
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.TEXTURE_2D,
|
|
|
|
|
0,
|
|
|
|
@@ -184,38 +207,99 @@ export class Glitcher {
|
|
|
|
|
|
|
|
|
|
this.ctx.clear(this.ctx.COLOR_BUFFER_BIT | this.ctx.DEPTH_BUFFER_BIT)
|
|
|
|
|
|
|
|
|
|
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.panelPositionsBuffer)
|
|
|
|
|
this.ctx.vertexAttribPointer(
|
|
|
|
|
this.aVertexPosition,
|
|
|
|
|
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.aVertexPosition)
|
|
|
|
|
|
|
|
|
|
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.panelUVBuffer)
|
|
|
|
|
this.ctx.vertexAttribPointer(
|
|
|
|
|
this.aTextureCoord,
|
|
|
|
|
2,
|
|
|
|
|
this.ctx.FLOAT,
|
|
|
|
|
false,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
)
|
|
|
|
|
this.ctx.enableVertexAttribArray(this.aTextureCoord)
|
|
|
|
|
{ // Base rendering
|
|
|
|
|
this.ctx.useProgram(this.base.program)
|
|
|
|
|
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.base.panelPositionsBuffer)
|
|
|
|
|
this.ctx.vertexAttribPointer(
|
|
|
|
|
this.base.aVertexPosition,
|
|
|
|
|
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.base.aVertexPosition)
|
|
|
|
|
|
|
|
|
|
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.base.panelUVBuffer)
|
|
|
|
|
this.ctx.vertexAttribPointer(
|
|
|
|
|
this.base.aTextureCoord,
|
|
|
|
|
2,
|
|
|
|
|
this.ctx.FLOAT,
|
|
|
|
|
false,
|
|
|
|
|
0,
|
|
|
|
|
0,
|
|
|
|
|
)
|
|
|
|
|
this.ctx.enableVertexAttribArray(this.base.aTextureCoord)
|
|
|
|
|
|
|
|
|
|
this.ctx.activeTexture(this.ctx.TEXTURE0)
|
|
|
|
|
this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.imageTexture)
|
|
|
|
|
this.ctx.uniform1i(this.uImageSampler, 0)
|
|
|
|
|
this.ctx.activeTexture(this.ctx.TEXTURE0)
|
|
|
|
|
this.ctx.bindTexture(this.ctx.TEXTURE_2D, this.base.imageTexture)
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
this.ctx.drawArrays(
|
|
|
|
|
this.ctx.TRIANGLE_STRIP,
|
|
|
|
|
0, // Vertex offset
|
|
|
|
|
4, // Total vertex
|
|
|
|
|
);
|
|
|
|
|
{ // Glitch rendering
|
|
|
|
|
this.ctx.useProgram(this.glitch.program)
|
|
|
|
|
this.ctx.bindBuffer(this.ctx.ARRAY_BUFFER, this.glitch.glitchPositionBuffer)
|
|
|
|
|
this.ctx.vertexAttribPointer(
|
|
|
|
|
this.glitch.glitchPositionBuffer,
|
|
|
|
|
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
|
|
|
|
|
}
|