什么是在4.1版本的OpenGL中进行文本渲染的最新技术?

在OpenGL中已经有很多关于文本渲染的问题,比如:

  • 如何做一个GUI的OpenGL生活文字渲染?

但是大部分讨论的是使用固定functionstream水线渲染纹理四边形。 着色器必须做出更好的方法。

我并不十分关心国际化,我的大部分string都是绘图标记(date和时间或纯数字)。 但是这些图将以屏幕刷新率重新渲染,并且可能有相当多的文本(屏幕上不超过几千个字形,但硬件加速布局足够好)。

使用现代OpenGL进行文本渲染的build议方法是什么? (使用这种方法引用现有的软件是很好的证据,表明它运行良好)

  • 几何着色器接受例如位置和方向以及字符序列并发出纹理四边形
  • 呈现vector字体的几何着色器
  • 如上所述,但使用曲面细分着色器
  • 一个计算着色器来做字体光栅化

渲染轮廓,除非只渲染十几个字符,否则由于每个angular色需要的顶点数目接近曲率,所以仍然是“不行”。 虽然已经有方法在像素着色器中对贝塞尔曲线进行评估,但是这些方法不容易抗锯齿,这种方法使用距离贴图纹理四元组很麻烦,而且着色器中的评估曲线仍然比计算更加昂贵。

“快速”和“质量”之间的最佳平衡仍然是带有符号距离场纹理的质感四边形。 这比使用普通纹理四边形稍微慢一些,但不是那么多。 另一方面,质量则完全不同。 结果是真正的惊人,它可以得到尽可能快的速度,像辉光这样的效果也很容易添加。 另外,如果需要,该技术可以很好地降级到较旧的硬件。

看到着名的阀门纸的技术。

该技术在概念上类似于隐式表面(元球等)的工作方式,尽pipe它不生成多边形。 它完全在像素着色器中运行,并将从纹理采样的距离作为距离函数。 超过所选阈值(通常为0.5)的所有内容都是“in”,其他所有内容都是“out”。 在最简单的情况下,在10年前没有着色器的硬件上,将alphatesting阈值设置为0.5就能做到这一点(尽pipe没有特殊效果和抗混叠效果)。
如果想为字体添加更多的权重(粗体),稍微小一些的阈值就可以完成,而不需要修改一行代码(只需更改“font_weight”制服即可)。 对于发光效果,人们简单地将超过一个阈值的所有事物视为“in”,将超过另一个(较小)阈值的所有事物都视为“out,but in glow”,并将两者之间的LERP考虑在内。 抗锯齿工作方式类似。

通过使用一个8位有符号距离值而不是一个单一的位,这种技术增加了你的纹理映射的有效分辨率在每个维度16倍(而不是黑色和白色,所有可能的色调被使用,因此我们有256倍的信息使用相同的存储)。 但是,即使你放大了16倍,结果仍然看起来相当可接受。 长长的直线最终会变得有点扭曲,但是不会有典型的“块状”抽样文物。

你可以使用一个几何着色器来生成四元组(减less总线带宽),但是老实说,这个增益是相当小的。 GPG8中描述的实例化字符渲染也是如此。 如果你有很多文字要绘制,实例的开销只能被分摊。 在我看来,收益与增加的复杂性和不可降级性无关。 另外,你要么受限于常量寄存器的数量,要么你必须从一个纹理缓冲区对象中读取,这对caching一致性来说是非最优的(而且目的是为了优化开始!)。
一个简单的,简单的旧的顶点缓冲区,如果你提前一点安排上传,并且可以运行在过去15年内build立的每一个硬件上,那么速度也一样快(可能更快)。 而且,它不限于字体中的任何特定数量的字符,也不限于要呈现的特定数量的字符。

如果您确定您的字体中没有超过256个字符,则纹理arrays可能值得考虑,以类似于从几何着色器中的点生成四边形的方式去除总线带宽。 当使用数组纹理时,所有四边形的纹理坐标具有相同的常量st坐标,并且只在r坐标上有所不同,这与要呈现的字符索引相等。
但是与其他技术一样,预期的收益也是微不足道的,这是以与上一代硬件不兼容为代价的。

Jonathan Dummer提供了一个便利的工具来生成距离纹理: 描述页面

更新:
正如最近在可编程顶点拉动 (D.Rákos,“OpenGL Insights”,第239页)中指出的那样,没有显着的额外延迟或开销与在最新一代的GPU上从着色器以编程方式拉动顶点数据相关,使用标准的固定function来做同样的事情。
此外,最新一代的GPU具有越来越多的合理大小的通用二级caching(例如,在开普勒上的1536kiB),所以当从缓冲纹理拉动四angular的随机偏移时,可能期望非相干访问问题less于问题。

