【问题标题】:Depth of Field: combining a point shader with a blur shader (Processing 3)景深:结合点着色器和模糊着色器(处理 3)
【发布时间】:2018-11-14 06:48:09
【问题描述】:

我想在具有景深效果的 3D 画布(正在处理中)上显示数千个点。更具体地说,我想使用 z-buffer(深度缓冲)根据 point 与相机的距离来调整其模糊程度。

到目前为止,我可以想出以下点着色器:

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

我还有一个模糊着色器(最初来自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);
}

问题

  • 如何将模糊片段着色器与点片段着色器结合使用?

理想情况下,我希望有一个片段着色器,它可以根据一个点的 z 坐标计算模糊程度。这有可能吗?

任何帮助将不胜感激。


使用上述 pointfrag.glslpointvert.glsl 着色器显示点的示例草图:

sketch.pde(需要 Python 模式 + PeasyCam 库)

add_library('peasycam')
liste = []

def setup():
    global pointShader, cam
    size(900, 900, P3D)
    frameRate(1000)
    smooth(8)

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

    pointShader = loadShader("pointfrag.glsl", "pointvert.glsl")
    pointShader.set("maxDepth", cam.getDistance()*3)

    for e in range(3000): liste.append(PVector(random(width), random(width), random(width)))

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

def draw():

    background(0)
    translate(-width/2, -width/2, -width/2)    
    for e in liste:
        point(e.x, e.y, e.z)

    cam.rotateY(.0002)
    cam.rotateX(.0001)

