如何将粗的2D线渲染成多边形?

26

我有一个由一系列2D点组成的路径。我想将这些转换成三角形带,以便使用指定的粗细(和其他相关内容)呈现纹理线条。因此,2D点列表需要变成指定多边形轮廓的顶点列表,以渲染出该线条。问题在于如何处理拐角连接、斜接、端点等。生成的多边形需要“完美”,即没有重叠、清晰的连接等,以便可以可行地拉伸或进行其他操作。

是否有任何简单的资源可以提供算法见解、代码或有关高效执行此操作的更多信息?

我绝对不想要完整的2D向量库(cairo,antigrain,OpenVG等),其中包含曲线、弧线、虚线和所有功能。我一直在多个OpenVG实现和其他东西的源代码树中挖掘来寻找一些见解,但这一切都非常复杂。

我肯定愿意自己编写代码,但存在许多退化案例(小段+厚度+尖角),这些情况会产生各种连接问题。即使只有一点点帮助也可以节省我数小时的尝试来解决它们。

编辑:这是其中一个导致丑陋结果的退化案例,如果仅从顶点到顶点进行操作。红色是原始路径。橙色块是在每个段上对齐并居中绘制的指定宽度的矩形。


我也一直想要这个。在原型中,我使用简单的方框在点之间弄了个折中,但最终还是得修复它。希望你的问题引来正确的回答。无论如何,请回复我们并告诉我们最终你做了什么。 - Adam Davis
根据您的图片,您并没有将角度二等分,而是垂直于您的线条。 - Neil N
是的,我知道。那只是在应用任何联接算法之前的预处理阶段。我只是举例说明一个问题情况,并不是当你实际应用某些东西时会发生什么。 - Patrick Hogan
9个回答

5

虽然这个链接可能回答了问题,但最好在此处包含答案的基本部分并提供参考链接。如果链接页面更改,仅有链接的答案可能会失效。 - AstroCB
1
@AstroCB:说得对,但是OP提出的问题实际上非常困难。事实上,这几乎是一个开放性研究问题。没有好的答案适合在Stack Overflow中。提供一个解决该问题的相当不错的资源链接是非常相关和有帮助的回答,在这种情况下,试图在此概括方法并没有太大意义,而且我也没有时间。"-1"表示“答案没有用”。我不明白指向一个好的资源怎么会没用。它并不完美,但确实有用。 - Boris Dalstein
记录一下,我没有给它点踩(见这里),但我想指出这个问题,它阐述了Stack Exchange针对这类回答的政策。这条评论是基于我在审查队列中采取的行动而自动生成的,这篇帖子在有人(可能是同一个给它点踩的人)将其标记为“低质量”后进入了审查队列。 - AstroCB
@AstroCB 好的,谢谢你的澄清 :) 请注意,与“链接答案”指南中给出的答案相比,我的答案仍然要长得多,并提供了一些上下文。无论如何,指南只是指南,而不是规则。 - Boris Dalstein
我已经花了一周的时间在线渲染方面工作,遇到了与OP相同的问题。虽然链接的文章/项目(花瓶渲染器)非常好,但在紧密的角落仍然存在过度绘制的问题。到目前为止,我还没有找到解决这个问题的方法。 - SleepProgger

5

嗯,好吧 - 我曾试图自己解决这个问题。我浪费了两个月的时间来解决零透支问题。就像你已经发现的那样,你无法处理所有退化情况并同时避免零透支。

不过,你可以使用混合方法:

写一个例程来检查是否可以使用简单几何体构造连接而没有问题。为此,您必须检查连接角度、线宽和连接线段的长度(短于其宽度的线段很麻烦)。通过一些启发式方法,您应该能够解决所有琐碎的情况。

我不知道您的平均线数据是什么样子的,但在我的情况下,超过90%的宽线条没有退化情况。

对于所有其他行:

您很可能已经发现,如果容忍零透支,则产生几何体要容易得多。这样做,并让多边形CSG算法和tesselation算法完成艰难的工作。

我已经评估了大多数可用的tesselation包,并最终选择了GLU tesselator。它速度快,稳定,从未崩溃(与大多数其他算法不同)。它是免费的,许可证允许我将其包含在商业程序中。tesselation的质量和速度都还可以。您将无法获得delaunay三角剖分质量,但由于您只需要渲染三角形,因此这不是问题。

由于我不喜欢tesselator API,所以我从免费的SGI OpenGL参考实现中提取了tesselation代码,重写了整个前端并添加了内存池来减少分配数量。这花了两天时间,但很值得(性能提高了5倍)。顺便说一句,解决方案最终出现在商业OpenVG实现中:-)

