Howdy, Stranger!

We are about to switch to a new forum software. Until then we have removed the registration on this forum.

  • PostFX library not working, error: InvalidPathException: Illegal char

    there's stuff in java/ch/bildspur/postfx/pass/BasePass.java which handles filenames. there is windows-specific code in there so you'd think it would work.

  • PostFX library not working, error: InvalidPathException: Illegal char

    AdvancedEffect, CustomShaderEffect, OffScreenEffect, ReadMeRendering, and SimpleEffect are not running. These examples contains "Pass" that are showing error message:

    C:\Documents\Processing\libraries\PostFX\src\main\java\ch\bildspur\postfx\pass

    go to above path on your computer, you will find different "pass", all "pass" are showing error in above mentioned examples.

  • PostFX library not working, error: InvalidPathException: Illegal char

    All examples are not running, BrightPass, sobelPass, BloomPass, BlurPass, and other are showing error.

    does not compute.

    i've just installed it (1.1) and i only get these examples:

    https://github.com/cansik/processing-postfx/tree/master/examples

  • PostFX library not working, error: InvalidPathException: Illegal char

    There is an error in running the example codes of PostFX library, the error is: InvalidPathException: Illegal char <:> Please help me how to resolve this???????

  • 3D objects and blendMode(ADD) causes artifacts
    PImage img;
    void setup() {
      size(480,640,P3D);
      hint(DISABLE_DEPTH_TEST);
      blendMode(ADD);
      //image credit: pixabay.com/de/startrails-felsen-nacht-918551/ CC0
      img=loadImage("https://"+"cdn.pixabay.com/photo/2015/09/02/12/36/startrails-918551_640.jpg?attachment");
    }
    int y=0;
    void draw() {
      background(0);
      image(img,0,0);
      translate(width/2,(y>640)?y-=y:y++);
      fill(204,102,0,125);
      sphere(90);
    }
    

    @trailbalzer47 can't help you any further. Blend Modes is something on my to do list. I just try things out.

    Here is a nice PostFx Lib
    github.com/cansik/processing-postfx

    Good Luck.

  • Advanced OpenGL: Point Size

    @nabr I'm hoping to have some PostFX render pass classes to share.

    a|x

  • PostFX - Shader based post processing library

    @toneburst Yes the OpenGL error is a bit annoying. It happens only with some shaders, but I did not have time to investigate it. But as far as I know it's more a warning then a real error. I have it on track here (it was the first issue I added ;) ):

    https://github.com/cansik/processing-postfx/issues/1

    Copying pixels around (loadpixels / updatepixels) is very slow. Try to avoid it. It simpler to draw the texture on to the other with the image(img, x, y) method:

    pass.beginDraw();
    // also apply shader if you want to process the drawn texture
    // pass.shader(myShader);
    pass.image(previewsTexture, 0, 0);
    pass.endDraw();
    

    I hope that helps :) This does the same as the filter() method with a shader.

  • PostFX - Shader based post processing library

    @cansik incidentally, I get an error

    OpenGL error 1282 at top endDraw(): invalid operation

    every time I run my sketch, when using PostFX passes.

    a|x

  • PostFX - Shader based post processing library

    @cansik Hi, hope you don't mind me cross-posting, but is this the best (fastest) way to implement texture feedback in a custom PostFX pass definition?

            pass.loadPixels();
            previousTexture.loadPixels();
            arrayCopy(pass.pixels, previousTexture.pixels);
            pass.updatePixels();
            previousTexture.updatePixels();
    

    a|x

  • Chaining Shaders and Feedback

    @nabr thanks re. the feedback! I got a bit sidetracked into messing around with that for a while. Still have a little idea I'd like to integrate into it.

    Am I right in thinking that copying the pixels in the custom PostFX pass definition is a performance bottleneck, do you think? I can't imagine it's done on the GPU.

    a|x

  • Chaining Shaders and Feedback

    @toneburst

    Yes, their a bug in your code. @cansik know's a quick fix i think

    It was not working, on my PC i changed few lines to make it work

    https://processing.org:8443/tutorials/video/

    You have different declarations like P2D and then P3D. I found it's best, while debuging your stetch, when you use nummers in the size(nummer, nummer, P3D)
    - and then createGraphic(nummer,nummer) >instead of width, height. Thouse values can evaluate druing the "startup" and "miss" the setup. default is 100x100 px so you would run a createGraphic, or a box(size) Object at a lower res ...etc.

       //
      ////////////////////////////////////////////////////
      ////////////////////////////////////////////////////
    
      //A-Life
    
     //Alex Drinkwater 2017
    
      ////////////////////////////////////////////////////
      ////////////////////////////////////////////////////
      //
    
    // Video library
    import processing.video.*;
    
    // PostFX shader-chaining library
    // https://github.com/cansik/processing-postfx
    import ch.bildspur.postfx.builder.*;
    import ch.bildspur.postfx.pass.*;
    import ch.bildspur.postfx.*;
    
    // ControlP5 GUI library
    // https://github.com/sojamo/controlp5
    import controlP5.*;
    
    //////////////////////////////////////////////////////
    //////////////////////////////////////////////////////
    // GLOBAL VARIABLES //////////////////////////////////
    //////////////////////////////////////////////////////
    //////////////////////////////////////////////////////
    
    Capture cam;
    PGraphics camFrame;
    
    // Custom shader pass class (requires PostFX)
    PostFX fx;
    FeedbackPass feedbackPass;
    ConwayPass conwayPass;
    
    PGraphics canvas;
    
    // GUI library
    ControlP5 cp5;
    color guiColor = color(200, 200, 200);
    
    float brushSize;
    float feedback;
    float channelSpread;
    boolean runFX;
    
    //changed
    void captureEvent(Capture video) {
      video.read();
    }
    //////////////////////////////////////////////////////
    //////////////////////////////////////////////////////
    // SETUP /////////////////////////////////////////////
    //////////////////////////////////////////////////////
    //////////////////////////////////////////////////////
    
    void setup() {
    
      //changed
      size(640, 480, P3D);
    
      //////////////////
      // Init Capture //
      //////////////////
    
      //changed
      camFrame = createGraphics(640, 480, P3D);
    
      ///////////////////////
      // PostFX pass stuff //
      ///////////////////////
    
      fx            = new PostFX(this);
      feedbackPass  = new FeedbackPass();
      conwayPass    = new ConwayPass();
    
      //////////////////
      // Add controls //
      //////////////////
    
      cp5 = new ControlP5(this);
      cp5.addSlider("brushSize")
        .setPosition(40, 40)
        .setSize(100, 20)
        .setRange(0.01, 0.05)
        .setValue(0.025)
        .setColorCaptionLabel(guiColor);
    
      cp5.addSlider("feedback")
        .setPosition(40, 70)
        .setSize(100, 20)
        .setRange(0.0, 0.90)
        .setValue(0.80)
        .setColorCaptionLabel(guiColor);
    
      cp5.addSlider("channelSpread")
        .setPosition(40, 100)
        .setSize(100, 20)
        .setRange(0.00, 0.07)
        .setValue(0.0)
        .setColorCaptionLabel(guiColor);
    
      cp5.addToggle("runFX")
        .setPosition(40, 130)
        .setSize(20, 20)
        .setColorCaptionLabel(guiColor)
        .setValue(false);
    
      //changed   
      cam = new Capture(this, 640, 480);
      cam.start();
    }
    
    //////////////////////////////////////////////////////
    //////////////////////////////////////////////////////
    // DRAW //////////////////////////////////////////////
    //////////////////////////////////////////////////////
    //////////////////////////////////////////////////////
    
    void draw() {
    
      // Set feedback level for feedback pass shader
      feedbackPass.setFeedback(feedback);
      feedbackPass.setChannelSpread(channelSpread);
    
      conwayPass.setStartFX(runFX);
      conwayPass.setMouse(map(mouseX, 0, width, 0, 1), map(mouseY, 0, height, 1, 0));
      conwayPass.setBrushSize(brushSize);
    
      // Copy capture pixels to PGraphics instance
      cam.loadPixels();
      camFrame.loadPixels();
      arrayCopy(cam.pixels, camFrame.pixels);
      cam.updatePixels();
      camFrame.updatePixels();
    
      image(camFrame, 0, 0);
    
      //OpenGL error 1282 at top endDraw(): invalid operation
      //Apply passes
      blendMode(BLEND);
       fx.render()
       .custom(conwayPass)
       .custom(feedbackPass)
       //.bloom(0.5, 20, 40)
       .compose();
    
      //enable for debug
      //image(cam, 0, 0);
    }
    
  • PostFX - Shader based post processing library

    Hi @cansik is this the right place to post about issues using PostFX?

    I'm having some problems implementing texture feedback in a custom pass (more details in this thread https://forum.processing.org/two/discussion/22508/chaining-shaders-and-feedback#latest ).

    a|x

  • Chaining Shaders and Feedback

    Got image capture working :)

    /*
      ////////////////////////////////////////////////////
      ////////////////////////////////////////////////////
    
      A-Life
    
      Alex Drinkwater 2017
    
      ////////////////////////////////////////////////////
      ////////////////////////////////////////////////////
    */
    
    //////////////////////////////////////////////////////
    //////////////////////////////////////////////////////
    // LIBRARIES /////////////////////////////////////////
    //////////////////////////////////////////////////////
    //////////////////////////////////////////////////////
    
    // Video library  
    import processing.video.*;
    
    // PostFX shader-chaining library
    // https://github.com/cansik/processing-postfx
    import ch.bildspur.postfx.builder.*;
    import ch.bildspur.postfx.pass.*;
    import ch.bildspur.postfx.*;
    
    // ControlP5 GUI library
    // https://github.com/sojamo/controlp5
    import controlP5.*;
    
    //////////////////////////////////////////////////////
    //////////////////////////////////////////////////////
    // GLOBAL VARIABLES //////////////////////////////////
    //////////////////////////////////////////////////////
    //////////////////////////////////////////////////////
    
    Capture cam;
    PGraphics camFrame;
    
    // Custom shader pass class (requires PostFX)
    PostFX fx;
    FeedbackPass feedbackPass;
    ConwayPass conwayPass;
    
    PGraphics canvas;
    
    // GUI library
    ControlP5 cp5;
    color guiColor = color(200,200,200);
    
    float brushSize;
    float feedback;
    boolean runFX;
    
    //////////////////////////////////////////////////////
    //////////////////////////////////////////////////////
    // SETUP /////////////////////////////////////////////
    //////////////////////////////////////////////////////
    //////////////////////////////////////////////////////
    
    void setup() {
    
      size(1280, 720, P2D);
    
      //////////////////
      // Init Capture //
      //////////////////
    
      String[] cameras = Capture.list();
    
      if (cameras.length == 0) {
        println("There are no cameras available for capture.");
        exit();
      } else {
        println("Available cameras:");
        for (int i = 0; i < cameras.length; i++) {
          println(cameras[i]);
        }   
        // The camera can be initialized directly using an 
        // element from the array returned by list():
        cam = new Capture(this, cameras[0]);
        cam.start();     
      }
    
      camFrame = createGraphics(width, height, P2D);
    
      ///////////////////////
      // PostFX pass stuff //
      ///////////////////////
    
      fx            = new PostFX(this);
      feedbackPass  = new FeedbackPass();
      conwayPass    = new ConwayPass();
    
      //////////////////
      // Add controls //
      //////////////////
    
      cp5 = new ControlP5(this);
      cp5.addSlider("brushSize")
        .setPosition(40, 40)
        .setSize(100, 20)
        .setRange(0.01, 0.05)
        .setValue(0.025)
        .setColorCaptionLabel(guiColor);
    
      cp5.addSlider("feedback")
        .setPosition(40, 70)
        .setSize(100, 20)
        .setRange(0., 0.95)
        .setValue(0.80)
        .setColorCaptionLabel(guiColor);
    
       cp5.addToggle("runFX")
         .setPosition(40, 100)
         .setSize(20, 20)
         .setColorCaptionLabel(guiColor)
         .setValue(false);
    }
    
    //////////////////////////////////////////////////////
    //////////////////////////////////////////////////////
    // DRAW //////////////////////////////////////////////
    //////////////////////////////////////////////////////
    //////////////////////////////////////////////////////
    
    void draw() {
    
      // Set feedback level for feedback pass shader
      feedbackPass.setfeedback(feedback);
    
      conwayPass.setStartFX(runFX);
      conwayPass.setMouse(map(mouseX, 0, width, 0, 1), map(mouseY, 0, height, 1, 0));
      conwayPass.setBrushSize(brushSize);
    
      if (cam.available() == true) {
        cam.read();
      }
    
      cam.loadPixels();
      camFrame.loadPixels();
      arrayCopy(cam.pixels, camFrame.pixels);
      cam.updatePixels();
      camFrame.updatePixels();
    
      image(camFrame, 0, 0);
    
      // Apply passes
      blendMode(BLEND);
      fx.render()
        //.custom(conwayPass)
        .custom(feedbackPass)
        //.bloom(0.5, 20, 40)
        .compose();
    }
    

    a|x

  • Chaining Shaders and Feedback

    Too easy...

    This seems to work :)

    /*
      ////////////////////////////////////////
      PostFX custom pass class definition
    
      Dependencies:
      PostFX library
      ////////////////////////////////////////
    */
    
    class FeedbackPass implements Pass
    {
      private PShader shader;
    
      private float feedbackLevel;
    
      private PGraphics previousTex;
    
      /////////////////
      // Constructor //
      /////////////////
    
      public FeedbackPass()
      {
        shader = loadShader("feedback.glsl");
        previousTex = createGraphics(width, height, P3D);
      }
    
      @Override
        public void prepare(Supervisor supervisor) {
           shader.set("oldTexture", previousTex);
      }
    
      @Override
        public void apply(Supervisor supervisor) {
        PGraphics pass = supervisor.getNextPass();
        //supervisor.clearPass(pass);
    
        shader.set("feedback", feedbackLevel);
        pass.beginDraw();
        pass.shader(shader);
        pass.image(supervisor.getCurrentPass(), 0, 0);
        pass.endDraw();
    
        // Update previous texture
        previousTex = pass;  
      }
    
      ///////////////////// 
      // Mutator methods //
      /////////////////////
    
      public void setfeedback(float feedback)
      {
        this.feedbackLevel = feedback;
      }
    }
    

    I have a feeling that setting 'oldTex' to 'pass' like this (line 45) may not be the fastest way to do it.

    I tried arrayCopy, but got a nullPointer exception.

    a|x

  • Chaining Shaders and Feedback

    Aha.. if I comment out the line

    supervisor.clearPass(pass);

    in my feedbackPass class declaration, it works...

    I've added a function to my feedbackPass class to allow me to change one of the shader uniforms from the draw loop in the main sketch. No idea if this is the correct way to do this, as I'm unfamiliar with Java (and OOP in general, to my shame).

    FeedbackPass.pde:

    class FeedbackPass implements Pass
    {
      PShader shader;
      float feedbackLevel;
    
      public FeedbackPass()
      {
        shader = loadShader("feedback.glsl");
      }
    
      @Override
        public void prepare(Supervisor supervisor) {
        shader.set("feedback", 0.75);
      }
    
      @Override
        public void apply(Supervisor supervisor) {
        PGraphics pass = supervisor.getNextPass();
        //supervisor.clearPass(pass);
    
        shader.set("feedback", feedbackLevel);
        pass.beginDraw();
        pass.shader(shader);
        pass.image(supervisor.getCurrentPass(), 0, 0);
        pass.endDraw();
      }
    
      public void setfeedback(float feedback)
      {
        feedbackLevel = feedback;
      }
    }
    

    And the main sketch .pde:

    // PostFX shader-chaining library
    import ch.bildspur.postfx.builder.*;
    import ch.bildspur.postfx.pass.*;
    import ch.bildspur.postfx.*;
    // GLSL version of Conway's game of life
    PShader conway;
    PGraphics canvas;
    
    // Custom shader pass class
    PostFX fx;
    FeedbackPass feedbackPass;
    
    // GUI library
    import controlP5.*;
    ControlP5 cp5;
    
    float brushSize;
    float feedback;
    
    ///////////////////////////
    // SETUP //////////////////
    ///////////////////////////
    
    void setup() {
    
      size(640, 480, P2D);
    
      // Add controls
      cp5 = new ControlP5(this);
      cp5.addSlider("brushSize")
        .setPosition(40, 40)
        .setSize(100, 20)
        .setRange(0.01, 0.05)
        .setValue(0.025)
        .setColorCaptionLabel(color(200,200,200));
    
      cp5.addSlider("feedback")
        .setPosition(40, 70)
        .setSize(100, 20)
        .setRange(0., 0.95)
        .setValue(0.5)
        .setColorCaptionLabel(color(200,200,200));
    
      // Init shader stuff
      canvas = createGraphics(width, height, P3D);
      canvas.noSmooth();
    
      conway = loadShader("conway.glsl");
      conway.set("resolution", float(canvas.width), float(canvas.height));
    
      // PostFX pass stuff
      //supervisor = new PostFXSupervisor(this);
      fx = new PostFX(this);
      feedbackPass = new FeedbackPass();
    }
    
    ///////////////////////////
    // DRAW ///////////////////
    ///////////////////////////
    
    void draw() {
    
      // Shader uniforms
      conway.set("time", millis()/1000.0);
      float x = map(mouseX, 0, width, 0, 1);
      float y = map(mouseY, 0, height, 1, 0);
      conway.set("brushsize", brushSize); 
      conway.set("mouse", x, y);  
    
      // Draw shader
      canvas.beginDraw();
      canvas.background(0);
    
      canvas.pushMatrix();
    
      canvas.shader(conway);
      canvas.rect(0, 0, canvas.width, canvas.height);
    
      canvas.popMatrix();
      canvas.endDraw();
    
      image(canvas, 0, 0);
    
      // Set feedback level for feedback pass shader
      feedbackPass.setfeedback(feedback);
    
      // Apply passes
      blendMode(BLEND);
      fx.render()
        //.bloom(0.5, 20, 40)
        .custom(feedbackPass)
        .compose();
    
    }
    

    a|x

  • Chaining Shaders and Feedback

    So, now I have:

    // PostFX shader-chaining library
    import ch.bildspur.postfx.builder.*;
    import ch.bildspur.postfx.pass.*;
    import ch.bildspur.postfx.*;
    // GLSL version of Conway's game of life
    PShader conway;
    PGraphics canvas;
    
    // Custom shader pass class
    PostFX fx;
    //PostFXSupervisor supervisor;
    FeedbackPass feedbackPass;
    NegatePass negatePass;
    
    // GUI library
    import controlP5.*;
    ControlP5 cp5;
    
    float brushSize;
    
    ///////////////////////////
    // SETUP //////////////////
    ///////////////////////////
    
    void setup() {
    
      size(640, 480, P2D);
    
      // Add controls
      cp5 = new ControlP5(this);
      cp5.addSlider("brushSize")
        .setPosition(40, 40)
        .setSize(100, 20)
        .setRange(0.01, 0.05)
        .setValue(0.025)
        .setColorCaptionLabel(color(200,200,200));
    
      // Init shader stuff
      canvas = createGraphics(width, height, P3D);
      canvas.noSmooth();
    
      conway = loadShader("conway.glsl");
      conway.set("resolution", float(canvas.width), float(canvas.height));
    
      // PostFX pass stuff
      //supervisor = new PostFXSupervisor(this);
      fx = new PostFX(this);
      feedbackPass = new FeedbackPass();
      negatePass = new NegatePass();
    }
    
    ///////////////////////////
    // DRAW ///////////////////
    ///////////////////////////
    
    void draw() {
    
      // Shader uniforms
      conway.set("time", millis()/1000.0);
      float x = map(mouseX, 0, width, 0, 1);
      float y = map(mouseY, 0, height, 1, 0);
      conway.set("brushsize", brushSize); 
      conway.set("mouse", x, y);  
    
      // Draw shader
      canvas.beginDraw();
      canvas.background(0);
    
      canvas.pushMatrix();
    
      canvas.shader(conway);
      canvas.rect(0, 0, canvas.width, canvas.height);
    
      canvas.popMatrix();
      canvas.endDraw();
    
      //
      blendMode(BLEND);
      fx.render(canvas)
        .bloom(0.5, 20, 40)
        .custom(feedbackPass)
        .compose();
    
    }
    

    And my shader:

    // Mix between input texture and previous frame pixels
    
    #ifdef GL_ES
    precision mediump float;
    precision mediump int;
    #endif
    
    uniform sampler2D texture;
    uniform sampler2D ppixels;
    uniform float feedback;
    varying vec4 vertTexCoord;
    
    void main( void ) {
        vec2 position = vertTexCoord.st;
        gl_FragColor = mix(texture2D(texture, position), texture2D(ppixels, position), feedback);
    }
    

    Which doesn't work. The ppixel texture in the feedback shader seems to contain nothing, so changing the feedback amount just has the effect of mixing between the output of the first pass and black, and making the image darker. I'd intended to create a kind of 'trails' effect.

    Does this mean I have to manage my own FBOs manually, in order to get feedback working?

    I'm able to use ppixels for feedback, while I do the initial drawing of the Conway shader, before I apply the PostFX passes.

    a|x

  • Chaining Shaders and Feedback

    I've made some progress.

    // PostFX shader-chaining library
    import ch.bildspur.postfx.builder.*;
    import ch.bildspur.postfx.pass.*;
    import ch.bildspur.postfx.*;
    // GLSL version of Conway's game of life
    PShader conway;
    PGraphics canvas;
    
    // Custom shader pass class
    PostFX fx;
    //PostFXSupervisor supervisor;
    //FeedbackPass feedbackPass;
    NegatePass negatePass;
    
    // GUI library
    import controlP5.*;
    ControlP5 cp5;
    
    float brushSize;
    
    ///////////////////////////
    // SETUP //////////////////
    ///////////////////////////
    
    void setup() {
    
      size(640, 480, P2D);
    
      // Add controls
      cp5 = new ControlP5(this);
      cp5.addSlider("brushSize")
        .setPosition(40, 40)
        .setSize(100, 20)
        .setRange(0.01, 0.05)
        .setValue(0.025)
        .setColorCaptionLabel(color(200,200,200));
    
      // Init shader stuff
      canvas = createGraphics(width, height, P3D);
      canvas.noSmooth();
    
      conway = loadShader("conway.glsl");
      conway.set("resolution", float(canvas.width), float(canvas.height));
    
      // PostFX pass stuff
      //supervisor = new PostFXSupervisor(this);
      fx = new PostFX(this);
      //feedbackPass = new FeedbackPass();
      negatePass = new NegatePass();
    }
    
    ///////////////////////////
    // DRAW ///////////////////
    ///////////////////////////
    
    void draw() {
    
      // Shader uniforms
      conway.set("time", millis()/1000.0);
      float x = map(mouseX, 0, width, 0, 1);
      float y = map(mouseY, 0, height, 1, 0);
      conway.set("brushsize", brushSize); 
      conway.set("mouse", x, y);  
    
      // Draw shader
      canvas.beginDraw();
      canvas.background(0);
    
      canvas.pushMatrix();
    
      canvas.shader(conway);
      canvas.rect(0, 0, canvas.width, canvas.height);
    
      canvas.popMatrix();
      canvas.endDraw();
    
      //
      blendMode(BLEND);
      fx.render(canvas)
        .bloom(0.5, 20, 40)
        //.custom(negatePass)
        .compose();
    
    }
    

    This nicely applies a bloom effect to the output of the conway shader.

    My hope is to replace the negate shader, from the PostFX CustomShaderEffect example with my own, but for the moment, I'm just using the negate shader from the example.

    Unfortunately, if I uncomment the line

    .custom(negatePass)

    I get an OpenGL error

    ERROR: 0:12: Regular non-array variable 'vertColor' may not be redeclared

    Is there a way to fix this, @cansik?

    a|x

  • Chaining Shaders and Feedback

    Hi,

    got sidetracked and ended up trying to do this in Quartz Composer, failed, so I'm now back in Processing...

    @cansik, can I access the previous frame's texture using ppixels when using postFX?

    a|x

  • PostFX - Shader based post processing library

    Congrats on this work. A major contribution! And just a reminder to all, PostFX is on the libraries page under utilities:

    ...and can be found in the PDE Contributions Manager (although that version number isn't always up-to-the-minute -- and you may need to restart PDE).

  • PostFX - Shader based post processing library

    I am happy to announce that I released version 1.1 today!

    Version 1.1 brings a lot of new features to post processing library. First of all a bunch of new shaders have been added to the library, which are useful for 3D and 2D scenes. Here is a list of the new shaders:

    Color

    • Brightness and Contrast Shader
    • Saturation and Vibrance Shader
    • Invert Shader
    • Grayscale Shader

    Reconstruction

    • Denoise Shader

    Effects

    • Bloom Shader
    • Pixelate Shader
    • Chromatic aberration shader
    • Noise Shader
    • Vignette Shader
    • RGB Split Shader

    Effects

    The second new thing is, that the library now works on the default graphics object (onscreen buffer g with P2D and P3D). So now it is not needed to draw your scene onto a canvas anymore. Just run following code:

    // in setup
    PostFX fx = new PostFX(this);
    
    // in draw
    fx.render()
      .invert()
      .compose();
    

    It is also possible now to preload shaders, to run initialisation in the setup method:

    fx.preload(InvertPass.class);

    And I also implemented a way to simply run your own custom passes:

    // in draw
    fx.render()
      .custom(myCustomPass)
      .compose();
    

    I hope the library works as expected! If you have any suggestion just write me here or leave a new issue in github!