首页 文章

OpenGL中的2D绘图:线性过滤,具有原始大小的像素精度

提问于
浏览
16

短版:

OpenGL中是否有一种通用的方法,当以原生大小(包括边缘像素)绘制时,允许从图集中完美地2D绘制纹理,并在过滤到非原生大小时进行高质量缩放?

关于问题的更多细节:

假设您正在编写某种用于从纹理图集中绘制精灵的系统,以便用户可以指定精灵,以及绘制精灵的大小和位置 . 另外,他们可能只想绘制精灵的子矩形 .

例如,这是一个64x64棋盘:

checkerboard

......并且单独在其纹理图集中:

checkerboard in texture atlas

注意:我们假设视口设置为将1:1映射到设备像素 .

方法

为清晰起见进行编辑:请注意,下面的前两个方法没有给出所需的结果,并且专门用于概述不起作用的内容 .

1. To draw the sprite at native size, simply use 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过滤会产生较差的结果 . 它还会强制纹理像素与像素网格对齐,在非整数像素位置绘制对象时效果不佳:

drawn with gl_nearest at pixel offset of 0.25

drawn with gl_nearest at pixel offset of 0.5

2. Enable GL_LINEAR

  • 使用线性滤波,我们需要将uv坐标移动到纹素的中心: (0.5/atlas_size,0.5/atlas_size)(63.5/atlas_size,63.5/atlas_size) . 否则,对于最外面的像素,线性过滤将对图集中的精灵的邻居进行采样 .

  • 然后我们还需要修改我们的顶点,因为继续使用从(0,0)到(64,64)的顶点会在两个方向上将纹理拉伸1px,如下所示:

drawn with uv coords shifted in by 0.5 texels, resulting in stretching

  • 因此我们需要使用从(0.5,0.5)到(63.5,63.5)的顶点 . 通常,当纹理以其原始大小(a,b)的比例绘制时,我们需要将我们的顶点“向内”移动,我相信(a / 2,b / 2) . 给出以下结果(在紫色背景上):

drawn with uv shifted in by 0.5 texels, and vertices by 0.5 pixels

请注意,除了与背景混合的边缘像素之外,我们得到像素精确的绘图,因为顶点边界落在像素之间的中间 .

编辑:还要注意,此行为还取决于是否启用了抗锯齿功能 . 如果不是,前一种方法实际上确实提供像素完美渲染,但是当精灵从像素对齐移动到子像素位置时不提供良好的转换 . 在许多情况下,加抗锯齿是必须的 .

3. Pad sprites in the texture atlas

边缘像素问题的两个明显的解决方案涉及在纹理图集中用1px边框填充精灵的边缘 . 你可以:

  • 具有1层透明像素的垫,并将顶点扩展(0.5a,0.5b)而不是收缩它们

  • 用精灵最外面像素的第二个副本填充,然后返回从(0,0)到(64 / atlas_size,64 / atlas_size)的纹理采样

这基本上为我们提供了线性缩放的像素精确绘图 . 但是,如果我们允许用户只绘制精灵的子矩形,则这些方法中的任何一个都会失败,因为子矩阵显然没有所需的填充 .

我错过了什么吗?这个问题有一般的解决方案吗?

1 回答

  • 2

    它正在寻找的是.2406154_ . 如果你有一个64x64精灵,并且想要将其缩放到65x65或63x63,那么任何过滤都不会使它看起来很好 . 当您将抗锯齿投入混合时,请记住多重采样不是超级采样,因此您的纹理不会神奇地变得更柔和的内部 .


    也就是说,真正非最接近的过滤应该可以很好地开箱即用,具有多重采样功能 . 我认为您的GL_LINEAR方法正在走上正轨,但我认为您可能遇到了实施问题 .

    特别是,线性滤波应该沿着纹理边界进行滤波 . 例如,如果您有两个相邻的三角形,就会发生这种情况 . 事实上,你应该期望沿精灵边缘进行线性过滤,这种过滤是正确的事情 .

    您不应该尝试通过调整纹理坐标(以及顶点)来纠正此问题,因为这会错误地缩放纹理上的纹理坐标 . 我建议将纹理坐标钳制到着色器中所需的范围加/减0.5 / texture_res .

    你会发现这解决了这个问题在原始缩放和高质量放大率下的像素完美结果 . 缩小看起来没问题,但我建议使用三线性(mipmap)过滤以获得最佳效果 .

相关问题