精简版:
OpenGL 中是否有一种通用方法,当以原生尺寸(包括边缘像素)绘制时,允许从图集绘制像素完美的 2D 纹理,并在过滤到非原生尺寸时实现高质量缩放?
有关该问题的更多详细信息:
假设您正在编写某种用于从纹理图集中绘制精灵的系统,这样用户就可以指定精灵,以及要绘制它的大小和位置。此外,他们可能只想绘制精灵的子矩形。
例如,这是一个 64x64 的棋盘:
...并且在其纹理图集中单独存在:
注意:我们假设视口设置为将 1:1 映射到设备像素。
方法
为清楚起见进行编辑:请注意,下面的前 2 种方法没有给出预期的结果,并且专门用于概述哪些方法不起作用。
1. 要以原生大小绘制精灵,只需使用 GL_NEAREST
- 将 OpenGL 设置为使用 GL_NEAREST min/mag 过滤
- 在位置 (x,y) 绘制,将顶点从 (x,y) 放置到 (x+64,y+64)
- 使用纹理坐标 (0,0) -> (64/atlas_size,64/atlas_size)
这对于以原始尺寸绘制来说很好,但是当用户以 64x64 以外的尺寸绘制对象时,NEAREST 过滤会产生较差的结果。它还强制纹素与像素网格对齐,当对象在非整数像素位置绘制时效果不佳:
2.启用GL_LINEAR
- 使用线性过滤,我们需要将我们的 uv 坐标移动到纹素的中心:
(0.5/atlas_size,0.5/atlas_size)
到(63.5/atlas_size,63.5/atlas_size)
. 否则,对于最外层的像素,线性过滤将在图集中采样精灵的邻居。 - 然后我们还需要修改我们的顶点,因为继续使用从 (0,0) 到 (64,64) 的顶点将在两个方向上将纹理拉伸 1px,如下所示:
- 因此,我们需要使用从 (0.5,0.5) 到 (63.5,63.5) 的顶点。通常,当纹理以其原始大小 (a,b) 的比例绘制时,我们需要将顶点“向内”移动,我相信,(a/2,b/2)。给出以下结果(在紫色背景上):
请注意,我们得到了像素精确的绘图,除了与背景混合的边缘像素,因为顶点边界位于像素之间的中间。
编辑:另请注意,此行为还取决于是否启用了抗锯齿。如果不是,前面的方法实际上确实提供了完美的像素渲染,但是当精灵从像素对齐位置移动到子像素位置时,并没有提供良好的过渡。加上抗锯齿在许多情况下是必须的。
3. 在纹理图集中填充精灵
边缘像素问题的两个明显解决方案包括在纹理图集中用 1px 边框填充精灵的边缘。您可以:
- 用 1 层透明像素填充,并将顶点扩展(0.5a,0.5b) 而不是收缩它们
- 用精灵最外层像素的第二个副本填充,然后返回从 (0,0) 到 (64/atlas_size,64/atlas_size) 的纹理采样
这基本上为我们提供了具有线性缩放的像素精确绘图。但是,如果我们随后允许用户仅绘制精灵的子矩形,那么这些方法中的任何一种都会失败,因为子矩形显然没有所需的填充。
我错过了什么吗?这个问题有一个通用的解决方案吗?