cansik / processing-postfx

A shader based postFX library for processing.
148 stars 13 forks source link

Is it possible to only affect a PGraphics Layer? #37

Closed cyrillkuhlmann closed 2 years ago

cyrillkuhlmann commented 2 years ago

I have this code here where ellipses are drawn on a PGraphics layer. The PGraphics layer is not displayed through draw, but is redrawn by a grid of ellipses - later in draw. Now I want to blur the PGraphics layer so that the "blurred PGraphics" layer is redrawn by the grid. Until now I only managed to blur the whole sketch... Is it possible to blur a PGraphics and then store it this way in the memory?

This is my code for a better understanding:

import ch.bildspur.postfx.builder.*;
import ch.bildspur.postfx.pass.*;
import ch.bildspur.postfx.*;

PostFX fx;

PGraphics pg;

float size;
float pixel;

ArrayList<Circle> circles;
void setup () {

  size(600, 600, P2D);
  pixel = 50;
  size = width/pixel;
  circles = new ArrayList<Circle>();
  circles.add(new Circle());
  frameRate(30);

  rectMode(CENTER);
  fx = new PostFX(this);
  pg = createGraphics(width, height);
}

void draw() {
  background(0);

  pg.beginDraw();
  pg.background(255);

  for (int i = circles.size()-1; i >= 1; i--) {

    Circle circle = circles.get(i);
    circle.display(pg);
  }
  pg.endDraw();

  //DRAW THE GRID
  for (int y = 0; y < pixel; y++) {
    for (int x = 0; x < pixel; x++) {

      color c = pg.get(int(size*x), int(size*y));
      float motionsize = map(brightness(c), 0, 255, size, 0);
      pushMatrix();
      translate(size/2, size/2);
      translate(x*size, y*size);
      fill(255);
      noStroke();
      ellipse(0, 0, motionsize, motionsize);
      popMatrix();
    }
  }
  // fx.render()
  //    .blur(10, 10)
  //    .compose();
}
void mousePressed() {
  circles.add(new Circle());
}

class Circle {

  int x = int(random(width));
  int y = int(random(height));

  Circle() {
  }
  void display(PGraphics pg) {
    pg.fill(0);
    pg.noStroke();
    pg.ellipse(x, y, 100, 100);
  }
}
cansik commented 2 years ago

This is documented here: https://github.com/cansik/processing-postfx#off-screen-buffer And there is an example too: https://github.com/cansik/processing-postfx/blob/master/examples/OffScreenEffect/OffScreenEffect.pde#L44

cyrillkuhlmann commented 2 years ago

Thank you! Yes i am working with this example right now … but

 fx.render(canvas)
    .brightPass(0.5)
    .blur(200, 200)
    .compose();

always displays it too – I want the PGraphics canvas really to be affected by it… so that if I later say image(canvas, 0, 0);

it shows up blurred…

This is my experiment so far – or am I missing something? The fx.render() function also always display it? Am I right?

import ch.bildspur.postfx.builder.*;
import ch.bildspur.postfx.pass.*;
import ch.bildspur.postfx.*;

PostFX fx;

PGraphics canvas;

void setup()
{
  size(500, 500, P2D);

  fx = new PostFX(this);  
  canvas = createGraphics(width, height, P2D);
}

void draw()
{

  background(0);
  // draw a simple rotating cube around a sphere
  canvas.beginDraw();
  canvas.background(0);

  canvas.noStroke();
  canvas.fill(255);
  canvas.ellipse(width/2, height/2, 200, 200);
  canvas.endDraw();

// image(canvas, 0, 0);

// add bloom filter

  fx.render(canvas)
    .brightPass(0.5)
    .blur(200, 200)
    .compose();

}
cyrillkuhlmann commented 2 years ago

+++ EDIT +++

I think i did it – this is what I was trying to achieve:

import ch.bildspur.postfx.builder.*;
import ch.bildspur.postfx.pass.*;
import ch.bildspur.postfx.*;

PostFX fx;

PGraphics canvas;
float size;
float pixel;

void setup()
{
  size(500, 500, P2D);

    pixel = 20;
  size = width/pixel;

  fx = new PostFX(this);  
  canvas = createGraphics(width, height, P2D);

    rectMode(CENTER);
}

