Skip to content

Commit

Permalink
suggestions-fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
perminder-17 committed Dec 10, 2024
1 parent 9aee0d0 commit e714125
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 55 deletions.
4 changes: 4 additions & 0 deletions src/core/p5.Renderer2D.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Graphics } from './p5.Graphics';
import { Image } from '../image/p5.Image';
import { Element } from '../dom/p5.Element';
import { MediaElement } from '../dom/p5.MediaElement';
import FilterRenderer2D from '../image/filterRenderer2D';

const styleEmpty = 'rgba(0,0,0,0)';
// const alphaThreshold = 0.00125; // minimum visible
Expand Down Expand Up @@ -66,6 +67,9 @@ class Renderer2D extends Renderer {
}
this.scale(this._pixelDensity, this._pixelDensity);

if(!this.filterRenderer){
this.filterRenderer = new FilterRenderer2D(this);
}
// Set and return p5.Element
this.wrappedElt = new Element(this.elt, this._pInst);
}
Expand Down
80 changes: 50 additions & 30 deletions src/image/filterRenderer2D.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,9 @@ class FilterRenderer2D {
/**
* Creates a new FilterRenderer2D instance.
* @param {p5} pInst - The p5.js instance.
* @param {string} operation - The filter operation type (e.g., constants.BLUR).
* @param {string} filterParameter - The strength of applying filter.
* @param {p5.Shader} customShader - Optional custom shader; if provided, ignore operation-based loading.
*/
constructor(pInst, operation, filterParameter, customShader) {
constructor(pInst) {
this.pInst = pInst;
this.filterParameter = filterParameter;
this.operation = operation;
this.customShader = customShader;


// Create a canvas for applying WebGL-based filters
this.canvas = document.createElement('canvas');
this.canvas.width = pInst.width;
Expand All @@ -39,7 +31,6 @@ class FilterRenderer2D {
console.error("WebGL not supported, cannot apply filter.");
return;
}

// Minimal renderer object required by p5.Shader and p5.Texture
this._renderer = {
GL: this.gl,
Expand All @@ -62,8 +53,8 @@ class FilterRenderer2D {
},
};

// Fragment shaders mapped to filter operations
this.filterShaders = {
// Store the fragment shader sources
this.filterShaderSources = {
[constants.BLUR]: filterBlurFrag,
[constants.INVERT]: filterInvertFrag,
[constants.THRESHOLD]: filterThresholdFrag,
Expand All @@ -74,8 +65,14 @@ class FilterRenderer2D {
[constants.OPAQUE]: filterOpaqueFrag,
};

// Store initialized shaders for each operation
this.filterShaders = {};

// These will be set by setOperation
this.operation = null;
this.filterParameter = 1;
this.customShader = null;
this._shader = null;
this._initializeShader();

// Create buffers once
this.vertexBuffer = this.gl.createBuffer();
Expand All @@ -90,32 +87,54 @@ class FilterRenderer2D {

// Upload texcoord data once
this._bindBufferData(this.texcoordBuffer, this.gl.ARRAY_BUFFER, this.texcoords);

}

updateFilterParameter(newFilterParameter) {
// Operation is the same, just update parameter if changed
this.filterParameter = newFilterParameter;
/**
* Set the current filter operation and parameter. If a customShader is provided,
* that overrides the operation-based shader.
* @param {string} operation - The filter operation type (e.g., constants.BLUR).
* @param {number} filterParameter - The strength of the filter.
* @param {p5.Shader} customShader - Optional custom shader.
*/
setOperation(operation, filterParameter, customShader = null) {
this.operation = operation;
this.filterParameter = filterParameter;
this.customShader = customShader;
this._initializeShader();
}

/**
* Initializes the shader program if it hasn't been already.
* Initializes or retrieves the shader program for the current operation.
* If a customShader is provided, that is used.
* Otherwise, returns a cached shader if available, or creates a new one, caches it, and sets it as current.
*/
_initializeShader() {
if (this._shader) return; // Already initialized

if (this.customShader) {
this._shader = this.customShader;
return;
}

const fragShaderSrc = this.filterShaders[this.operation];
if (!this.operation) {
console.error("No operation set for FilterRenderer2D, cannot initialize shader.");
return;
}

// If we already have a compiled shader for this operation, reuse it
if (this.filterShaders[this.operation]) {
this._shader = this.filterShaders[this.operation];
return;
}

const fragShaderSrc = this.filterShaderSources[this.operation];
if (!fragShaderSrc) {
console.error("No shader available for this operation:", this.operation);
return;
}

this._shader = new Shader(this._renderer, filterShaderVert, fragShaderSrc);
// Create and store the new shader
const newShader = new Shader(this._renderer, filterShaderVert, fragShaderSrc);
this.filterShaders[this.operation] = newShader;
this._shader = newShader;
}

/**
Expand Down Expand Up @@ -151,36 +170,37 @@ class FilterRenderer2D {
this._shader.setUniform('canvasSize', [this.pInst.width, this.pInst.height]);
this._shader.setUniform('radius', Math.max(1, this.filterParameter));

const identityMatrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
const identityMatrix = [1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1];
this._shader.setUniform('uModelViewMatrix', identityMatrix);
this._shader.setUniform('uProjectionMatrix', identityMatrix);

// Bind and enable vertex attributes
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
this._shader.enableAttrib(this._shader.attributes.aPosition, 2);

gl.bindBuffer(gl.ARRAY_BUFFER, this.texcoordBuffer);
this._shader.enableAttrib(this._shader.attributes.aTexCoord, 2);

// Draw the quad
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

// Unbind the shader
this._shader.unbindShader();
}

/**
* Applies the filter operation. If the filter requires multiple passes (e.g. blur),
* it handles those internally.
* Applies the current filter operation. If the filter requires multiple passes (e.g. blur),
* it handles those internally. Make sure setOperation() has been called before applyFilter().
*/
applyFilter() {
if (!this._shader) {
console.error("Cannot apply filter: shader not initialized.");
return;
}

// For blur, we typically do two passes: one horizontal, one vertical.
if (this.operation === constants.BLUR && !this.customShader) {
if (this.operation === constants.BLUR && !this.customShader) {
// Horizontal pass
this._shader.setUniform('direction', [1, 0]);
this._renderPass();
Expand Down
30 changes: 12 additions & 18 deletions src/image/pixels.js
Original file line number Diff line number Diff line change
Expand Up @@ -750,29 +750,23 @@ function pixels(p5, fn){
if (this._renderer.isP3D) {
this._renderer.filter(operation, value);
}

// when this is P2D renderer, create/use hidden webgl renderer
else {
if (!this.filterRenderer) {
this.filterRenderer = new FilterRenderer2D(this);
this._renderer.filterRenderer = this.filterRenderer;
}
if (shader) {
const customFilterRenderer = new FilterRenderer2D(this, operation, value, shader);
customFilterRenderer.applyFilter();
this.filterRenderer.setOperation(operation, value, shader);
} else {
if (!this._filterRenderers) {
this._filterRenderers = {};
}

// Check if we have a cached renderer for this operation
if (!this._filterRenderers[operation]) {
// If no cached renderer for this filter, create and cache it
this._filterRenderers[operation] = new FilterRenderer2D(this, operation, value);
} else {
// If we already have a renderer for this operation and value is different, update it.
this._filterRenderers[operation].updateFilterParameter(value);
}
// Use the currently requested operation's renderer
this.filterRenderer = this._filterRenderers[operation];
this.filterRenderer.applyFilter();
// No custom shader, just a built-in filter
this.filterRenderer.setOperation(operation, value);
}

// Apply the current filter
this.filterRenderer.applyFilter();

}
};

Expand Down
6 changes: 1 addition & 5 deletions src/webgl/material.js
Original file line number Diff line number Diff line change
Expand Up @@ -647,11 +647,7 @@ function material(p5, fn){
`;
let vertSrc = fragSrc.includes('#version 300 es') ? defaultVertV2 : defaultVertV1;
const shader = new Shader(this._renderer, vertSrc, fragSrc);
if (this._renderer.GL) {
shader.ensureCompiledOnContext(this._renderer);
} else {
shader.ensureCompiledOnContext(this._renderer.getFilterGraphicsLayer()._renderer);
}
shader.ensureCompiledOnContext(this);
return shader;
};

Expand Down
6 changes: 4 additions & 2 deletions src/webgl/p5.Shader.js
Original file line number Diff line number Diff line change
Expand Up @@ -625,11 +625,13 @@ class Shader {
'The shader being run is attached to a different context. Do you need to copy it to this context first with .copyToContext()?'
);
} else if (this._glProgram === 0) {
this._renderer = context;
this._renderer = context._renderer.filterRenderer

Check failure on line 628 in src/webgl/p5.Shader.js

View workflow job for this annotation

GitHub Actions / test

test/unit/webgl/p5.RendererGL.js > p5.RendererGL > texture binding > setting a custom texture works

TypeError: Cannot read properties of undefined (reading 'filterRenderer') ❯ Shader.ensureCompiledOnContext src/webgl/p5.Shader.js:628:42 ❯ RendererGL.shader src/webgl/material.js:3598:7 ❯ fn.shader src/webgl/material.js:838:20 ❯ test/unit/webgl/p5.RendererGL.js:92:12

Check failure on line 628 in src/webgl/p5.Shader.js

View workflow job for this annotation

GitHub Actions / test

test/unit/webgl/p5.RendererGL.js > p5.RendererGL > texture binding > textures remain bound after each draw call

TypeError: Cannot read properties of undefined (reading 'filterRenderer') ❯ Shader.ensureCompiledOnContext src/webgl/p5.Shader.js:628:42 ❯ RendererGL.shader src/webgl/material.js:3598:7 ❯ fn.shader src/webgl/material.js:838:20 ❯ test/unit/webgl/p5.RendererGL.js:121:12

Check failure on line 628 in src/webgl/p5.Shader.js

View workflow job for this annotation

GitHub Actions / test

test/unit/webgl/p5.RendererGL.js > p5.RendererGL > filter shader > filter accepts correct params

TypeError: Cannot read properties of undefined (reading 'filterRenderer') ❯ Shader.ensureCompiledOnContext src/webgl/p5.Shader.js:628:42 ❯ RendererGL.shader src/webgl/material.js:3598:7 ❯ src/webgl/p5.RendererGL.js:1139:14 ❯ Framebuffer.draw src/webgl/p5.Framebuffer.js:1271:5 ❯ RendererGL.filter src/webgl/p5.RendererGL.js:1136:11 ❯ fn.filter src/image/pixels.js:751:22 ❯ test/unit/webgl/p5.RendererGL.js:274:12

Check failure on line 628 in src/webgl/p5.Shader.js

View workflow job for this annotation

GitHub Actions / test

test/unit/webgl/p5.RendererGL.js > p5.RendererGL > filter shader > BLUR parameters make different output

TypeError: Cannot read properties of undefined (reading 'filterRenderer') ❯ Shader.ensureCompiledOnContext src/webgl/p5.Shader.js:628:42 ❯ RendererGL.shader src/webgl/material.js:3598:7 ❯ RendererGL.filter src/webgl/p5.RendererGL.js:1107:12 ❯ fn.filter src/image/pixels.js:751:22 ❯ test/unit/webgl/p5.RendererGL.js:501:12

Check failure on line 628 in src/webgl/p5.Shader.js

View workflow job for this annotation

GitHub Actions / test

test/unit/webgl/p5.RendererGL.js > p5.RendererGL > filter shader > POSTERIZE parameters make different output

TypeError: Cannot read properties of undefined (reading 'filterRenderer') ❯ Shader.ensureCompiledOnContext src/webgl/p5.Shader.js:628:42 ❯ RendererGL.shader src/webgl/material.js:3598:7 ❯ src/webgl/p5.RendererGL.js:1139:14 ❯ Framebuffer.draw src/webgl/p5.Framebuffer.js:1271:5 ❯ RendererGL.filter src/webgl/p5.RendererGL.js:1136:11 ❯ fn.filter src/image/pixels.js:751:22 ❯ test/unit/webgl/p5.RendererGL.js:527:12

Check failure on line 628 in src/webgl/p5.Shader.js

View workflow job for this annotation

GitHub Actions / test

test/unit/webgl/p5.RendererGL.js > p5.RendererGL > filter shader > THRESHOLD parameters make different output

TypeError: Cannot read properties of undefined (reading 'filterRenderer') ❯ Shader.ensureCompiledOnContext src/webgl/p5.Shader.js:628:42 ❯ RendererGL.shader src/webgl/material.js:3598:7 ❯ src/webgl/p5.RendererGL.js:1139:14 ❯ Framebuffer.draw src/webgl/p5.Framebuffer.js:1271:5 ❯ RendererGL.filter src/webgl/p5.RendererGL.js:1136:11 ❯ fn.filter src/image/pixels.js:751:22 ❯ test/unit/webgl/p5.RendererGL.js:547:12

Check failure on line 628 in src/webgl/p5.Shader.js

View workflow job for this annotation

GitHub Actions / test

test/unit/webgl/p5.RendererGL.js > p5.RendererGL > filter shader > external context > blur filter > webgl mode > corner rectMode

TypeError: Cannot read properties of undefined (reading 'filterRenderer') ❯ Shader.ensureCompiledOnContext src/webgl/p5.Shader.js:628:42 ❯ RendererGL.shader src/webgl/material.js:3598:7 ❯ RendererGL.filter src/webgl/p5.RendererGL.js:1107:12 ❯ fn.filter src/image/pixels.js:751:22 ❯ getFilteredPixels test/unit/webgl/p5.RendererGL.js:580:14 ❯ test/unit/webgl/p5.RendererGL.js:602:21
? context._renderer.filterRenderer._renderer
: context._renderer;
this.init();
}
}

/**
* Queries the active attributes for this shader and loads
* their names and locations into the attributes array.
Expand Down

0 comments on commit e714125

Please sign in to comment.