我想从纹理生成平滑(宽松和连续)轮廓,如下所示:
我目前运行的是一个执行非常基本边缘检测的着色器,最终得到以下结果:
void main(void) {
vec2 texCoord = vec2(((vProjectedCoords.x / vProjectedCoords.w) + 1.0 ) / 2.0,
((vProjectedCoords.y / vProjectedCoords.w) + 1.0 ) / 2.0 );
float borderWidth = uWidth; // in px
float step_u = borderWidth * 1.0 / uCanvasWidth;
float step_v = borderWidth * 1.0 / uCanvasHeight;
vec4 centerPixel = texture2D(uTextureFilled, texCoord);
vec4 rightPixel = texture2D(uTextureFilled, texCoord + vec2(step_u, 0.0));
vec4 bottomPixel = texture2D(uTextureFilled, texCoord + vec2(0.0, step_v));
// now manually compute the derivatives
float _dFdX = length(rightPixel - centerPixel) / step_u;
float _dFdY = length(bottomPixel - centerPixel) / step_v;
gl_FragColor.r = max(max(centerPixel.r, rightPixel.r), bottomPixel.r);
gl_FragColor.g = max(max(centerPixel.g, rightPixel.g), bottomPixel.g);
gl_FragColor.b = max(max(centerPixel.b, rightPixel.b), bottomPixel.b);
gl_FragColor.a = max(_dFdX, _dFdY);
return;
显然生成的轮廓不干净,但即使采用适当的索贝尔过滤也没有提高结果。
我希望获得几乎像素完美的轮廓,如果可能的话,理想情况下也会忽略输入纹理中的噪点。
谢谢!
编辑:可能需要添加一些平滑的步骤,例如: https://www.shadertoy.com/view/4ssSRl
答案 0 :(得分:2)
根据您的示例图像,您似乎正在处理具有固定背景颜色的图像。这可以简化工作。我不确定第二张图像与第一张图像的关系。它是结果的放大部分吗?
如果我理解正确,你所描述的可以通过“渗出”纹理来实现。以下是步骤:
添加一个预过滤器,用于检测小于N像素的非背景色区域,并将其从源图像中删除。每个纹素应该:
这种方法和其他方法可能有不同的方法来消除可能适用于您的情况的噪音。另见https://computergraphics.stackexchange.com/questions/3904/is-it-better-to-blur-the-input-or-output-of-an-edge-detection-shader-for-noise-r。
您从ShaderToy引用的平滑步骤直接使用不适用于您的用例的程序线方程。
该想法是生成“边缘距离”的地图,其包含对于每个纹素,到最近的非背景纹理像素的距离。应用以下过滤器以从源图像生成另一个纹理。对于每个像素:
最终处理步骤将适用于两种纹理以添加轮廓线。对于每个像素:
smoothstep
生成平滑轮廓,而不是使用硬边框切断。整个过程有许多可能的优化领域。其中,计算着色器和共享内存的智能使用可以显着减少内存带宽的使用。