我想在具有景深效果的3D画布(处理中)上显示数千个点。更具体地说,我想使用z缓冲区(深度缓冲)来根据距离相机的距离来调整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.glsl 和 pointvert.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)
答案 0 :(得分:3)
您的任务中的主要问题是,高斯模糊着色器通常在后期处理中运行。 通常,在绘制完所有几何体后,它将应用于整个视口。 高斯模糊着色器采用帧缓冲区(纹理)及其邻居的片段,并通过高斯函数将颜色混合并将新颜色存储到最终帧缓冲区。为此,必须在之前完成整个场景(所有点)的绘制。
但你可以做点别的事。写一个着色器,在其中心绘制完全不透明的点,在其外边界处完全透明。
在顶点着色器中,您必须将视点空间顶点坐标和点的视图空间中心传递给片段着色器:
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;
}
在片段着色器中,您必须计算从片段到点中心的距离。要做到这一点,你必须知道点的大小。距离可用于计算opacity
,opacity
是该点的新Alpha通道。
添加统一变量strokeWeight
并在程序中设置制服。请注意,因为边界处的点是透明的,所以看起来更小。我建议增加点的大小:
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个后期处理过程中应用。一个通道沿水平面模糊,另一个通道沿垂直轴模糊。
为此,您必须执行以下步骤:
代码清单,使用您问题中的着色器,可能如下所示:
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)
查看预览:
对于结合了2种解决方案的方法,请参阅上一个问题的答案: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遍景深,高斯模糊着色器看起来像这样:
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.渲染&#34;深度&#34;到另一个图像缓冲区 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()
查看预览:
答案 1 :(得分: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的演示中看到的想法,然而他们可能以不同的方式实现它我不知道