从版本4.1开始,OpenGL中的文本渲染技术是什么?

Ben*_*igt 196 opengl shader text glsl opengl-4

关于OpenGL中的文本呈现已经有很多问题,例如:

但主要讨论的是使用固定功能管道渲染纹理四边形.当然着色器必须做出更好的方法.

我并不真正关心国际化,我的大部分字符串都是情节刻度标签(日期和时间或纯数字).但是这些图将以屏幕刷新率重新渲染,并且可能会有相当多的文本(屏幕上不超过几千个字形,但足够硬件加速布局会很好).

使用现代OpenGL进行文本渲染的推荐方法是什么?(引用现有软件使用该方法是很好的证据,它运作良好)

  • 接受例如位置和方向以及字符序列并发出纹理四边形的几何着色器
  • 渲染矢量字体的几何着色器
  • 如上所述,但改为使用曲面细分着色器
  • 用于进行字体光栅化的计算着色器

Dam*_*mon 196

除非您仅渲染十几个字符,否则渲染轮廓仍然是"不行",因为每个字符需要近似曲率的顶点数量.虽然已经有了在像素着色器中评估贝塞尔曲线的方法,但是这些方法不易被抗锯齿,这使用距离图纹理四边形是微不足道的,并且在着色器中评估曲线的计算结果仍然比必要的贵得多.

"快速"和"质量"之间的最佳折衷仍然是具有签名距离场纹理的纹理四边形.它比使用普通的普通纹理四边形慢,但不是那么多.另一方面,质量完全不同.结果非常令人惊叹,它可以尽可能快地获得,并且发光等效果也很容易添加.此外,如果需要,该技术可以很好地降级到较旧的硬件.

请参阅着名的Valve纸张了解该技术.

该技术在概念上类似于隐式曲面(元球等)的工作方式,尽管它不生成多边形.它完全在像素着色器中运行,并将纹理采样的距离作为距离函数.超过所选阈值(通常为0.5)的所有内容都是"in",其他所有内容都是"out".在最简单的情况下,在具有10年历史的非着色器硬件上,将alpha测试阈值设置为0.5将完成相同的操作(尽管没有特殊效果和抗锯齿).
如果想要为字体添加更多的权重(人造粗体),稍微小一点的阈值就可以完成这一操作,而无需修改单行代码(只需更改"font_weight"统一).对于发光效果,只需将一个阈值以上的所有内容视为"in",将另一个(较小)阈值之上的所有内容视为"out,but in glow",以及两者之间的LERP.抗锯齿的工作方式类似.

通过使用8位有符号距离值而不是单个位,此技术可将纹理贴图的有效分辨率在每个维度上增加16倍(而不是黑色和白色,使用所有可能的阴影,因此我们有256倍使用相同存储的信息).但即使你放大远超过16倍,结果仍然是可以接受的.长直线最终会变得有点晃动,但不会有典型的"块状"采样文物.

您可以使用几何着色器从点生成四边形(减少总线带宽),但老实说,增益相当小.对于GPG8中描述的实例字符渲染也是如此.如果您要绘制大量文本,则实例化的开销仅摊销.在我看来,收益与增加的复杂性和不可降级性无关.另外,您要么受到常量寄存器数量的限制,要么必须从纹理缓冲区对象中读取,这对于缓存一致性来说并非最佳(并且目的是优化以开始!).
如果您提前安排上传并且将在过去15年内构建的每个硬件上运行,那么简单,普通的旧顶点缓冲区同样快(可能更快).并且,它不限于字体中任何特定数量的字符,也不限于要呈现的特定字符数.

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

Jonathan Dummer有一个方便的工具用于生成距离纹理:描述页面

更新:
正如最近在可编程顶点拉动(D.Rákos,"OpenGL Insights",第239页)中指出的那样,在最新一代GPU上从着色器以编程方式提取顶点数据没有明显的额外延迟或开销,与使用标准固定功能相同.
此外,最新一代GPU具有越来越多的合理大小的通用L2高速缓存(例如nvidia Kepler上的1536kiB),因此当从缓冲区纹理中减去四角形的随机偏移时,可能会出现非相干访问问题.问题.

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

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

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

