我该如何在OpenGL中管理缓存纹理?

4
我正在为OpenGL应用程序编写文本渲染器。在运行时可以调整大小、颜色、字体和抗锯齿,因此多种字体可以同时出现在屏幕上。但是,对于每个字符串和属性的组合,分配一个纹理显得过于繁琐。然而,任何给定时间屏幕上显示的字符串只是整个数据库的一小部分。
这给了我机会创建一个缓存,用于缓存逐帧打印的字符串。我们规定只使用一个纹理来完成整个操作,因为创建多个纹理的缓存将导致每次从缓存中打印不同的字符串都要进行纹理交换,这会产生额外的开销。
因此,我有一个2048x2048的纹理,我可以在其中放置任何我能适合的字符串,因为它们被应用程序请求进行缓存。我很快意识到,在二维空间中跟踪可用空间并不容易。
我一直在研究最佳适合和下一步适合等算法,但这些似乎仅适用于1D空间。
那么我该如何在OpenGL中管理这个缓存纹理呢?
编辑:我后来了解到这是“2D装箱问题”的实例。
3个回答

2

你所面临的问题是装箱问题。

首先不好的消息是,它是NP难问题,因此找到最优解非常值得。

我也曾为字体做过纹理缓存。我没有缓存整个单词,而只是字形图像。这使得事情变得容易得多,因为所有的图像都大致呈正方形。一个简单的基于网格的方法来跟踪纹理内存效果非常好。

如果我得到的字形比我的网格盒子大,我就使用暴力搜索分配两个或更多的盒子(这种情况并不经常发生)。如果我没有找到合适的块,则随机从缓存中删除一些字形以释放空间。

这比保持最近使用的缓存要容易得多,并且表现几乎一样好。

顺便说一句 - 这样的缓存总会浪费一些纹理内存。除非你的内存非常紧张,否则这不应该成为问题。你应该使用小的纹理格式(8位Alpha对于字体效果很好)。

另外:如果你将网格块设置为8像素的倍数,并且将抗锯齿降低到4位,你可以在飞行中将字形压缩成一种压缩的DXT或S3TC格式。这样浪费的纹理空间就不再是问题。


2

如果您的纹理内存不足,您可以尝试使用“距离场”或“有符号距离场”字体渲染技术。您可以为每个字体系列使用512x512的纹理,并且可以呈现任何大小的完美抗锯齿文本。

对于该算法,您需要生成一个特殊的纹理,其中包含从纹素到纹理边缘的距离。请查看Valve公司原始论文:http://www.valvesoftware.com/publications/2007/SIGGRAPH2007_AlphaTestedMagnification.pdf。有一些框架利用了这一点。例如,Qt的最新版本使用有符号距离场进行文本渲染。


哦,我在发布之前应该检查日期 :) - user730816
为了完整的实现,您可能需要查看libcinder。 - Marco van de Voort

1
我选择采用简单的方法。将纹理分成可变高度的行。第一个放置在行中的纹理决定了该行的高度。如果一种纹理可以通过高度适应现有行,则检查是否还有足够的宽度并将其放置在那里。否则开始新的一行。如果无法启动新行,则不缓存字符串。

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