这使得从缓冲区纹理中提取常量数据(如四边形大小)的想法更具吸引力。 假设的实现可以通过如下方式将PCIe和内存传输以及GPU内存降至最低:

  • 只有上传一个字符索引(每个要显示的字符一个)作为唯一的input到一个顶点着色器,该顶点着色器将通过此索引和gl_VertexID ,并将其放大到几何着色器中的4个点,仍然具有字符索引和顶点标识这将是“顶点着色器中可用的gl_primitiveID”)作为唯一属性,并通过变换反馈来捕获它。
  • 这将是快速的,因为只有两个输出属性(GS中的主要瓶颈),并且在两个阶段都接近“非操作”。
  • 绑定缓冲区纹理,该纹理包含字体中每个字符相对于基点的纹理四边形的顶点位置(这些基本上是“字体度量”)。 这个数据可以通过只存储左下angular顶点的偏移量来压缩为每四个数字四个数字,并且编码轴alignment框的宽度和高度(假设一半浮点数,这将是每字符8个字节的恒定缓冲区 – 典型的256个字符的字体可以完全适合L1caching的2kiB)。
  • 为基线设置一个统一
  • 绑定具有水平偏移量的缓冲区纹理。 这些可能甚至可以在GPU上进行计算,但是对于CPU上的这种事情来说,它更容易,更高效,因为它是一个严格的顺序操作,并不重要(考虑字距)。 此外,它将需要另一个反馈通过,这将是另一个同步点。
  • 渲染之前从反馈缓冲区生成的数据,顶点着色器从缓冲区对象(使用原始ID和字符索引)拉取基点的水平偏移量和顶点顶点的偏移量。 提交的顶点的原始顶点ID现在是我们的“原始ID”(记住GS将顶点转换为四边形)。

像这样,理想情况下可以将所需的顶点带宽降低75%(摊销),尽pipe它只能渲染一条线。 如果想要在一个绘图调用中渲染多行,则需要将基线添加到缓冲区纹理,而不是使用统一的(使带宽增益更小)。

然而,即使假定减less了75%,由于显示“合理”文本量的顶点数据仅在50-100kB左右(对于GPU或PCIe总线实际上为0 ),我仍然怀疑添加的复杂性和丢失的向后兼容性真的是值得的麻烦。 将零点减less75%仍然只有零。 我承认没有尝试过上述方法,需要更多的研究才能做出真正合格的陈述。 但是,除非有人能够certificate真正令人惊叹的性能差异(使用“正常”数量的文本,而不是数十亿字符!),否则我的观点是对于顶点数据来说,一个简单的,旧的顶点缓冲区足够好被视为“最先进的解决scheme”的一部分。 这很简单,直接,工作,而且运作良好。

上面已经引用了“ OpenGL Insights ”,值得指出的是由Stefan Gustavson撰写的“Distance Shape Rendering by Distance Fields”一章,它详细解释了距离场渲染。

2016年更新:

同时,还有一些附加技术,旨在消除在极端放大倍数下变得令人不安的边angular变形。

一种方法简单地使用伪距场而不是距离场(不同之处在于距离是不是实际轮廓的最短距离,而是轮廓突出于边缘的假想线)。 这是更好的,并运行在相同的速度(相同的着色器),使用相同数量的纹理内存。

另一种方法是使用github上的三通道纹理细节和实现中的三位中值。 这是为了改进以前用来解决问题的手段。 质量好,略微不明显,速度较慢,但​​使用三倍的纹理记忆。 另外,额外的效果(如发光)很难得到正确的。

最后,存储构成字符的实际贝塞尔曲线,并在片段着色器中进行评估已经变得可行 ,性能稍差(但不是太大,这是一个问题),即使在最高放大倍数下也是令人惊叹的结果。
使用这种技术实时渲染大型PDF的WebGL演示在这里可用。

最普遍的技术仍然是纹理四边形。 然而在2005年,LORIA开发了一种称为vector纹理的东西,即将vectorgraphics渲染为图元上的纹理。 如果使用这个将TrueType或OpenType字体转换为vector纹理,您可以得到:

http://alice.loria.fr/index.php/publications.html?Paper=VTM@2005

http://code.google.com/p/glyphy/

GLyphy和其他基于SDF的OpenGL渲染器之间的主要区别在于大多数其他项目将SDF抽样为纹理。 这有所有抽样的常见问题。 IE浏览器。 它扭曲的轮廓和低质量。 GLyphy代表使用提交给GPU的实际向量的SDF。 这导致非常高质量的渲染。

缺点是代码是用于OpenGL ES的iOS。 我可能会做一个Windows / Linux的OpenGL 4.x端口(希望作者将添加一些真实的文档,虽然)。

我很惊讶Mark Kilgard的宝贝NV_path_rendering (NVpr)没有被上述任何一个提到。 尽pipe其目标比字体渲染更普遍,但它也可以使用字体和字距渲染文本。 它甚至不需要OpenGL 4.1,但目前它是一个供应商/ Nvidia专用的扩展。 它基本上使用glPathGlyphsNV (取决于freetype2库来获取指标等)将字体转换为path。然后,您还可以使用glGetPathSpacingNV来访问字距信息,并使用NVpr的常规path呈现机制来显示使用path“转换”字体的文本。 (我把它放在引号中,因为没有真正的转换,曲线就是这样使用的。)

NVpr的字体function录制的演示不幸的不是特别令人印象深刻。 (也许有人应该做一个沿着更快速的SDF演示的人可以find在intertubes …)