void draw()
{

  background(0);
  // draw a simple rotating cube around a sphere
  canvas.beginDraw();

  canvas.noStroke();
  canvas.fill(255);
  canvas.ellipse(width/2, height/2, 200, 200);
  canvas.endDraw();

// blendMode(BLEND);

    fx.render(canvas)
    .brightPass(0.5)
    .blur(200, 200)
    .compose(canvas);

    for (int y = 0; y < pixel; y++) {
    for (int x = 0; x < pixel; x++) {

      color c = canvas.get(int(size*x), int(size*y));
      float motionsize = map(brightness(c), 0, 255, 0, size);
      pushMatrix();
      translate(x*size, y*size);
      rotate(radians(45));
      fill(255);
      noStroke();
      ellipse(0, 0, motionsize, motionsize);
      popMatrix();
    }
  }

}

The thing was to write the PGraphics name into compose(); – is that correct?

cansik commented 2 years ago

Render does not display anything, it's the compose method. And as documented (not with an example), it is possible to compose the buffer onto something else than the main-canvas:

  fx.render(pg)
    .blur(10, 10)
    .compose(pg);

You would do this after drawing your mask pg. But there are several other issues with your sketch:

First of all, I would recommend to only use P2D specific PGraphics objects, otherwise you are mixing Java2D and P2D:

// set the mode of this PGraphics object to P2D in setup
pg = createGraphics(width, height, P2D);

Second, never use get(x, y) to read pixel values from an image. It is slow, even slower if you deal with framebuffers. Always try to load the framebuffer into CPU space once (pg.loadPixels()) and then use the pixels array which is available:

// load pixels before loop
pg.loadPixels();
for (int y = 0; y < pixel; y++) {
  for (int x = 0; x < pixel; x++) {

    // access pixels through the pixels array by using index access
    int ix = int(size*x);
    int iy = int(size*y);
    color c = pg.pixels[iy * width + ix];
Here the full sketch, running with 30 FPS. ```processing import ch.bildspur.postfx.builder.*; import ch.bildspur.postfx.pass.*; import ch.bildspur.postfx.*; PostFX fx; PGraphics pg; PGraphics pgResult; float size; float pixel; ArrayList circles; void setup () { size(600, 600, P2D); pixel = 50; size = width/pixel; circles = new ArrayList(); circles.add(new Circle()); frameRate(30); rectMode(CENTER); fx = new PostFX(this); pg = createGraphics(width, height, P2D); } void draw() { background(0); pg.beginDraw(); pg.background(255); for (int i = circles.size()-1; i >= 1; i--) { Circle circle = circles.get(i); circle.display(pg); } pg.endDraw(); fx.render(pg) .blur(10, 10) .compose(pg); //DRAW THE GRID pg.loadPixels(); for (int y = 0; y < pixel; y++) { for (int x = 0; x < pixel; x++) { int ix = int(size*x); int iy = int(size*y); color c = pg.pixels[iy * width + ix]; float motionsize = map(brightness(c), 0, 255, size, 0); pushMatrix(); translate(size/2, size/2); translate(x*size, y*size); fill(255); noStroke(); ellipse(0, 0, motionsize, motionsize); popMatrix(); } } surface.setTitle("FPS: " + frameRate); } void mousePressed() { circles.add(new Circle()); } class Circle { int x = int(random(width)); int y = int(random(height)); Circle() { } void display(PGraphics pg) { pg.fill(0); pg.noStroke(); pg.ellipse(x, y, 100, 100); } } ```

And last but not least, what you are trying to do is using an image mask to mask out another image. Try to use blendMode operations to do that, it is way faster.

cyrillkuhlmann commented 2 years ago

WOW! Thank you so much! This helps a lot… Also with all the other explanations! I did not know that get() is this slow… I have one more little question according… I know that:

pixels[x+y * width] (or in our case):

      int ix = int(size*x);
      int iy = int(size*y);
      color c = pg.pixels[iy * width + ix];

lets us run through each pixel… if my sketch size is squared… like: size(1080,1080); but i can not get the math right if it is rectangular … like: size(1920, 1080);

How do I then have to calculate?

cansik commented 2 years ago

You should ask these questions in the processing forum, because this is not related with my library. But as a hint, you are setting size = width/pixel;, which you should calculate for width & height separately.

cyrillkuhlmann commented 2 years ago

Ah yes! This is it... I have less rows than columns – thank you so much!

cansik commented 2 years ago

I am closing this issue as it seems to be solved.