使用OpenGL(和OpenGL ES)渲染SVG

63

我目前正在调查使用OpenGL和OpenGL ES从SVG文件中呈现矢量图形的可能性。 我计划针对Windows和Android进行开发。 我理想的解决方案是具有最小化C库,可以从给定的SVG文件生成一个多边形三角剖分。 然后将生成标准的OpenGL或OpenGL ES调用,并在重新绘制时使用显示列表或VBO进行优化。 在平移和旋转后,我会简单地绘制一个显示列表以绘制矢量图像,使我可以将其与其他OpenGL调用混合使用。

到目前为止,我看到的建议是首先使用QT或Cairo。 - 鉴于我想要实现的内容,这不是一个选项,因为我希望管理自己的OpenGL上下文而没有臃肿的库。 并且这也不适用于Android。

第二个选择是使用呈现纹理的库。虽然这对于静态矢量图形可能还可以,但对于需要频繁缩放和旋转的游戏来说,这不是一种有效或可行的选项。

第三种方法是使用OpenVG。 OpenVG规范有一些开源实现(如ShivaVG等),但我尚未找到能够在运行时从给定的SVG文件生成适当的OpenVG调用的库,并且我无法看到如何像我们希望使用显示列表或VBO进行优化。

这三种方法都有局限性。如果没有其他解决方案,我认为最有前途的选择是使用OpenVG实现。因此,我的问题是,是否存在任何库可以做到我想要的事情,或者接近我想要的内容? 如果没有,是否有充分的理由? 还是尝试从头开始做这件事更好?


1
解析SVG文件中的路径并不困难。请查看以下内容以绘制二次贝塞尔曲线:http://www.opengl.org/resources/code/samples/redbook/bezcurve.c - Grady Player
@Grady 在OpenGL ES中不存在glEvalCoord;GLU也不存在,其曲面细分器在处理凸多边形等情况下非常关键。@me232 很好的问题,已点赞并收藏。 - Calvin1602
+1 好的研究和清晰明了的问题提出。 - Ramya Maithreyi
是的,你说得对,我被最小化的C库部分所困扰。 - Grady Player
1
这基本上也是 https://dev59.com/4XNA5IYBdhLWcg3wSrma 的一个副本。 - Fizz
显示剩余3条评论
6个回答

18

我的回答将涉及OpenGL中显示矢量图形的一般情况,因为解决此问题的所有解决方案都可以相当容易地支持特定的SVG,尽管没有一个支持动画SVG(SMIL)。由于没有提到动画,我假设问题只涉及静态SVG。

首先,我不会费心用任何OpenVG,甚至不会使用最现代的MonkVG,这可能是最现代但不完整的实现。 OpenVG委员会在2011年关闭,如果没有全部,至少所有实现都是废弃软件或最好是遗留软件。

自2011年以来,最新技术是Mark Kilgard的作品NV_path_rendering,目前仅是供应商(Nvidia)扩展,您可能已经从其名称中猜到了。关于这个有很多材料:

当然,您可以加载SVG等文件:https://www.youtube.com/watch?v=bCrohG6PJQE。它们也支持路径的PostScript语法。您还可以将路径渲染与其他OpenGL(3D)内容混合使用,如以下演示:

现在,Google的Skia库在可用时在幕后使用了NV_path_rendering。(Nvidia在2013年和2014年末贡献了该代码。)开发cairo的其中一名开发人员(也是Intel的雇员)似乎也喜欢它http://lists.cairographics.org/archives/cairo/2013-March/024134.html,尽管我还不知道cairo是否有任何具体的使用NV_path_rendering的努力。

NV_path_rendering在固定管线上有一些微小的依赖性,因此在OpenGL ES中使用它可能有些麻烦。这个问题在上面链接的官方扩展文档中有记录。例如,要解决此问题,请参见Skia / Chromium的做法:https://code.google.com/p/chromium/issues/detail?id=344330

