A Better Way to Fade?

Hello friends,

I am having some trouble figuring out how to fade my sketch. By 'fade' I mean have older frames slowly lower their opacity to appear like they are wiping off the screen.

First Method

The traditional method I have used was to place a semi-transparent rectangle over my sketch at the beginning of the draw() loop in order to fake a fade. This, however, doesn't work if you want to export an image with Alpha using PGraphics, and also seems to create weird streaks or smudges rather than getting rid of the old frames entirely. I have read this is due to integer-rounding, preventing the semi-transparent rectangles from ever fully covering the old frames, since they never add up to full opacity. Regardless, this solution isn't working for me.

Second Method

The next method was formed out of a repurposing of Dan Shiffman's examples (ex 9.8 from Learning Processing) This involves drawing each frame as a PGraphics image, storing the last n images in an ArrayList, and then manually controlling their opacity. The code for this looked something like this:

PGraphics canvas,capture;
ArrayList< PImage > frames;
int fadeAmt = 20;

void setup(){
    size(600,600,P2D);
    canvas = createGraphics(600,600,P2D);
    capture = createGraphics(600,600,P2D);
    frames = new ArrayList< PImage >(); // make arraylist
}

void draw(){

    // temp background for processing, will not be saved.
    background(#CCCCCC);

    canvas.beginDraw();
    canvas.cear();
        //draw something to canvas here.
        canvas.noStroke();
        canvas.fill(0);
        canvas.ellipse(mouse.x, mouse.y, 60, 60);
    canvas.endDraw();

    //add this canvas to frames.
    frames.add(0, canvas.get());

    //remove last frame if past fadeAmt
    if(frames.size() > fadeAmt){
        frames.remove(frames.size()-1);
    }

    //draw frames to capture
    capture.beginDraw();
    capture.clear();

        // loop through all frames.
        for (int i = 0; i < frames.size(); ++i){

            capture.pushStyle();

                // map transparency based on current array position.
                float trans = map(i, 0, frames.size(), 255, 0);
                // apply transparency.
                capture.tint(255, trans);
                // draw the image.
                capture.image(frames.get(i),0,0);

            capture.popStyle();

        }

    capture.endDraw();

    // display capture.
    image(capture,0,0);

    capture.save(frameCount + ".png");

}

This method slowed down my sketches a lot (down to about 4 FPS), but did work for some sketches, allowing a smooth fade which was easily controllable with the fadeAmt variable. However, this does seem to give me weird issues with edging. I am not sure if it's a fine stroke being applied, or just strange aliasing, but sometimes the trails appear 'steppy' rather than smooth. Sometimes I also see bright shapes fade to black before disappearing.

The Third Option

The third option is one I have not yet gotten to work. I have played around w/ the HYPE framework a lot and really enjoy using it for simple animations. HYPE has a great built in .fade() method which works cleanly. Digging around the code, I found this snippet which I think is handling the fading:

if(!_renderer.equals(PConstants.JAVA2D))
    _graphics.loadPixels();
int[] pix = _graphics.pixels;
for(int i=0; i>> 24;
    if(a == 0) continue;
    a -= _fadeAmt;
    if(a < 0) a = 0;
    pix[i] = clr & 0xFFFFFF | (a 

My knowledge of bit-shifting is not super good, but from some online research it seems like the line int a = clr >>> 24 is extracting the alpha value of the color, and then the line pix[i] = clr & 0xFFFFFF | (a << 24) is re-applying an updated alpha value.

However, I can't seem to integrate this into my code and make it work. I'm not sure where it goes or what PGraphics it should be targeting.

I am hoping there's some super obvious function or method that I am missing. I feel like fading the screen is a very basic thing and I am slightly embarrassed that I haven't been able to figure it out. From quick searches on these and other forums, it doesn't appear that this problem is super common which is only making me more frustrated.

Anyone who can offer any advice is greatly appreciated.

Thanks so much!

Answers

  • How about 1st "traditional" option but "fading" over some PGraphics instead: 8->

    // forum.processing.org/two/discussion/13189/a-better-way-to-fade
    // GoToLoop (2015-Oct-22)
    
    static final int FPS = 10, FADE = 030;
    PGraphics fader;
    
    void setup() {
      size(600, 400, JAVA2D);
      frameRate(FPS);
      imageMode(CORNER);
    
      fader = createGraphics(width, height, JAVA2D);
      fader.smooth(4);
      fader.beginDraw();
      fader.strokeWeight(5);
      fader.stroke(-1);
    
      mousePressed();
    }
    
    void draw() {
      image(fader, 0, 0);
    
      fader.beginDraw();
      fader.pushStyle();
      fader.noStroke();
      fader.fill(0, FADE);
      fader.rect(0, 0, width, height);
      fader.popStyle();
      fader.endDraw();
    }
    
    void mousePressed() {
      fader.beginDraw();
      fader.fill((color) random(#000000));
      fader.ellipse(width>>1, height>>1, width*3/4, height*3/4);
      fader.endDraw();
    }
    
    void keyPressed() {
      if (key == ENTER | key == RETURN)  fader.save(dataPath(nf(frameCount, 4) + ".png"));
      else                               mousePressed();
    }
    
  • Ah! Very cool. Thanks so much for your reply.

    This does seem to completely clear the background which solves one of my problems.

    The follow-up question to this is, when I export the PNG from your sketch, I still get a low-alpha black value over the areas which I want to be transparent. Any way to set this up to give me a perfectly transparent alpha channel in the areas not occupied by your ellipse?

    Thanks so much!

  • Not w/ rect(). Since its fill() covers the whole area w/ semi-transparency. Sorry... :(

  • The other approach to this problem is to store the last n number of drawing actions and redraw them each frame, applying the opacity change as you go. In practice that's not always appropriate and the effect isn't necessarily equivalent...

    It might be possible to use this to fudge a solution to your transparency problem though: draw the last n actions to a separate pgraphic. You then have an alpha mask to apply to your image on export...

  • Answer ✓

    An example for the third option, using PGraphics an fading out the pixels manually:

      PGraphics canvas;
    
      void setup() {
        size(400, 400);
        canvas = createGraphics(width, height);
      }
    
      void draw() {
        background(200, 0, 0);
    
        fadeGraphics(canvas, 2);
    
        canvas.beginDraw();
        canvas.rect(mouseX, mouseY, 10, 10);
        canvas.endDraw();
    
        image(canvas, 0, 0);
      }
      void fadeGraphics(PGraphics c, int fadeAmount) {
        c.beginDraw();
        c.loadPixels();
    
        // iterate over pixels
        for (int i =0; i<c.pixels.length; i++) {
    
          // get alpha value
          int alpha = (c.pixels[i] >> 24) & 0xFF ;
    
          // reduce alpha value
          alpha = max(0, alpha-fadeAmount);
    
          // assign color with new alpha-value
          c.pixels[i] = alpha<<24 | (c.pixels[i]) & 0xFFFFFF ;
        }
    
        canvas.updatePixels();
        canvas.endDraw();
      }
    
  • Ah, Benja, that is it! That's exactly what I was looking for. Works perfectly. Thanks everyone for your suggestions and help. :)

Sign In or Register to comment.