然而,即使假设减少了75% - 因为顶点数据显示"合理"数量的文本只有大约50-100kiB(对于GPU或PCIe总线几乎为零) - 我仍然怀疑添加复杂性和失去向后兼容性确实值得麻烦.零减少75%仍然只有零.我承认没有尝试过上述方法,需要更多的研究才能做出真正合格的陈述.但是,除非有人能够展示真正令人惊叹的性能差异(使用"正常"的文本量,而不是数十亿个字符!),我的观点仍然是,对于顶点数据,一个简单的,普通的旧顶点缓冲区是合理的足够好被认为是"最先进的解决方案"的一部分.它简单明了,工作正常,效果很好.

上面已经引用了" OpenGL Insights ",值得指出Stefan Gustavson撰写的"距离场的2D形状渲染"一章,它详细解释了距离场渲染.

2016年更新:

同时,存在若干附加技术,其旨在消除在极端放大率下变得令人不安的拐角圆角伪影.

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

另一种方法是在三通道纹理细节和github实现的实现中使用三个中值.这旨在改进以前用于解决该问题的和/或黑客.良好的质量,略微,几乎不明显,较慢,但使用三倍的纹理内存.此外,额外的效果(如发光)更难以正确.

最后,存储构成字符的实际贝塞尔曲线,并在片段着色器中它们进行评估已变得切实可行,性能稍差(但不是很多,这是一个问题),即使在最高放大倍率下也能获得令人惊叹的结果.
WebGL的演示使这一技术在提供实时的大型PDF 这里.

  • @NicolBolas:抱歉,你对这句话说得对.这确实有点误导.在上一段中,我写道_"人们甚至可能在GPU上生成这个,但这需要反馈和... isnogud."_ - 然后错误地继续_"从反馈缓冲区生成的数据"_ .我会纠正这个.实际上,我会在周末改写完整的东西,所以它不那么模棱两可. (3认同)
  • 工作和看起来很棒,感谢分享!对于那些想要HLSL frag着色器源的人,请参见[这里](http://bytewrangler.blogspot.co.uk/2011/10/signed-distance-fields.html).您可以通过将`clip(...)`行替换为`if(text.a <0.5){discard;}`(或`text.a <threshold`)来适应GLSL.HTH. (2认同)
  • @NicolBolas:您似乎没有仔细阅读。这两个问题在答案中都有解释。开普勒作为“最新一代”的例子给出,没有第二次通过(并解释了原因),我声明我_不_相信假设的带宽节省技术明显更快或完全值得麻烦。然而,信念没有任何意义——人们必须尝试知道(我没有,因为我不认为绘制“正常”数量的文本是瓶颈)。然而,当人们对带宽感到绝望并且拥有“异常”数量的文本时,它可能是值得的。 (2认同)

Dis*_*ame 15

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

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

缺点是该代码适用于带有OpenGL ES的iOS.我可能会制作一个Windows/Linux OpenGL 4.x端口(希望作者会添加一些真实的文档).

  • 任何对GLyphy感兴趣的人都应该在Linux.conf.au 2014上观看作者的演讲:https://www.youtube.com/watch?v = KdNxR5V7prk (3认同)

dat*_*olf 13

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

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

  • 你知道使用这种技术的任何实现吗? (2认同)
  • 不(如在生产级别),但Kilgard的论文(请参阅下面的链接答案)有一个简短的批评,我总结为:尚不实用.该地区的研究越来越多; Kilgard引用的最新工作包括http://research.microsoft.com/en-us/um/people/hoppe/ravg.pdf和https://uwspace.uwaterloo.ca/handle/10012/4262 (2认同)

Fiz*_*izz 8

我很惊讶Mark Kilgard的宝贝,NV_path_rendering(NVpr),上面没有提到过.虽然它的目标比字体渲染更通用,但它也可以从字体和字距调整渲染文本.它甚至不需要OpenGL 4.1,但它目前仅是供应商/ Nvidia扩展.它基本上将字体转换为路径,使用glPathGlyphsNV它取决于freetype2库以获取指标等.然后您还可以访问字距调整信息glGetPathSpacingNV并使用NVpr的一般路径渲染机制来显示使用路径"转换"字体的文本.(我把它放在引号中,因为没有真正的转换,曲线按原样使用.)

遗憾的,NVpr字体功能录制演示并不是特别令人印象深刻.(也许有人应该按照可以在intertubes上找到的更加流畅的SDF演示来制作一个......)

有关字体部分的2011 NVpr API演示文稿将在此处开始,在下一部分继续; 这个演示文稿如何拆分有点不幸.

关于NVpr的更一般材料:

  • Nvidia NVpr集线器,但登陆页面上的一些材料并不是最新的
  • Siggraph 2012论文为路径渲染方法的大脑,称为"模板,然后覆盖"(StC); 本文还简要介绍了Direct2D等竞争技术的工作原理.与字体相关的位已经降级为论文的附件.还有一些额外的东西,如视频/演示.
  • GTC 2014演示文稿提供更新状态; 简而言之:它现在由Google的Skia支持(Nvidia在2013年末和2014年提供了代码),而后者又用于谷歌Chrome和[独立于Skia,我认为]在Adobe Illustrator CC 2014的测试版中使用
  • OpenGL扩展注册表中的官方文档
  • 美国专利商标局已经向Kilgard/Nvidia授予至少四项与NVpr有关的专利,如果你想自己实施StC,你可能应该知道这些专利:US8698837,US8698808,US8704830US8730253.请注意,有17个美国专利商标局文件与此相关,因为"也发表为",其中大多数是专利申请,因此完全有可能从这些专利中获得更多专利.

由于"模板"这个词在我的回答之前没有在这个页面上产生任何点击,所以看起来参与此页面的SO社区的子集,尽管数量众多,却没有意识到无曲面,模板缓冲 - 一般来说,基于路径/字体渲染的方法.Kilgard 在opengl论坛上有一篇类似FAQ的帖子,可以说明无曲面路径渲染方法与沼泽标准3D图形的区别,即使它们仍在使用[GP] GPU.(NVpr需要一个支持CUDA的芯片.)

从历史角度来看,Kilgard也是经典的"基于OpenGL的纹理映射文本API"的作者,SGI,1997,不应该与2011年推出的基于模板的NVpr相混淆.


大多数(如果不是全部)最近在本页讨论的方法,包括基于模板的方法,如NVpr或基于SDF的方法,如GLyphy(我在这里没有进一步讨论,因为其他答案已经涵盖了它)有一个限制:它们是适用于传统(~100 DPI)显示器上的大文本显示,无需任何级别的缩放,即使尺寸较小,它们在高DPI,视网膜状显示器上也看起来不错.但是它们没有完全提供Microsoft的Direct2D + DirectWrite为您提供的功能,即在主流显示器上暗示小字形.(例如,对于提示的视觉调查,请参见此类别的页面.更多深入的资源在antigrain.com上.)

我不知道任何开放和产品化的基于OpenGL的东西,可以做微软目前的暗示.(我承认对Apple的OS X GL/Quartz内部构件一无所知,因为据我所知,Apple还没有公布他们如何进行基于GL的字体/路径渲染.似乎OS X与MacOS 9不同暗示一下,这会让一些人感到懊恼.)无论如何,有一篇2013年的研究论文通过INRIA的Nicolas P. Rougier撰写的OpenGL着色器来解决暗示.如果你需要从OpenGL中提示,那么它可能值得一读.虽然看起来像freetype这样的库已经完成了暗示的所有工作,但实际上并非如此,原因如下:我从论文中引用:

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

解决方案并非完全无关紧要,因此我不打算在此解释.(该论文是开放获取的.)


我从Rougier的论文(以及Kilgard似乎没有考虑过)中学到的另一件事是,(Microsoft + Adob​​e)的字体功能不仅仅创建了两种字距调整规范方法.旧的基于所谓的kern表,它由freetype支持.新的称为GPOS,它只受到自由软件世界中较新的字体库如HarfBuzz或pango的支持.由于NVpr似乎不支持这些库中的任何一个,因此对于某些新字体,字距调整可能无法与NVpr一起开箱即用; 根据这次论坛讨论,有些人显然是在野外.

最后,如果您需要进行复杂的文本布局(CTL),您似乎目前在使用OpenGL时运气不佳,因为似乎没有基于OpenGL的库.(另一方面,DirectWrite可以处理CTL.)有像HarfBuzz这样的开源库可以渲染CTL,但我不知道你是如何让它们运行良好的(如使用基于模板的方法) OpenGL的.您可能必须编写胶水代码以提取重新形状的轮廓并将其作为路径提供给基于NVpr或SDF的解决方案.

  • 我没有提到NV_path_rendering,因为它是一个扩展,一个专有的供应商使事情变得更糟.我通常会尝试仅为普遍适用的技术提供答案. (4认同)