How to combine a point shader with a blur shader?

edited June 6 in GLSL / Shaders

I would like to display thousands of points on a 3D canvas with a Depth of Field effect. More specifically, I would like to use a z-buffer (depth buffering) to adjust the level of blur of a point based on its distance from the camera.

So far, I could come up with the following point shader:

pointfrag.glsl

#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif

varying vec4 vertColor;
uniform float maxDepth;

void main() {

  float depth = gl_FragCoord.z / gl_FragCoord.w;
  gl_FragColor = vec4(vec3(vertColor - depth/maxDepth), 1) ;

}

pointvert.glsl

uniform mat4 projection;
uniform mat4 modelview;

attribute vec4 position;
attribute vec4 color;
attribute vec2 offset;


varying vec4 vertColor;
varying vec4 vertTexCoord;

void main() {
  vec4 pos = modelview * position;
  vec4 clip = projection * pos;

  gl_Position = clip + projection * vec4(offset, 0, 0);

  vertColor = color;
}

I also have a blur shader (originally from the PostFX library):

blurfrag.glsl

#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;

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 blurMultiplyVec = 0 < horizontalPass ? vec2(1.0, 0.0) : vec2(0.0, 1.0);

  // 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;

  // 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);
}

Question:

  • How can I combine the blur fragment shader with the point fragment shader ?

Ideally I'd like to have one single fragment shader that computes the level of blur based on the z-coordinate of a point. Is that even possible ?

@noahbuddy @kosowski I would be really grateful if you could help me.


An example sketch displaying points using the pointfrag.glsl and pointvert.glsl shaders above:

sketch.pde

import peasy.*;
import peasy.org.apache.commons.math.*;
import peasy.org.apache.commons.math.geometry.*;

PeasyCam cam;
PShader pointShader;
PShape shp;
ArrayList<PVector> vectors = new ArrayList<PVector>();

void setup() {
  size(900, 900, P3D);
  frameRate(1000);
  smooth(8);

  cam = new PeasyCam(this, 500);
  cam.setMaximumDistance(width);
  perspective(60 * DEG_TO_RAD, width/float(height), 2, 6000);

  double d = cam.getDistance()*3;

  pointShader = loadShader("pointfrag.glsl", "pointvert.glsl");
  pointShader.set("maxDepth", (float) d);


  for (int i = 0; i < 5000; i++) {
    vectors.add(new PVector(random(width), random(width), random(width)));
  }

  shader(pointShader, POINTS);
  strokeWeight(2);
  stroke(255);

  shp = createShape();
  shp.beginShape(POINTS);
  shp.translate(-width/2, -width/2, -width/2);  
  for (PVector v: vectors) {
    shp.vertex(v.x, v.y, v.z);
  }
  shp.endShape();

}

void draw(){
  background(0);
  shape(shp, 0, 0);

  cam.rotateY(.0001);
  cam.rotateX(.00005);

  println(frameRate);
}

https://i.imgur.com/mf5dVRn.mp4

Tagged:

Answers

  • There is a new forum

    Better ask there

    See first sticky post

  • Line 16 of pointvert zeroes the z of the position. If you're relying on that in the blur shader then that'll need fixing.

  • I am afraid that I do not know how to do this without an offscreen buffer.

    Not sure if this is exactly what you were after but is a proof of concept I suppose.

    It was a challenge tuning in the depth value.

    There should be another way... :-?

    Most likely a step in the matrix math that I am missing.

    Fragment shader:

    varying vec4 vertColor;
    
    void main() {
        gl_FragColor = vertColor;
    }
    

    Vertex shader:

    uniform mat4 projection;
    uniform mat4 modelview;
    
    attribute vec4 position;
    attribute vec4 color;
    attribute vec2 offset;
    
    varying vec4 vertColor;
    varying vec4 vertTexCoord;
    
    uniform int getZ;
    uniform float maxDepth;
    
    void main() {
    
      vec4 pos = modelview * position;
      vec4 clip = projection * pos;
      vec4 posit = clip + projection * vec4(offset, 0., 0.);
    
      float depth = -vertTexCoord.z * 8.0; // Not fond of this magic multiplier...
      vec4 deep = vec4((depth/maxDepth).xxx, 1.0);
    
      vertColor = getZ != 0 ? deep : color; // Choose colored dots or depth value
    
      gl_Position = posit;
    }
    

    I wound up using the modified blur shader from your other post. https://forum.processing.org/two/discussion/27807/how-to-combine-a-z-buffer-with-a-blur-fragment-shader

    Blur shader:

    #define PROCESSING_TEXTURE_SHADER
    
    uniform sampler2D texture;
    uniform sampler2D depthBuffer;
    
    // The inverse of the texture dimensions along X and Y
    uniform vec2 texOffset;
    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() {  
      vec4 effx = texture2D(depthBuffer, vertTexCoord.st);
      //float numBlurPixelsPerSide = float(blurSize / 2); 
      int numBlurPixelsPerSide = int((blurSize - effx.x*blurSize) / 2); // <--    Using depth info here, farther away, more blur
    
      vec2 blurMultiplyVec = 0 < horizontalPass ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
    
      // 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;
    
      // 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);
    }
    

    Sketch:

    import peasy.*;
    import peasy.org.apache.commons.math.*;
    import peasy.org.apache.commons.math.geometry.*;
    
    // Multipass. No, not that kind. Shaders. https://en.wikipedia.org/wiki/Deferred_shading
    
    PGraphics zbuff, starbuff;
    
    PeasyCam rendercam, bufcam;
    PShader pointShader, blurShader;
    PShape shp;
    ArrayList<PVector> vectors = new ArrayList<PVector>();
    
    void setup() {
      size(900, 900, P3D);
      frameRate(120);
      //smooth(8);
    
      zbuff = createGraphics(width, height, P3D);
      starbuff = createGraphics(width, height, P3D);
    
      // Camera settings need to match
      bufcam = new PeasyCam(this, zbuff, 500);
      bufcam.setMaximumDistance(width);
      zbuff.perspective(60 * DEG_TO_RAD, width/float(height), 2, 6000);
    
      rendercam = new PeasyCam(this, starbuff, 500);
      rendercam.setMaximumDistance(width);
      starbuff.perspective(60 * DEG_TO_RAD, width/float(height), 2, 6000);
    
      double d = bufcam.getDistance()*3;
    
      // Shader setup, adjust as needed
      blurShader = loadShader("blurfrag.glsl");
      blurShader.set("sigma", 1.25);
      blurShader.set("blurSize", 9);
    
      pointShader = loadShader("pointfrag.glsl", "pointvert.glsl");
      pointShader.set("maxDepth", (float) d);
    
      for (int i = 0; i < 5000; i++) {
        vectors.add(new PVector(random(width), random(width), random(width)));
      }
    
      // Star depth
      zbuff.shader(pointShader, POINTS);
      zbuff.strokeWeight(2);
      zbuff.stroke(255);
    
      // Stars
      starbuff.shader(pointShader, POINTS);
      starbuff.strokeWeight(2);
      starbuff.stroke(255);
    
      // We can use the same shape object
      shp = createShape();
      shp.setStroke(255);
      shp.beginShape(POINTS);
      shp.translate(-width/2, -width/2, -width/2);
      for (PVector v: vectors) {
        shp.stroke(255);
        // We can color the stars too
        // The selection of depth or color is in the shader
        //shp.stroke((int)random(255),(int)random(255),(int)random(255));
        shp.vertex(v.x, v.y, v.z);
      }
      shp.endShape();
    
      shader(blurShader);
    }
    
    void draw(){
      zbuff.beginDraw();
      zbuff.background(0);
      pointShader.set("getZ", 1);
      zbuff.shape(shp, 0, 0);
      zbuff.endDraw();
    
      starbuff.beginDraw();
      starbuff.background(0);
      pointShader.set("getZ", 0);
      starbuff.shape(shp, 0, 0);
      starbuff.endDraw();
    
      // pass in the z depth info
      blurShader.set("depthBuffer", zbuff);
    
      // and draw the star field
      blurShader.set("horizontalPass", 0);
      image(starbuff, 0, 0);
      blurShader.set("horizontalPass", 1);
      filter(blurShader);
    
      // Match render to zbuff positions
      rendercam.rotateY(.0001);
      rendercam.rotateX(.00005);
      bufcam.rotateY(.0001);
      bufcam.rotateX(.00005);
    
      //println(frameRate);
    }
    

    Seems the next thing I need to do is sign up at the new forum...

  • edited June 8

    Thanks a bunch for your answer @noahbuddy. When running your script I have a compiler error saying:

    Swizzle of non-vector primitive float: Types in conditional operator do not match

    Don't know how to fix that, are you sure the vertex shader is correct ?

  • Sorry, I was using the 'swizzling' trick on a float (only available with OpenGL 4.2 or newer)

    If your GL compiler is new enough, you may have to specify "#version 420".

    Try replacing this line in the vertex shader...

    vec4 deep = vec4((depth/maxDepth).xxx, 1.0);
    

    with:

    depth /= maxDepth;
    vec4 deep = vec4(depth, depth, depth, 1.0);
    
Sign In or Register to comment.