一种新兴的、甚至没有(或者根本没有)供应商支持或学术光环的技术是NanoVG,目前正在开发和维护。(https://github.com/memononen/nanovg)。在我看来,考虑到随着时间的推移,已经出现了许多基于OpenGL的2D库并消失了,你要使用一些不受主要供应商支持的东西,这是一个很大的赌注。


13

请看MonkVG,它是一个基于OpenGL ES的OpenVG类API实现。

另外,如果需要在OpenVG(MonkVG)之上呈现SVG,请查看MonkSVG

MonkVG已经适用于iOS、OSX和Android平台。

我是这两个库的作者,乐意回答任何问题。


1
嗨,Micah。我找到了MonkVG的Windows项目,并成功地在VS2010中构建了它。有没有适用于Windows的工作示例?我尝试跟随示例,但程序崩溃了。我手动创建了自己的窗口和上下文,但如果你有基于GLUT的示例,那也可以。 - Matt Esch

9
需要指出的是,使用OpenGL或OpenGL ES渲染SVG或OpenVG基本上是一个坏主意。这就是为什么OpenVG实现都非常缓慢且被大部分人放弃的原因。将路径(所有SVG / OpenVG渲染的基础)转换为三角形列表所需的细分过程基本上是缓慢和低效的。这基本上需要在3D渲染管道中插入排序/搜索算法,这会破坏性能。还有一个问题是需要动态内存分配方案,因为数据集的大小未知,因为SVG对路径几何复杂度没有限制。这是一个真正糟糕的设计。
SVG和OpenVG是由对现代3D图形硬件引擎(三角形列表)的工作原理了解很少的开发人员创建的。它们被创建为Adobe Flash的开放替代品,而Flash也具有相同的缺陷架构,使得Flash因性能不可预测而在行业中备受厌恶。
我的建议是重新考虑设计,并直接使用OpenGL三角形列表。您可能需要编写更多代码,但是您的应用程序的性能将提高约一千倍,并且您可以更轻松地调试自己的代码而不是别人的代码。

1
只是一个提醒:有很多使用OpenGL绘制SVG的方法,将路径细分成三角形可能是最糟糕的方法。那么使用着色器呢? - Adrian Maire
@AdrianMaire,你知道是否存在基于着色器的方法吗? - trusktr
我不记得有任何现成的库。我所提到的方法是绘制一个覆盖整个路径的大矩形,然后对于每个像素,检测它是否在内部/外部/边界,并计算其颜色。可以应用超采样和其他技术。我相信这方面已经有相关的论文了。 - Adrian Maire

6
我正在研究使用OpenGL和OpenGL ES从SVG文件中渲染矢量图形的可能性,目标是Windows和Android。我的理想解决方案是拥有一个最小的C库,可以从给定的SVG文件生成多边形三角剖分。然后会生成标准的OpenGL或OpenGL ES调用,并在重新绘制时使用显示列表或VBO进行优化。我只需在平移和旋转后绘制显示列表来绘制矢量图像,这样可以将其与其他OpenGL调用混合使用。
如果您只想将SVG矢量形状转换为OpenGL|ES,则建议自己编写解析器和逻辑。请注意,SVG是一个庞大的规范,具有不同的功能,如绘画服务器(渐变,图案...),引用,过滤,裁剪,字体处理,动画,脚本,链接等等。
如果您需要完整的SVG支持,则可以使用http://code.google.com/p/enesim上的一个名为egueb(特别是esvg)的库,它使用enesim(具有软件和OpenGL后端的渲染库)进行绘图。在大多数情况下,它使用着色器,并将所有内容呈现为纹理,该库非常灵活,允许您适应自己的特定需求,例如修改呈现场景,转换等。因为gl绘图总是在纹理中完成。

到目前为止,我看到的建议首先是使用QT或Cairo。-这不是一个选项, 因为我希望管理自己的OpenGL上下文而没有臃肿的库(在 我试图实现的内容方面)。这也不适用于Android。

第二个选择是使用渲染到纹理的库。虽然这对于静态矢量图形可能没问题, 但对于需要频繁缩放和旋转的游戏来说,这不是一种有效或可行的选项。

在gl后端的特定情况下,enesim不会创建GLX(或任何其他依赖于窗口的上下文),您必须提供它,因此它完全适应您的情况,因为它仅使用GL调用。

唯一的缺点是图形库在GL支持或完整的SVG规范支持方面尚未完全实现,但根据您的需求,对我来说看起来是一个不错的选择。

你使用了什么算法将三次贝塞尔曲线(路径)转换为OpenGL?抗锯齿怎么办? - Raks

3

源自http://shivavg.svn.sourceforge.net/viewvc/shivavg/trunk/src/shPipeline.c?revision=14&view=markup

static void shDrawVertices(SHPath *p, GLenum mode)
{
int start = 0;
int size = 0;

/* We separate vertex arrays by contours to properly
handle the fill modes */
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, sizeof(SHVertex), p->vertices.items);

while (start < p->vertices.size) {
size = p->vertices.items[start].flags;
glDrawArrays(mode, start, size);
start += size;
}

glDisableClientState(GL_VERTEX_ARRAY);
}

所以它确实使用了VBO。因此,我建议您制作自己的SVG解析器/使用预制的解析器,并将调用转发到ShivaVG。
您仍然面临一个问题,即ShivaVG是用C编写的(而不是Java),并创建了一个OpenGL上下文(如果我正确地阅读了代码,则不是OpenGLES)。因此,即使您使用Android的NDK编译它,您也需要修改代码(例如,我看到了一些glVertex3f,但似乎并不是很必要...希望一切顺利)。当然,另一个选择是将代码从C移植到Java。也许没有你想象的那么痛苦。
祝你好运!

2
谢谢,我会看一下这个。我很惊讶硬件加速矢量图形的需求如此之高,并且OpenVG是专为移动设备设计的,竟然没有一个开箱即用的实现。我知道有一个用C#编写的项目,试图做类似的事情并取得了成功的结果[http://neuronspew.wordpress.com/category/svg-game-engine-project/]。如果我能让它工作,我一定会公开我的代码。 - Matt Esch
1
谢谢!请在完成后更新此线程的当天通知我。 - Calvin1602
ShivaVG不使用VBO。使用glVertexPointer()并不意味着使用VBO;在这段代码中,顶点取自p->vertices.items所指向的内存。我之所以知道这一点是因为当调用此函数时,程序的另一部分留下了一个绑定的VBO,导致我需要进行调试。 - Tim

1
你可以看一下AmanithVG,他们似乎已经实现了一个很好的路径-三角形管道。我尝试过iOS GL tiger示例,似乎三角剖分不是真正的瓶颈。

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