如果在PC上使用OpenGL进行呈现,则可能希望将tesselation / CSG工作从CPU移动到GPU,并使用stencil-buffer或z-buffer技巧来消除透支。那要容易得多,而且甚至可能比CPU tesselation更快。


什么是PITA?当线宽大于线段长度时,你需要做什么? - moka
@moka:PITA = 麻烦的事 - aehlke
1
这些退化情况具体是什么?我不明白问题在哪里? - paulm

2
我脑海中想到一个简单的方法。
将每个2D顶点的角度二分,这将创建一个漂亮的斜切线。然后沿着该线向内外移动你的 "厚度"(或者是厚度除以二?),你现在就有了内部和外部多边形点。移动到下一个点,重复同样的过程,一路建立你的新多边形点。然后应用三角剖分来获取渲染所需的顶点。

我已经解决了那个部分的问题。 :) 问题在于有一些情况下,这些顶点会生成重叠的三角形。基本上,我需要消除那些最终会导致奇怪伪影的顶点。 - Patrick Hogan
我认为你没有理解二分法部分。 - Neil N
我明白,但它依然产生了丑陋的结果。请参考我在帖子中附加的示例图片。那条线的每一侧都存在不同的问题。 - Patrick Hogan
不,你的图片使用的是垂直线,而不是角的平分线。平分线是角度一半的点。如果你的基准线形成90度角,那么它的平分线位于45度处。明白了吗? - Neil N

1

最终我不得不动手写一个小的ribbonizer来解决类似的问题。

对我来说,问题在于我想要在OpenGL中使用粗线,但是在iPhone上使用OpenGL时出现了一些我看到的伪影。经过研究各种解决方案,如贝塞尔曲线等,我决定最简单的方法可能就是自己制作。有几种不同的方法。

一种方法是找到两个线段之间的交点角度,然后沿着该交点线移动一定距离远离表面,并将其视为ribbon顶点。我尝试了一下,但看起来不直观;ribbon宽度会变化。

另一种方法是实际计算线段表面的法线,并使用它来计算该线段的理想ribbon边缘,并在ribbon段之间进行实际的交点测试。这很有效,但对于锐角,ribbon线段交点太远了(如果线段间的角度接近180')。

我用了两种方法解决了锐角问题。Paul Bourke的线段交点算法(我用的是未优化的版本)建议检测交点是否在线段内。由于两个线段是相同的,我只需要测试其中一个线段是否有交点。然后可以仲裁如何解决这个问题;可以通过在两端之间伪造一个最佳点或者放置端点来解决——这两种方法都很好——放置端点的方法可能会扰乱OpenGL的多边形正面和背面的顺序。

请参见http://paulbourke.net/geometry/lineline2d/

在此处查看我的源代码:https://gist.github.com/1474156


1
我对PostScript有一个小抱怨,不幸的是其他绘图范式也遵循它的先例,那就是斜接角要么被画成完美的斜接(如果它没有超出斜接限制),要么被画成斜角。我想知道为什么不能将斜接限制用作“截断”斜接角的点。 - supercat

0
在我的情况下,我可以承受透支。我只是在每个折线顶点处绘制半径为宽度/2的圆形。
通过这种方式掩盖工件,如果您可以接受“圆角”和一些超绘制,那么实现起来非常容易。

0

从您的图像来看,似乎您正在使用FILL绘制线段周围的框,并使用橙色。这样做肯定会产生不良的过度绘制。因此,首先要做的是不渲染黑色边框,填充颜色可以是不透明的。

为什么您不能使用GL_LINES原语来实现您想要做的事情?您可以指定宽度、过滤、平滑度、纹理等任何内容。您可以使用glDrawArrays()渲染所有顶点。我知道这不是您所想的,但由于您专注于2D绘图,这可能是更容易的方法。(搜索Textured lines等)


0

我认为我会使用镶嵌算法。虽然在大多数情况下,这些算法的使用目的是减少顶点数量以优化渲染,但在您的情况下,您可以进行参数化以保留所有细节 - 优化的可能性可能会有用。

网络上有许多镶嵌算法和代码 - 几年前,我将一个纯C语言的算法封装成DLL供Delphi景观渲染器使用,而且它们也是高级图形编程教程等常见主题。


0

我也很感兴趣,因为我想完善我的地图应用程序 (Kosmos) 对道路的绘制。我使用的一个解决方法是将折线画两次,一次用粗线,一次用细线,颜色不同。但这并不是真正的多边形,只是一种快速模拟的方式。在这里可以看到一些样例:http://wiki.openstreetmap.org/wiki/Kosmos_Rendering_Help#Rendering_Options

我不确定这是否符合你的需求。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接