【问题讨论】:

    标签: processing shader fragment-shader depth-buffer gaussianblur


    【解决方案1】:

    您的任务中的主要问题是,高斯模糊着色器通常在后期处理中运行。 通常,它会在绘制完所有几何图形后应用于整个视口。 高斯模糊着色器获取帧缓冲区(纹理)及其邻居的片段,并通过高斯函数混合颜色并将新颜色存储到最终帧缓冲区。为此,必须先完成整个场景(所有点)的绘制。


    但是你可以做其他事情。编写一个着色器,在其中心绘制完全不透明的点,在其外部边界绘制完全透明的点。

    在顶点着色器中,您必须将视图空间顶点坐标和点的视图空间中心传递给片段着色器:

    pointvert.glsl

    uniform mat4 projection;
    uniform mat4 modelview;
    
    attribute vec4 position;
    attribute vec4 color;
    attribute vec2 offset;
    
    varying vec3 vCenter;
    varying vec3 vPos;
    varying vec4 vColor;
    
    
    void main() {
    
        vec4 center = modelview * position;
        vec4 pos    = center + vec4(offset, 0, 0); 
    
        gl_Position = projection * pos;
    
        vCenter = center.xyz;
        vPos    = pos.xyz;
        vColor  = color;
    }
    

    在片段着色器中,您必须计算从片段到点中心的距离。为此,您必须知道点的大小。距离可以用来计算opacityopacity是该点的新alpha通道。

    添加一个uniform变量strokeWeight并在程序中设置uniform。请注意,因为点在其边界处是透明的,所以看起来更小。我建议增加点的大小:

    pointShader.set("strokeWeight", 6.0)
    
    .....
    
    strokeWeight(6)
    

    pointfrag.glsl

    #ifdef GL_ES
    precision mediump float;
    precision mediump int;
    #endif
    
    varying vec3 vCenter;
    varying vec3 vPos;
    varying vec4 vColor;
    
    uniform float strokeWeight;
    uniform float maxDepth;
    uniform float focus;
    
    void main() {
    
        float depth = clamp(abs(vCenter.z)/maxDepth, 0.0, 1.0);
        float blur  = abs(focus-depth);
    
        float dist_to_center = length(vPos-vCenter)*2.0/strokeWeight;
        float threshold      = max(0.0, blur);
        float opacity        = 1.0 - smoothstep(threshold/2.0, 1.0-threshold/2.0, dist_to_center); 
    
        gl_FragColor = vec4(vColor.rgb, opacity);
    }
    

    您正在绘制部分透明的对象。要获得适当的混合效果,您应该按 z 坐标升序对点进行排序:

    liste = []
    listZ = []
    
    .....
    
    for e in range(3000): listZ.append(random(width))
    listZ.sort()
    for z in listZ: liste.append(PVector(random(width), random(width), z))
    

    完整的示例代码可能如下所示:

    add_library('peasycam')
    liste = []
    listZ = []
    
    def setup():
        global pointShader, cam
        size(900, 900, P3D)
        frameRate(1000)
        smooth(8)
    
        cam = PeasyCam(this, 500)
        cam.setMaximumDistance(width)
        perspective(60 * DEG_TO_RAD, width/float(height), 2, 6000)
    
        pointShader = loadShader("pointfrag.glsl", "pointvert.glsl")
        pointShader.set("maxDepth", 900.0)
        pointShader.set("strokeWeight", 6.0)
    
        for e in range(3000): listZ.append(random(width))
        listZ.sort()
        for z in listZ: liste.append(PVector(random(width), random(width), z))
    
        shader(pointShader, POINTS)
        strokeWeight(6)
        stroke(255)
    
    def draw():
    
        background(0)
        blendMode(BLEND)
        translate(-width/2, -width/2, -width/2) 
        pointShader.set("focus", map(mouseX, 0, width, 0.2, 1.0))   
        for e in liste:
            point(e.x, e.y, e.z)
    
        cam.rotateY(.0002)
        cam.rotateX(.0001)
    

    查看预览:


    当然也可以使用高斯模糊着色器。

    您在问题中提出的高斯模糊着色器是一个 2 遍后期处理模糊着色器。这意味着它必须在整个视口上的 2 个后期处理过程中应用。一个通道沿水平方向模糊,另一个通道沿垂直轴方向模糊。

    为此,您必须执行以下步骤:

    1. 将场景渲染到缓冲区(图像)
    2. 对图像应用垂直高斯模糊通道并将结果渲染到新的图像缓冲区
    3. 将水平高斯模糊通道应用于垂直高斯模糊通道的结果

    完全使用您问题中的着色器的代码清单可能如下所示:

    add_library('peasycam')
    liste = []
    
    def setup():
        global pointShader, blurShader, cam, bufScene, bufBlurV, bufBlurH
        size(900, 900, P3D)
        frameRate(1000)
    
        cam = PeasyCam(this, 900)
        cam.setMaximumDistance(width)
        perspective(60 * DEG_TO_RAD, width/float(height), 2, 6000)
    
        pointShader = loadShader("pointfrag.glsl", "pointvert.glsl")
        pointShader.set("maxDepth", cam.getDistance()*3)
    
        blurShader = loadShader("blurfrag.glsl")
        blurShader.set("texOffset", [1.0/width, 1.0/height])
        blurShader.set("blurSize", 40);
        blurShader.set("sigma", 5.0);
    
        bufScene, bufBlurV, bufBlurH  = [createGraphics(width, height, P3D) for e in range(3)]
        bufScene.smooth(8), bufBlurV.shader(blurShader), bufBlurH.shader(blurShader)
    
        for e in range(5000): liste.append(PVector(random(width), random(width), random(width)))
    
    def drawScene(pg):
        pg.beginDraw()
        pg.background(0)
    
        shader(pointShader, POINTS)
        strokeWeight(4)
        stroke(255)
    
        pushMatrix()
        translate(-width/2, -width/2, 0.0)
        for e in liste:
            point(e.x, e.y, e.z)
        popMatrix()
    
        pg.endDraw()
        cam.getState().apply(pg)
    
    def draw():
        drawScene(bufScene) 
    
        bufBlurV.beginDraw()
        blurShader.set("horizontalPass", 0);
        bufBlurV.image(bufScene, 0, 0)
        bufBlurV.endDraw()
    
        bufBlurH.beginDraw()
        blurShader.set("horizontalPass", 1);
        bufBlurH.image(bufBlurV, 0, 0)
        bufBlurH.endDraw()
    
        cam.beginHUD()
        image(bufBlurH, 0, 0)
        cam.endHUD()
    
        cam.rotateY(.0002)
        cam.rotateX(.0001)
    

    查看预览:


    有关结合这两种解决方案的方法,另请参阅您之前问题的答案:Depth of Field shader for points/strokes in Processing

    创建深度着色器:

    depth_vert.glsl

    uniform mat4 projection;
    uniform mat4 modelview;
    
    attribute vec4 position;
    attribute vec2 offset;
    
    varying vec3 vCenter;
    
    void main() {
        vec4 center = modelview * position;
        gl_Position = projection * (center + vec4(offset, 0, 0));
        vCenter = center.xyz;
    }
    

    depth_frag.glsl

    #ifdef GL_ES
    precision mediump float;
    precision mediump int;
    #endif
    
    varying vec3 vCenter;
    
    uniform float maxDepth;
    
    void main() {
        float depth = clamp(abs(vCenter.z)/maxDepth, 0.0, 1.0);
        gl_FragColor = vec4(vec3(depth), 1.0);
    }
    

    还需要一个点着色器来绘制点:

    point_vert.glsl

    uniform mat4 projection;
    uniform mat4 modelview;
    
    attribute vec4 position;
    attribute vec4 color;
    attribute vec2 offset;
    
    varying vec4 vColor;
    
    void main() {
        vec4 pos = modelview * position;
        gl_Position = projection * (pos + vec4(offset, 0, 0));
        vColor = color;
    }
    

    point_frag.glsl

    #ifdef GL_ES
    precision mediump float;
    precision mediump int;
    #endif
    
    varying vec4 vColor;
    
    void main() {
        gl_FragColor = vec4(vColor.rgb, 1.0);
    }
    

    2 pass 景深,高斯模糊着色器如下所示:

    blurfrag.glsl

    uniform sampler2D tDepth;
    
    uniform float focus;
    
    const float pi = 3.14159265;
    
    void main()
    {  
        vec2 vUv = vertTexCoord.st;
        vec4 depth = texture2D( tDepth, vUv );
        float dofblur = abs( depth.x - focus );
    
        float numBlurPixelsPerSide = float(blurSize / 2) * dofblur; 
        float dofSigma = sigma; 
    
        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) * dofSigma);
        incrementalGaussian.y = exp(-0.5 / (dofSigma * dofSigma));
        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);
    }
    

    在程序中,您必须完成 4 个阶段: 1. 将场景渲染到缓冲区(图像) 2.将“深度”渲染到另一个图像缓冲区 3. 对图像应用垂直高斯模糊通道并将结果渲染到新的图像缓冲区 4. 将水平高斯模糊通道应用于垂直高斯模糊通道的结果

    add_library('peasycam')
    liste = []
    
    def setup():
        global depthShader, point_shader, blurShader, cam, bufDepth, bufScene, bufBlurV, bufBlurH
        size(900, 900, P3D)
        frameRate(1000)
    
        cam = PeasyCam(this, 900)
        cam.setMaximumDistance(width)
        perspective(60 * DEG_TO_RAD, width/float(height), 2, 6000)
    
        point_shader = loadShader("point_frag.glsl","point_vert.glsl")
        depthShader = loadShader("depth_frag.glsl","depth_vert.glsl")
        blurShader = loadShader("blurfrag.glsl")
    
        bufDepth, bufScene, bufBlurV, bufBlurH = [createGraphics(width, height, P3D) for e in range(4)]
        bufDepth.smooth(8)
        bufScene.smooth(8)
        bufBlurV.shader(blurShader)
        bufBlurH.shader(blurShader)
    
        depthShader.set("maxDepth", 900.0)
    
        blurShader.set("tDepth", bufScene)
        blurShader.set("texOffset", [1.0/width, 1.0/height])
        blurShader.set("blurSize", 40)
        blurShader.set("sigma", 5.0)
    
        for e in range(3000): liste.append(PVector(random(width), random(width), random(width)))
    
    def drawScene(pg,sh):
        pg.beginDraw()
        pg.background(0)
    
        shader(sh, POINTS)
        strokeWeight(6)
        stroke(255)
    
        pushMatrix()
        translate(-width/2, -width/2, 0.0)
        for e in liste:
            point(e.x, e.y, e.z)
        popMatrix()
    
        pg.endDraw()
        cam.getState().apply(pg)
    
    def draw():
        drawScene(bufDepth, point_shader) 
        drawScene(bufScene, depthShader)
    
        blurShader.set("focus", map(mouseX, 0, width, .1, 1))
    
        bufBlurV.beginDraw()
        blurShader.set("horizontalPass", 0);
        bufBlurV.image(bufScene, 0, 0)
        bufBlurV.endDraw()
    
        bufBlurH.beginDraw()
        blurShader.set("horizontalPass", 1);
        bufBlurH.image(bufBlurV, 0, 0)
        bufBlurH.endDraw()
    
        cam.beginHUD()
        image(bufBlurH, 0, 0)
        cam.endHUD()
    

    查看预览:

    【讨论】:

      【解决方案2】:

      我一直愿意尝试这个似乎涵盖了您的用例一段时间的简单技巧 :) 我在 Unity 中实现,但逻辑非常简单,应该很容易适应。

      如果您没有点以外的其他图元,则可以轻松地将模糊预卷积为纹理。我在 Ps 中做了我的,但我确信有更好更准确的方法。照片噪点不是强制性的

      从那里根据模糊量计算纹理的偏移量(或更准确地说,步骤 n 和 n-1 以及使用提醒的 lerp)。 我统一映射到视空间 Z 位置和一个简单的镜像线性衰减(我不确定这里的光学实际逻辑是什么)。 (_ProjectionParams.w 是逆远平面)

      half focus = -UnityObjectToViewPos( v.vertex ).z;
      focus = abs(focus - _FocalPlane);
      focus *= _Dof * _ProjectionParams.w;
      

      编辑:我应该提到我在 this company 的演示中看到了这个想法,但是他们可能会以不同的方式实现它,我不知道

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-05-30
        • 2013-09-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多