OpenGL-边缘检测


12

我想加载任意网格并沿边缘绘制粗黑线以获得类似卡通色调的外观。我设法通过使用模板缓冲区在对象周围绘制了一个黑色的轮廓。您可以在此处查看结果:

在此处输入图片说明

但是缺少的是对象本身的黑线。我考虑过检查法线不连续性:检查相邻像素的法向矢量是否不同于当前像素。如果是,则找到边缘。不幸的是,无论是在OpenGL中还是在GLSL顶点/片段着色器中,我都不知道如何实现这种方法。

对于这种方法或其他有关边缘检测的帮助,我将非常高兴。

编辑:我不对我的网格使用任何纹理。

太精确了,我想创建一个看起来像这样的CAD / CAM解决方案(摘自Top Solid https://www.youtube.com/watch?v=-qTJZtYUDB4):

在此处输入图片说明


我相信您需要更详细地定义“边缘”。它与简单的线框有何不同?在cifz答案中,对屏幕空间后处理有很好的描述,但是根据您的问题很难确定它是否适用。
安德烈亚斯

好吧,用边缘表示形成实体的“折痕”和“凸脊”。线框将显示所有三角形的面,这不是我想要的。
enne87 '16

好的,正是我要的:-)我将从那些折痕和山脊生成线框。棘手的部分仍然是确定折痕/隆起是什么。您是否知道该怎么做?
安德里亚斯

1
cad程序通常不会使用着色器执行此操作。取而代之的是,他们从模型中知道了硬边,并在该信息的网格上方绘制了一条线。
joojaa

joojaa您是否有关于此技术的更多信息?如果是双曲面自由曲面,该怎么办?我也不确定当圆锥或圆柱体被自由形式的东西切割/修剪时会发生什么。stackoverflow.com/questions/43795262/...
杜尚Bosnjak 'pailhead'

Answers:


18

通常,边缘检测归结为检测具有高梯度值的图像区域。

在我们的例子中,我们可以粗略地看到梯度作为衍生图像功能的,因此梯度的大小给你上的信息有多少图像局部改变(在相邻像素/纹理像素的问候)。
现在,正如您所说的,边缘是不连续的指示,因此,既然我们定义了渐变,就很清楚此信息就是我们所需要的。一旦找到图像的梯度,只需将阈值应用于图像即可获得二进制值的边缘/非边缘。

您如何找到这个梯度确实是您要问的,但我还没有回答:)

有很多方法!这里一对:)

内置着色器功能

hlsl和glsl都提供派生功能。在GLSL中,您可以使用dFdx和dFdy分别为您提供x和y方向上的渐变信息。通常,这些功能以2x2片段的块进行评估。
除非您对单个方向感兴趣,否则是一种紧凑结果的好方法,该结果表明区域中的梯度的宽度fwidth,除dFdy和dFdy的绝对值之和外,其他什么都没有。
您可能会对整个图像的边缘感兴趣,而不是对特定通道感兴趣,因此您可能希望将图像功能转换为亮度。考虑到这一点,当涉及边缘检测时,着色器可能包含以下内容:

  float luminance = dot(yourFinalColour,vec3(0.2126, 0.7152, 0.0722));
  float gradient = fwidth(luminance );
  float isEdge = gradient > threshold;

高阈值时,您会发现较粗糙的边缘,相反,低阈值时,您可能会发现一些虚假边缘。您必须尝试找到最适合您需求的阈值。

这些功能起作用的原因值得一提,但我现在没有时间,我可能会在以后更新此答案:)

屏幕空间后处理

您可能会做得更好,现在图像处理中的边缘检测领域非常广阔。我可以根据您的需求引用数十种检测边缘检测的好方法,但是现在让我们保持简单,如果您有兴趣,我可以引用更多的选择!

因此,该想法与上面的想法类似,不同之处在于,您可以查看更广泛的邻域,并根据需要对加权样本使用一组权重。通常,您使用内核对图像进行卷积,从而为您提供良好的渐变信息。
一个非常常见的选择是Sobel内核

                                   在此处输入图片说明

分别为您提供x和y方向的渐变:

                                  很久以前,我从一个旧的pdf文件中写道。

G[R一种d一世ËñŤ中号一种Gñ一世ŤüdË=G[R一种d一世ËñŤX2+G[R一种d一世ËñŤÿ2

然后,您可以像我上面提到的一样阈值。

如您所见,该内核将更多的权重分配给中央像素,因此有效地计算了梯度+一点平滑度,这在传统上是有帮助的(通常对图像进行高斯模糊处理以消除小边缘)。

上面的方法工作得很好,但是如果您不喜欢平滑,则可以使用Prewitt内核:

                                                   在此处输入图片说明

(请注意,我很着急,很快就会写出正确格式的文本而不是图像!)

确实有更多的内核和技术可以以图像处理y的方式而不是实时图形来查找边缘检测,因此我排除了更多复杂的方法(双关语无意),因为使用dFdx / y函数可能就很好了。


很好的解释cifz,但是如果在特定情况下看不到渐变怎么办?例如,立方体的背面没有光源,因此看不到渐变。然后,您描述的这种基于图像的边缘检测过程将无法正常工作,对吗?
enne87 '16

如果使用延迟渲染器,则可以执行此操作,但是可以在普通缓冲区上执行一次,也可以再次执行该操作。如果您具有预通过,则可以将算法应用于深度。仍然您是对的,屏幕空间方法可能并非在所有情况下都是理想的:)哪种解决方案可行,很大程度上取决于您为此效果准备的预算以及场景的复杂程度。
cifz

如果这对于您的游戏而言确实很重要,则一个非常简单但可能会增加负担的方法是为每个顶点(加载时离线)在相邻法线上计算一个梯度,并将此因子作为附加顶点属性传递。
cifz

再次挂起cifz,为您提供帮助。好吧,我想要实现的是开发一个类似于以下内容的CAD / CAM解决方案:youtube.com/watch?v=-qTJZtYUDB4而且我真的很想知道他们是如何管理它来呈现所有黑色边缘,折痕的和山脊。cifz,您认为作为图形编程专家,他们通过使用您的一种屏幕空间方法实现了这种外观吗?还是通过计算相邻法线的梯度?我真的很想知道如何做到这一点。再次感谢!
enne87 '16

1
无需支付任何费用,只需开始在线阅读一些教程,购买一些书籍并努力学习即可:)最后,这将是非常有益的!
cifz

3

以防万一其他人也需要检测边缘: 是一篇不错的文章,文章介绍了如何显示线框,并且本文介绍了如何仅显示边缘。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.