Frosted Glass (Blurry Glass) on Processing

edited November 2017 in GLSL / Shaders

Hello

I'm looking for a way to code a Frosted Glass surface on top of a P3D render in processing. I guess it will use some glsl shader and I've played a bit with them but didn't find a way to constrain the shader to a surface.

float alpha = 0;

void setup() { 
  size(640, 360, P3D);
  rectMode(CENTER);
} 

void draw() { 
  background(255, 128, 128);

  pushMatrix(); 
  fill(255);
  translate(width/2, height/2, -30); 
  rotateY(alpha);
  rotateX(alpha);
  box(150);
  popMatrix();

  pushMatrix(); 
  fill(255, 64);
  translate(width/2, height/2, 120); 
  rect(0, 0, 200, 100);
  popMatrix();

  alpha+= 0.01;
} 

what do you think would be the best way to make this rectangle blur what is behind it ?

cheers

Answers

  • Could you copy an area of the screen into a small rectangular PGraphics and then run the shader on the PGraphics?

    I believe there are some "frosted glass" shaders listed on shadertoy -- but I am no shader expert.

  • That's the kind of idea I'm looking for :)

    But is it possible to render the view of "a" camera into that rectangle PGraphics so that I could move the rectangle ?

  • Sure -- rather than image(pg,0,0) you can copy(pg, [camera geometry]):

  • BTW, you mentioned both frosted glass and a moving camera. On the off-chance you are trying to create a looking-glass effect, these examples might be interesting:

    - https://forum.processing.org/two/discussion/comment/76980/#Comment_76980 - https://forum.processing.org/two/discussion/comment/108038/#Comment_108038

  • Really nice but I'm looking for something like that https://codepen.io/jonitrythall/pen/GJQBOp

    to be able to draw a frosted glass plane inside a 3D scene

  • edited November 2017

    @phoebus -- huh.

    I just replaced a few lines at the beginning of the sketch setup to turn the two images into an unblurred and blurred version of the same image. Without any other changes it seemed like it was doing exactly what you wanted....

    void setup()
    {
      size(512, 512);
      maskSize = 200;
      imgForeground = loadImage("https://processing.org/img/processing3-logo.png");
      imgHidden = imgForeground.copy();
      imgHidden.filter(BLUR, 10);
      iw = imgHidden.width;
      ....
    

    See PImage, PImage.copy(), and PImage.blur(): https://processing.org/reference/PImage_filter_.html

    Of course, a shader will perform much better! But if you are only blurring the background once and not every frame then that performance probably doesn't matter.

  • this code does work as "Frosted Glass" for a certain image

    /**
     *  Mask to Reveal Hidden Image
     *  2016-10-19 Jeremy Douglass Processing 3.2.1
     *  based on a sketch by PhiLo -- https:// processing.org/discourse/beta/num_1231933751.html
     *  and a sketch by VD_D -- https:// forum.processing.org/two/discussion/18575/creating-a-looking-glass-an-ellipse-which-sees-the-image-underneath-it#latest
     **/
    
    PImage imgForeground;
    PImage imgHidden;
    PGraphics pgMask;
    PImage imgMask;
    int iw, ih;
    int dw, dh;
    float maskSize;
    
    void setup()
    {
      size(512, 512);
      maskSize = 200;
      imgForeground = loadImage("https://processing.org/img/processing3-logo.png");
      imgHidden = imgForeground.copy();
      imgHidden.filter(BLUR, 10);
      iw = imgHidden.width;
      ih = imgHidden.height;
      dw = width - iw;
      dh = height - ih;
      pgMask = createGraphics(iw, ih);
    }
    
    void draw()
    {
      background(200);
      image(imgForeground,0,0);
      pgMask.beginDraw();
        // Erase graphics
        pgMask.background(0);
        // Draw the mask
        int x = mouseX - dw/2;
        int y = mouseY - dh/2;
        pgMask.noStroke();
        //// mostly transparent circle (250) with fuzzy edge (0-250)
        for(int i=0;i<25;i++){
          pgMask.fill(0+i*10);
          pgMask.ellipse(x, y, maskSize-i*2, maskSize-i*2);
        }
        //// or just a simple circle
        // pgMask.fill(255);
        // pgMask.ellipse(x, y, maskSize, maskSize);
      pgMask.endDraw();
      //// Copy the original image (kept as reference)
      imgMask = imgHidden.get();
      //// Apply mask and display result
      imgMask.mask(pgMask);
      image(imgMask, dw/2, dh/2);
      //// Draw a magnifying frame
      stroke(96,48,32);
      strokeWeight(maskSize/10);
      noFill();
      ellipse(mouseX,mouseY,maskSize,maskSize);
      //// ...and handle
      strokeWeight(maskSize/5);
      line(mouseX+(maskSize/2/sqrt(2)),mouseY+(maskSize/2/sqrt(2)),mouseX+maskSize*3/4,mouseY+maskSize*3/4);
    }
    

    But I don't get how I can have it working here

    float alpha = 0;
    
    void setup() { 
      size(640, 360, P3D);
      rectMode(CENTER);
    } 
    
    void draw() { 
      background(255, 128, 128);
    
      pushMatrix(); 
      fill(255);
      translate(width/2, height/2, -30); 
      rotateY(alpha);
      rotateX(alpha);
      box(150);
      popMatrix();
    
      pushMatrix(); 
      fill(255, 64);
      translate(width/2, height/2, 120); 
      rect(0, 0, 200, 100);
      popMatrix();
    
      alpha+= 0.01;
    } 
    
  • Ah, that second approach is less flexible but much simpler if you just want to wash a 3D scene with a partially transparent white rectangle.

    When you say "have it working" I assume that you mean there is a problem because you are losing the lines on your cube? you need to add this to setup:

    hint(ENABLE_DEPTH_SORT);
    

    That should create a brightening effect without the line loss:

      pushMatrix(); 
      fill(255, 160);
      translate(width/2, height/2, 120); 
      rect(0, 0, 200, 80);
      popMatrix();
    

    For more on OpenGL hints, see the JavaDoc API:

  • (it's not that easy have to interact with fellow creative coder only by forum and never in person :S)

    So, I have this Code

    float alpha = 0;
    
    void setup() { 
      size(640, 360, P3D);
      hint(ENABLE_DEPTH_SORT);
      rectMode(CENTER);
    } 
    
    void draw() { 
      background(255, 128, 128);
    
      pushMatrix(); 
      fill(255);
      translate(width/2, height/2, -30); 
      rotateY(alpha);
      rotateX(alpha);
      box(150);
      popMatrix();
    
      pushMatrix(); 
      fill(255, 64);
      translate(width/2, height/2, 120); 
      rect(0, 0, 200, 100);
      popMatrix();
    
      alpha+= 0.01;
    } 
    

    it looks like that : notBlur

    and I would like it to look like that blur

  • i think you are searching for something like this:

    processing code:

    PShader blur;
    PImage img;
    PGraphics src;
    PGraphics pass1, pass2;
    
    void settings() {
      img=loadImage("img.jpg");
      //size(img.width, img.height, P3D);
      size(500, 500, P3D);
    }
    void setup() {
      blur = loadShader("blur.glsl"); 
      blur.set("blurSize", 100);
      blur.set("sigma", 15.0f);
      blur.set("resolution", float(width), float(height));
      src = createGraphics(width, height, P3D); 
    
      pass1 = createGraphics(width, height, P3D);
      pass1.noSmooth();  
    
      pass2 = createGraphics(width, height, P3D);
      pass2.noSmooth();
    }
    
    void draw() {
      src.beginDraw();
      src.pushMatrix();
      src.translate(width/2, height/2);
      src.rotateY(90);
      src.rotateX(90);
      src.background(0);
      src.box(250);
      src.popMatrix();
      src.endDraw();
      // Applying the blur shader along the vertical direction   
      blur.set("horizontalPass", 0);
      pass1.beginDraw();            
      pass1.shader(blur);  
      pass1.image(src, 0, 0);
      pass1.endDraw();
      // Applying the blur shader along the horizontal direction      
      blur.set("horizontalPass", 1);
      pass2.beginDraw();            
      pass2.shader(blur);  
      pass2.image(pass1, 0, 0);
      pass2.endDraw();    
      image(pass2, 0, 0);
    }
    

    glsl code:

    // Adapted from:
    // http://callumhay.blogspot.com/2010/09/gaussian-blur-shader-glsl.html
    
    #ifdef GL_ES
    precision mediump float;
    precision mediump int;
    #endif
    
    #define PROCESSING_TEXTURE_SHADER
    
    uniform sampler2D texture;
    
    // The inverse of the texture dimensions along X and Y
    uniform vec2 texOffset;
    uniform vec2 mouse;
    uniform vec2 resolution;
    varying vec4 vertColor;
    varying vec4 vertTexCoord;
    
    uniform int blurSize;       
    uniform int horizontalPass; // 0 or 1 to indicate vertical or horizontal pass
    uniform float sigma;        // The sigma value for the gaussian function: higher value means more blur
                                // A good value for 9x9 is around 3 to 5
                                // A good value for 7x7 is around 2.5 to 4
                                // A good value for 5x5 is around 2 to 3.5
                                // ... play around with this based on what you need :)
    
    const float pi = 3.14159265;
    
    void main() {  
      float numBlurPixelsPerSide = float(blurSize / 2); 
      vec2 uv = gl_FragCoord.xy/resolution.xy;
      vec2 blurMultiplyVec = 0 < horizontalPass ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
      uv.y=1.-uv.y;
      // Incremental Gaussian Coefficent Calculation (See GPU Gems 3 pp. 877 - 889)
      vec3 incrementalGaussian;
      incrementalGaussian.x = 1.0 / (sqrt(2.0 * pi) * sigma);
      incrementalGaussian.y = exp(-0.5 / (sigma * sigma));
      incrementalGaussian.z = incrementalGaussian.y * incrementalGaussian.y;
    
      vec4 avgValue = vec4(0.0, 0.0, 0.0, 0.0);
      float coefficientSum = 0.0;
    
      // Take the central sample first...
      avgValue += texture2D(texture, vertTexCoord.st) * incrementalGaussian.x;
      coefficientSum += incrementalGaussian.x;
      incrementalGaussian.xy *= incrementalGaussian.yz;
    if(uv.y>0.3 && uv.y<0.7){
      // Go through the remaining 8 vertical samples (4 on each side of the center)
      for (float i = 1.0; i <= numBlurPixelsPerSide; i++) { 
        avgValue += texture2D(texture, vertTexCoord.st - i * texOffset * 
                              blurMultiplyVec) * incrementalGaussian.x;         
        avgValue += texture2D(texture, vertTexCoord.st + i * texOffset * 
                              blurMultiplyVec) * incrementalGaussian.x;         
        coefficientSum += 2.0 * incrementalGaussian.x;
        incrementalGaussian.xy *= incrementalGaussian.yz;
      }
    } 
      gl_FragColor = avgValue / coefficientSum;
    }
    

    you can change the statement if to modify the blur rectangle.

    hope this help you :)

  • spot on @pietroLama !

    so here it is; exactly what I was looking for

    PShader blur;
    PImage img;
    PGraphics src;
    PGraphics pass1, pass2;
    
    float angle = 0;
    
    void setup() { 
      size(640, 360, P3D);
      img=loadImage("img.jpg");
      hint(ENABLE_DEPTH_SORT);
      rectMode(CENTER);
    
      //FROSTED SHADER
      blur = loadShader("blur.glsl"); 
      blur.set("blurSize", 100);
      blur.set("sigma", 7.0f);
      blur.set("resolution", float(width), float(height));
    
      //FROSTED OBJECT
      src = createGraphics(width, height, P3D); 
    
      //FROSTED GLASS1
      pass1 = createGraphics(width, height, P3D);
      pass1.noSmooth();  
      //FROSTED GLASS2
      pass2 = createGraphics(width, height, P3D);
      pass2.noSmooth();
    } 
    
    void draw() { 
    
      //CUBE
      src.beginDraw();
      src.pushMatrix();
      src.translate(width/2, height/2, -200);
      src.rotateY(angle);
      src.rotateX(angle);
      src.background(255, 128, 128); //SET BACKGROUND HERE
      src.box(250);
      src.popMatrix();
      src.endDraw();
      angle+= 0.01;
    
      //FROSTED GLASS
      // Applying the blur shader along the vertical direction   
      blur.set("horizontalPass", 0);
      pass1.beginDraw();    
      pass1.shader(blur);  
      pass1.image(src, 0, 0);
      pass1.endDraw();
      // Applying the blur shader along the horizontal direction      
      blur.set("horizontalPass", 1);
      pass2.beginDraw();         
      pass2.shader(blur);  
      pass2.image(pass1, 0, 0);
      pass2.endDraw();    
      image(pass2, 0, 0);
    
      //FRAME THE FROSTED GLASS
      pushMatrix(); 
      noFill();
      stroke(0);
      translate(width/2, height/2, 0); 
      rect(0, 0, (width/10)*4, (height/10)*6);
      popMatrix();
    } 
    

    for the sketch and for the glsl file

    // Adapted from:
    // <a href="http://callumhay.blogspot.com/2010/09/gaussian-blur-shader-glsl.html" target="_blank" rel="nofollow">http://callumhay.blogspot.com/2010/09/gaussian-blur-shader-glsl.html</a>;
    
    #ifdef GL_ES
    precision mediump float;
    precision mediump int;
    #endif
    
    #define PROCESSING_TEXTURE_SHADER
    
    uniform sampler2D texture;
    
    // The inverse of the texture dimensions along X and Y
    uniform vec2 texOffset;
    uniform vec2 mouse;
    uniform vec2 resolution;
    varying vec4 vertColor;
    varying vec4 vertTexCoord;
    
    uniform int blurSize;       
    uniform int horizontalPass; // 0 or 1 to indicate vertical or horizontal pass
    uniform float sigma;        // The sigma value for the gaussian function: higher value means more blur
                                // A good value for 9x9 is around 3 to 5
                                // A good value for 7x7 is around 2.5 to 4
                                // A good value for 5x5 is around 2 to 3.5
                                // ... play around with this based on what you need <span class="Emoticon Emoticon1"><span>:)</span></span>
    
    const float pi = 3.14159265;
    
    void main() {  
      float numBlurPixelsPerSide = float(blurSize / 2); 
      vec2 uv = gl_FragCoord.xy/resolution.xy;
      vec2 blurMultiplyVec = 0 < horizontalPass ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
      uv.y=1.-uv.y;
      // Incremental Gaussian Coefficent Calculation (See GPU Gems 3 pp. 877 - 889)
      vec3 incrementalGaussian;
      incrementalGaussian.x = 1.0 / (sqrt(2.0 * pi) * sigma);
      incrementalGaussian.y = exp(-0.5 / (sigma * sigma));
      incrementalGaussian.z = incrementalGaussian.y * incrementalGaussian.y;
    
      vec4 avgValue = vec4(0.0, 0.0, 0.0, 0.0);
      float coefficientSum = 0.0;
    
      // Take the central sample first...
      avgValue += texture2D(texture, vertTexCoord.st) * incrementalGaussian.x;
      coefficientSum += incrementalGaussian.x;
      incrementalGaussian.xy *= incrementalGaussian.yz;
    if(uv.y> 0.2 && uv.y< 0.8 ){ //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!CHANGE the vertical size of the blur rect
        if(uv.x> 0.3 && uv.x< 0.7 ){ //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!CHANGE the horizontal size of the blur rect
          // Go through the remaining 8 vertical samples (4 on each side of the center)
          for (float i = 1.0; i <= numBlurPixelsPerSide; i++) { 
            avgValue += texture2D(texture, vertTexCoord.st - i * texOffset * 
                                  blurMultiplyVec) * incrementalGaussian.x;         
            avgValue += texture2D(texture, vertTexCoord.st + i * texOffset * 
                                  blurMultiplyVec) * incrementalGaussian.x;         
            coefficientSum += 2.0 * incrementalGaussian.x;
            incrementalGaussian.xy *= incrementalGaussian.yz;
          }
      }
    } 
      gl_FragColor = avgValue / coefficientSum;
    }
    

    and a special thank to @jeremydouglass for the shader free tricks

  • i suggest you to modify the code like this.

    glsl:

    // Adapted from:
    // http://callumhay.blogspot.com/2010/09/gaussian-blur-shader-glsl.html
    
    #ifdef GL_ES
    precision mediump float;
    precision mediump int;
    #endif
    
    #define PROCESSING_TEXTURE_SHADER
    
    uniform sampler2D texture;
    
    // The inverse of the texture dimensions along X and Y
    uniform vec2 texOffset;
    uniform vec2 mouse;
    uniform vec2 resolution;
    varying vec4 vertColor;
    varying vec4 vertTexCoord;
    uniform float rad; 
    uniform int blurSize;       
    uniform int horizontalPass; // 0 or 1 to indicate vertical or horizontal pass
    uniform float sigma;        // The sigma value for the gaussian function: higher value means more blur
    ////////////////////////////// A good value for 9x9 is around 3 to 5
    ////////////////////////////// A good value for 7x7 is around 2.5 to 4
    ////////////////////////////// A good value for 5x5 is around 2 to 3.5
    ////////////////////////////// ... play around with this based on what you need :)
    
    const float pi = 3.14159265;
    
    void main() {  
      float numBlurPixelsPerSide = float(blurSize / 2); 
      vec2 uv = gl_FragCoord.xy/resolution.xy;
      vec2 blurMultiplyVec = 0 < horizontalPass ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
      uv.y=1.-uv.y;
      // Incremental Gaussian Coefficent Calculation (See GPU Gems 3 pp. 877 - 889)
      vec3 incrementalGaussian;
      incrementalGaussian.x = 1.0 / (sqrt(2.0 * pi) * sigma);
      incrementalGaussian.y = exp(-0.5 / (sigma * sigma));
      incrementalGaussian.z = incrementalGaussian.y * incrementalGaussian.y;
    
      vec4 avgValue = vec4(0.0, 0.0, 0.0, 0.0);
      float coefficientSum = 0.0;
    
    // Take the central sample first...
      avgValue += texture2D(texture, vertTexCoord.st) * incrementalGaussian.x;
      coefficientSum += incrementalGaussian.x;
      incrementalGaussian.xy *= incrementalGaussian.yz;
      //if(uv.x>0.3 && uv.x<0.7){
        if(uv.y>mouse.y-rad && uv.y<mouse.y+rad && uv.x>mouse.x-rad && uv.x<mouse.x+rad){
    // Go through the remaining 8 vertical samples (4 on each side of the center)
          for (float i = 1.0; i <= numBlurPixelsPerSide; i++) { 
          avgValue += texture2D(texture, vertTexCoord.st - i * texOffset * 
          blurMultiplyVec) * incrementalGaussian.x;         
          avgValue += texture2D(texture, vertTexCoord.st + i * texOffset * 
          blurMultiplyVec) * incrementalGaussian.x;         
          coefficientSum += 2.0 * incrementalGaussian.x;
          incrementalGaussian.xy *= incrementalGaussian.yz;
          }
       // }
      } 
    gl_FragColor = avgValue / coefficientSum;
    }
    

    processing:

    PShader blur;
    PImage img;
    PGraphics src;
    PGraphics pass1, pass2;
    
    void settings() {
      img=loadImage("img.jpg");
      //size(img.width, img.height, P3D);
      size(500, 500, P3D);
    }
    void setup() {
      blur = loadShader("blur.glsl"); 
      blur.set("blurSize", 100);
      blur.set("sigma", 15.0f);
      blur.set("resolution", float(width), float(height));
      src = createGraphics(width, height, P3D); 
    
      pass1 = createGraphics(width, height, P3D);
      pass1.noSmooth();  
    
      pass2 = createGraphics(width, height, P3D);
      pass2.noSmooth();
    }
    
    void draw() {
      src.beginDraw();
      src.pushMatrix();
      src.translate(width/2, height/2);
      src.rotateY(90);
      src.rotateX(90);
      src.background(0);
      src.box(250);
      src.popMatrix();
      src.endDraw();
      // Applying the blur shader along the vertical direction   
      blur.set("mouse", float(mouseX)/width, float(mouseY)/height); //used to move the mask
      blur.set("horizontalPass", 0);
      blur.set("rad", 0.1); //how much big the blur mask is value 0. to 1.
      pass1.beginDraw();            
      pass1.shader(blur);  
      pass1.image(src, 0, 0);
      pass1.endDraw();
      // Applying the blur shader along the horizontal direction      
      blur.set("horizontalPass", 1);
      pass2.beginDraw();            
      pass2.shader(blur);  
      pass2.image(pass1, 0, 0);
      pass2.endDraw();      
      image(pass2, 0, 0);
    }
    
  • great !

    then I guess we can have radX and radY, or even a way to set the frosted rectangle with parameters like (x, y, w, h)...

    thank !

  • @pietroLama

    would this thing work on P5.js ?

  • edited November 2017

    So this seems to have already a good solution, nice work!

    Sometimes controling the LOD level of the texture is enough, maybe some1 find it interessting i leave it here:

    PShader shdr;
    void setup(){ 
      size(920, 540, P2D);
      noStroke();
      shdr=new PShader(this, 
        new String[]{"#version 150 \n"
        + "in vec2 position;"
        + "out vec2 vuv;"
        + "void main() {"
        // normalize texture coordinate and remap for 0 - 1
        + "vuv =position.xy*.5+.5; "
        + "gl_Position = vec4(position.xy,0.,1.);"
      + "}"
        }, new String[]{"#version 150 \n"
          +" precision highp float;"
          + "out vec4 fragColor;"
          + "in vec2 vuv;"
          + "uniform sampler2D tex;"
          + "uniform float time;"
          + "void main() {"
          // jentell you can try level 3 or higher
          + "float bias = .2;" //.5 in the post below
          // flip and mirror the texture default opengl and also translation to java behavior
          + "vec2 p= vec2(vuv.x,1.-vuv.y);"
          + "vec4 defaultex =texture(tex,p);"
          // rect i eyeball it
          + "if ( abs(p.x-.5)<.275&&abs(p.y-.5)<.275)"
          // khronos.org/registry/OpenGL-Refpages/gl4/html/texture.xhtml
          // 3th parameter controls the lod level
          + "fragColor = texture(tex,p, bias +sin(time)*.5+.5  );"
          + "else fragColor = defaultex;"
         +"}"
        });
        // load texture
        shdr.set("tex",loadImage("https://"+"processing.org/examples/moonwalk.jpg"));
    }
    float a =0.f; 
    void draw() {
      background(0);
      // uniform time 
      shdr.set("time",a++*.1f);
      shader(shdr);
      rect(0, 0, width, height);
      resetShader();
      // draw text rect 
      fill(255, 255, 255,76.5);
      rectMode(CENTER);
      rect(width/2, height/2, 500, 300);
      // draw text 
      fill(0, 0, 0, 225);
      textSize(18);
      textAlign(CENTER, BOTTOM);
      text("test", mouseX, mouseY);
    }
    
  • Interesting. Given that the demo is blurring the whole image and not just the rect area, how would this actually be applied in practice to create the desired "frosted glass" effect? Would you apply the shader just to one image crop area, then layer the rect on top of that?

  • i updated the scetch, replaced the image and made the surface bigger.

    this is what i see, can you confirm ?

    alt text

  • edited November 2017

    So i running into strange bugs, means : - time to stop here.

    I was only a sketchy idea, that needs some time and knowledge to develop it's potential. : )

    Nevermind

    Their is also an lib for filters, found it again, searching trough this forum.

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

    and as i said the current solution, seems to work.

    i was only flyby this forum ....

  • It does look like an interesting approach. The updated version is much closer, although if you zoom in you can see the artifacts spilling out at the edges of the rect.

    My guess would be that the best (and most intuitive) way to do this is to run the shader on a PImage or PGraphics -- not on the whole screen area. But I don't really know anything about shaders. Then a moving glass pane of any geometry is implemented by changing the buffer where you run the shader. But: I don't really know much about shaders.

  • edited November 2017

    thank you for respose. confirmed. An PImage "pre render" via second PGraphics Method at the size of the actual rectangle would be the best.

    I also don't know how to do it :)

Sign In or Register to comment.