2011年的NVpr API演示文稿的字体部分从这里开始,并继续在下一部分 ; 这个演讲是如何分裂的,这有点不幸。

NVpr更多的一般资料:

  • Nvidia NVpr hub ,但是着陆页上的一些内容并不是最新的
  • Siggraph 2012为纸张的path渲染方法,称为“模板,然后覆盖”(StC); 本文还简要介绍了Direct2D这样的竞争技术如何工作。 与字体相关的位已被降级到文件的附件 。 还有一些额外的video/演示 。
  • GTC 2014呈现更新状态; 简而言之:现在Google的Skia(Nvidia在2013年底和2014年提供了这个代码)支持了这个function,而这个function在Google Chrome浏览器和[独立于Skia上,我认为]在Adobe Illustrator CC 2014
  • OpenGL扩展registry中的官方文档
  • USPTO已经向Kilgard / Nvidia授予至less四项与NVpr有关的专利,如果您想自行实施StC, US8698837 , US8698808 , US8704830和US8730253 ,您应该知道这些专利 。 请注意,美国专利商标局另有17个文件与此相关,如“也作为出版物”,其中大部分是专利申请,因此完全有可能授予更多专利。

而且由于在我的回答之前,“模板”这个词在这个页面上没有产生任何点击,所以看起来参与这个页面的SO社区的子集尽pipe数量很多,但并不知道无镶嵌的模板 – 缓冲 – 一般用于path/字体渲染的基于方法的方法。 Kilgard 在opengl论坛上有一个类似于FAQ的post ,可以说明无镶嵌的path渲染方法与bog标准3Dgraphics不同,尽pipe它们仍然使用[GP] GPU。 (NVpr需要一个支持CUDA的芯片。)

从历史的angular度来看,Kilgard也是1997年SGI的经典“一个简单的基于OpenGL的纹理映射文本API”的作者,不应该与2011年推出的基于模板的NVpr混淆。


大多数,如果不是所有最近在这个页面上讨论的方法,包括模板为基础的方法,如NVpr或基于SDF的方法,如GLyphy(我不在这里进一步讨论,因为其他答案已经覆盖它)有一个限制:它们是适用于传统(〜100 DPI)显示器的大尺寸文字显示,无任何缩放级别的锯齿,而且即使在尺寸较小的高DPI视网膜显示器上也很好看。 他们并没有完全提供微软的Direct2D + DirectWrite给你的东西,也就是暗示主stream显示器上的小字形。 (对于一般的暗示性的视觉调查,请参阅这个例子的网页 ,更深入的资源位于antigrain.com 。)

我没有意识到任何开放和产品化的基于OpenGL的东西,可以做微软现在可以暗示的东西。 (我承认对苹果OS X GL / Quartz内部的无知,因为据我所知,苹果并没有公布如何做基于GL的字体/path渲染的东西。看来OS X不像MacOS 9 )无论如何,有一个2013年的研究论文,通过INRIA的Nicolas P. Rougier编写的OpenGL着色器来解决提示问题 。 如果您需要从OpenGL中进行提示,则可能值得一读。 虽然似乎像freetype这样的图书馆在暗示的时候已经完成了所有的工作,但实际上并非如此,因为我引用了这个论点:

FreeType库可以在RGB模式下使用亚像素抗锯齿来栅格化字形。 但是,这只是问题的一半,因为我们也希望实现子像素定位以准确放置字形。 在分数像素坐标处显示带纹理的四边形不能解决问题,因为它仅在全像素级别导致纹理内插。 相反,我们想要在子像素域中实现精确的移位(0和1之间)。 这可以在片段着色器[…]中完成。

解决scheme并不是微不足道的,所以我不打算在这里解释它。 (这篇文章是开放的。)


我从Rougier的论文(以及Kilgard似乎没有考虑过的)中学到的另外一件事是,(微软+ Adob​​e)的字体力量创造出的不是一种,而是两种字距规范方法。 旧的基于一个所谓的kern表,它由freetype支持。 新版本被称为GPOS,只有在自由软件世界里,像HarfBuzz或pango这样的新字体库才支持它。 由于NVpr似乎不支持这两个库中的任何一个,因此字距可能无法与NVpr一起使用,以获得一些新的字体; 根据这个论坛的讨论 ,有一些显然在野外。

最后,如果你需要做复杂的文本布局(CTL),那么你现在看起来好像没有OpenGL的运气,因为没有基于OpenGL的库存在。 (另一方面,DirectWrite可以处理CTL)。像HarfBuzz这样的开源库可以呈现CTL,但是我不知道如何让它们很好地工作(如使用基于模板的方法) OpenGL的。 您可能必须编写粘合代码来提取重新塑造的轮廓,并将它们作为pathinput到基于NVpr或SDF的解决scheme中。

我认为你最好的select是使用OpenGL后端查看开罗graphics 。

我开发一个3.3核心的原型时遇到的唯一问题是在OpenGL后端不推荐使用函数。 这是1-2年前,所以情况可能有所改善…

无论如何,我希望在未来的桌面openglgraphics驱动程序将实施OpenVG。