From d71efff57cc56b3f27c9f08d015c61b71c9f5775 Mon Sep 17 00:00:00 2001 From: Perminder Date: Fri, 6 Dec 2024 03:39:04 +0530 Subject: [PATCH] 2dFilterRenderer still a work in progress PR, needs to do some todo tasks --- src/image/filter-2d-renderer.js | 149 ++++++++++++++++++++++++++++++++ src/image/pixels.js | 68 +++++++++++---- 2 files changed, 198 insertions(+), 19 deletions(-) create mode 100644 src/image/filter-2d-renderer.js diff --git a/src/image/filter-2d-renderer.js b/src/image/filter-2d-renderer.js new file mode 100644 index 0000000000..8a3ef0f2b2 --- /dev/null +++ b/src/image/filter-2d-renderer.js @@ -0,0 +1,149 @@ +import { Shader } from "../webgl/p5.Shader"; +import { Texture } from "../webgl/p5.Texture"; +import { Image } from "./p5.Image"; +import * as constants from '../core/constants'; +import filterGrayFrag from '../webgl/shaders/filters/gray.frag'; +import filterErodeFrag from '../webgl/shaders/filters/erode.frag'; +import filterDilateFrag from '../webgl/shaders/filters/dilate.frag'; +import filterBlurFrag from '../webgl/shaders/filters/blur.frag'; +import filterPosterizeFrag from '../webgl/shaders/filters/posterize.frag'; +import filterOpaqueFrag from '../webgl/shaders/filters/opaque.frag'; +import filterInvertFrag from '../webgl/shaders/filters/invert.frag'; +import filterThresholdFrag from '../webgl/shaders/filters/threshold.frag'; + +class FilterRenderer2D { + constructor(pInst, operation) { + this.pInst = pInst; + this.operation = operation; + // creating webgl context + this.canvas = document.createElement('canvas'); + this.canvas.width = pInst.width; + this.canvas.height = pInst.height; + this.gl = this.canvas.getContext('webgl'); + + // if not able to create, return + if (!this.gl) { + console.error("WebGL not supported"); + return; + } + + // Set up the minimal renderer required by p5.Shader and p5.Texture + this._renderer = { + GL: this.gl, + registerEnabled : new Set(), + _curShader: null, + _emptyTexture: null, + webglVersion: 'WEBGL', + states: { + textureWrapX: this.gl.CLAMP_TO_EDGE, + textureWrapY: this.gl.CLAMP_TO_EDGE, + }, + _arraysEqual: function(a, b) { + return JSON.stringify(a) === JSON.stringify(b); + }, + _getEmptyTexture: () => { + if (!this._emptyTexture) { + const im = new Image(1, 1); + im.set(0, 0, 255); + this._emptyTexture = new Texture(this._renderer, im); + } + return this._emptyTexture; + }, + }; + } + + filterShaders = { + [constants.BLUR] : filterBlurFrag, + [constants.INVERT]: filterInvertFrag, + [constants.THRESHOLD]: filterThresholdFrag, + [constants.ERODE]: filterErodeFrag, + [constants.GRAY] : filterGrayFrag, + [constants.DILATE]: filterDilateFrag, + [constants.POSTERIZE]: filterPosterizeFrag, + [constants.OPAQUE]: filterOpaqueFrag, + }; + + + vertSrc() { + return ` + attribute vec2 aPosition; + attribute vec2 aTexCoord; + varying vec2 vTexCoord; + void main() { + gl_Position = vec4(aPosition, 0.0, 1.0); + vTexCoord = aTexCoord; + } + `; + } + + // binding buffer + _bindBuffer(buffer, target, values, type, usage) { + const gl = this.gl; + if (!target) target = gl.ARRAY_BUFFER; + gl.bindBuffer(target, buffer); + if (values !== undefined) { + let data = values; + if (!(data instanceof (type || Float32Array))) { + data = new (type || Float32Array)(data); + } + gl.bufferData(target, data, usage || gl.STATIC_DRAW); + } + } + + render() { + // console.log(this.pInst._renderer.pixelDensity()); + const gl = this.gl; + // console.log(this.pInst.width); + let texelSize = [ + 1 / (this.pInst.width * this.pInst._renderer.pixelDensity()), + 1 / (this.pInst.height * this.pInst._renderer.pixelDensity()) + ]; + + // console.log(texelSize); + // Create and initialize the shader + if (!this._shader) { + // console.log(this.filterShaders['invert']); + this._shader = new Shader(this._renderer, this.vertSrc(), this.filterShaders[this.operation]); + } + this._shader.bindShader(); + + // Create a texture from the main p5.js canvas + // console.log(this.pInst._renderer.wrappedElt) + const canvasTexture = new Texture(this._renderer, this.pInst._renderer.wrappedElt); + // canvasTexture.update(); // Ensure the texture is updated with the latest canvas content + const vertices = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]); + const texcoords = new Float32Array([0, 1, 1, 1, 0, 0, 1, 0]); + + const vertexBuffer = gl.createBuffer(); + this._bindBuffer(vertexBuffer, gl.ARRAY_BUFFER, vertices); + + this._shader.enableAttrib(this._shader.attributes.aPosition, 2); + // Create and bind the vertex buffer for positions + const texcoordBuffer = gl.createBuffer(); + + this._bindBuffer(texcoordBuffer, gl.ARRAY_BUFFER, texcoords); + // Create and bind the texture coordinate buffer + this._shader.enableAttrib(this._shader.attributes.aTexCoord, 2); + + // Set the texture uniform + this._shader.setUniform('tex0', canvasTexture); + this._shader.setUniform('texelSize', texelSize); + this._shader.setUniform('canvasSize', [this.pInst.width, this.pInst.height]); + this._shader.setUniform('direction', [1, 0]); + this._shader.setUniform('radius', 5); + + // Clear the canvas and draw the quad + gl.viewport(0, 0, this.canvas.width, this.canvas.height); + gl.clearColor(0.0, 0.0, 0.0, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT); + + // Draw the quad (two triangles) + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + + // Unbind the shader and texture + this._shader.unbindShader(); + canvasTexture.unbindTexture(); + } +} + +export default FilterRenderer2D; diff --git a/src/image/pixels.js b/src/image/pixels.js index 78df6bd877..c06c5368df 100644 --- a/src/image/pixels.js +++ b/src/image/pixels.js @@ -6,6 +6,7 @@ */ import Filters from './filters'; +import FilterRenderer2D from './filter-2d-renderer'; function pixels(p5, fn){ /** @@ -722,7 +723,7 @@ function pixels(p5, fn){ */ fn.filter = function(...args) { p5._validateParameters('filter', args); - + let { shader, operation, value, useWebGL } = parseFilterArgs(...args); // when passed a shader, use it directly @@ -749,37 +750,60 @@ function pixels(p5, fn){ if (this._renderer.isP3D) { this._renderer.filter(operation, value); } - + // when this is P2D renderer, create/use hidden webgl renderer else { + + // TODO-1: 2 PASS BLUR (currently only works in horizontal blurring) + // TODO-2: ADDING CONDITIONS FOR shifting rendererGL filter for webgl and filterREnderer2D for 2d build filters + // TODO-3: PASSING p5.Shader, p5.Texture and fragment shader to expose an addon function that sets p5.Renderer2D.prototype.filter + // TODO-4: ADDING projection, modelView and necessary matrix + // TODO-5: code cleanups, adjusting the peices where they belong to. + + + + + // console.log(this._renderer.wrappedElt); + // this._renderer.clear(); + if (!this.filterRenderer) { + // console.log(this._renderer._pInst.canvas); + this.filterRenderer = new FilterRenderer2D(this, operation); + } + const filterGraphicsLayer = this.getFilterGraphicsLayer(); // copy p2d canvas contents to secondary webgl renderer // dest filterGraphicsLayer.copy( - // src this._renderer, - // src coods 0, 0, this.width, this.height, - // dest coords - -this.width/2, -this.height/2, this.width, this.height + 0, 0, this.width, this.height ); //clearing the main canvas - this._renderer.clear(); + this.filterRenderer.render(); + this.drawingContext.drawImage(this.filterRenderer.canvas, 0, 0, this.width, this.height); + // this._renderer.clear(); + // this.filterRenderer.renderAgain(); + // this.drawingContext.drawImage(this.filterRenderer.canvas, 0, 0, this.width, this.height); + + // this._pInst.image(filterRenderer.canvas, 0, 0, width, height); - this._renderer.resetMatrix(); - // filter it with shaders - filterGraphicsLayer.filter(...args); + // this._renderer.resetMatrix(); + // filter it with shaders + // filterGraphicsLayer.filter(...args); + // console.log(this._renderer.image); + // console.log(this.filterRenderer.canvas); + // this._renderer.image(this.filterRenderer, 0, 0, this.width, this.height); // copy secondary webgl renderer back to original p2d canvas - this.copy( - // src - filterGraphicsLayer._renderer, - // src coods - 0, 0, this.width, this.height, - // dest coords - 0, 0, this.width, this.height - ); - filterGraphicsLayer.clear(); // prevent feedback effects on p2d canvas + // this.copy( + // // src + // filterGraphicsLayer, + // // src coods + // 0, 0, this.width, this.height, + // // dest coords + // 0, 0, this.width, this.height + // ); + // filterGraphicsLayer.clear(); // prevent feedback effects on p2d canvas } }; @@ -796,6 +820,12 @@ function pixels(p5, fn){ useWebGL: true }; + // console.log("hi"); + // if(args[0] instanceof FilterRenderer2D){ + // console.log("hii"); + // } + // console.log(args); + // console.log(this); if (args[0] instanceof p5.Shader) { result.shader = args[0]; return result;