渲染像素艺术

如果您曾经尝试用像素艺术制作游戏,也许您就遇到了这个问题:当以更高的分辨率显示时,如何使低分辨率的像素艺术显得清晰而干净? 图形需要按比例放大,因此需要某种插值。 插值的最常见类型是“最近”和“线性”,但假定非整数比例因子,它们将分别使您的漂亮像素画看起来失真或模糊。 渲染旋转的精灵具有相同的问题。 有几种方法可以解决此问题。 在这篇文章中,我将描述我对这个问题的亲身经历,以及解决问题的方式随着时间的变化。

在我的早期游戏中,我采取了最简单的方法,将游戏窗口强制为特定的预定分辨率,并以预定的整数比例因子缩放所有图像。 当然,这是非常有限的。 它没有利用整个设备的显示,并且尝试任意缩放和旋转精灵会导致上述图形伪像。

当我在2014年开始研究无武器武器时,我知道我需要一个更灵活的解决方案。 我的第一个想法是可以使用某种着色器,但是我是OpenGL的新手,完全不熟悉其他图形库,因此OpenGL着色器语言吓倒了我。 为了使事情变得更复杂,我使用了Java和libGDX,但是学习OpenGL的大多数教程都是C / C ++。 因此我想出了另一种不涉及编写着色器的解决方案。 首先,我将使用最近的插值将所有图像文件缩放200%,覆盖旧图像文件。 然后,在渲染这些图像时,我将使用线性插值。 组合效果介于最近插值和线性插值之间,因此我将其称为“混合”插值。 看起来还可以接受,尽管仍然有些模糊。 同样,图像占用的内存当然是所需数量的4倍,实际上这并不是什么大问题。

后来,我选择的语言从使用Java和libGDX转变为使用Rust和SDL2。 在这一点上,我认为现在是时候该学习如何正确编写OpenGL着色器了。 SDL2与OpenGL可以很好地互操作,使用户可以选择直接调用所有OpenGL方法。 我完成了有关制作简单着色器以渲染纹理的教程。 然后,我环顾四周,看看是否可以找到可以清晰地渲染像素艺术的着色器。 我偶然发现了Wikipedia关于像素艺术缩放的页面,但是提到的着色器旨在使像素艺术看起来不像像素艺术,这不是我想要的。

掌握了着色器语言的基础知识之后,我决定看看我自己可以想到的东西。 解决此类问题的第一步是定义所需的输出。 如果我们想按3.5倍的比例缩放子画面,理想情况下应为每个像素值分配什么颜色? 我决定图像应该采用与使用最近插值进行超级采样时相同的颜色。 基本思想是,不是对每个屏幕像素采样一种颜色,而是对每个屏幕像素内不同位置的几种颜色进行采样,然后对这些颜色求平均。 这是非常昂贵的,但结果看起来不错。 如果您想在不编写着色器的情况下进行尝试,请在绘画程序中打开一些像素图,使用最近/无插值将其缩放1600%,然后使用线性插值将其缩放22%。

在我们的情况下,过多的超级采样是多余的。 将这个问题想象成两个彼此重叠的栅格:像素(纹理像素)重叠在像素(屏幕像素)上。 通常,我们期望纹理像素比像素大得多。 如果我们暂时忽略旋转的可能性,并假定比例系数大于1.0,则每个像素最多重叠4个像素,然后仅在像素的一个角上重叠。 因此,我们只需要对每个像素采样4次纹理,并计算这些颜色的加权平均值。

因此,这是我最初的方法,但每个像素甚至4个样本似乎都比必要的成本高。 我想知道是否可以改进它,但是经过更多考虑之后,我确信对四个相邻纹理像素进行加权平均绝对是必要的。 然后我有了一个顿悟的时刻:我意识到OpenGL中有一个内置的机制可以对4个相邻纹理像素进行加权平均,这就是线性插值! 乍一看,这似乎有点违反直觉,但是可以在仔细调整采样位置的同时使用线性插值,以产生看起来像具有超级采样的最近插值的结果。

这适用于任意缩放,但是任意旋转呢? 好吧,尽管将其应用于旋转时不再是超级采样效果的精确复制,但它是非常接近的近似值。 可以将相同的着色器代码用于旋转,并且很难注意到任何缺陷。

我编写的代码是我的开源Gate库的一部分。 片段着色器的当前版本位于此处。 但是,我从OpenGL中学到的一件事是不应孤立地考虑着色器。 重要的是要知道对OpenGL设置,纹理以及如何填充顶点缓冲区的期望。 这篇博客文章并不意味着要成为一个教程,鉴于所有的细微之处,我将不在这里解释着色器代码。 如果您对所有细节都感兴趣,则可以浏览我的代码。 但是考虑到周围的期望,我会说OpenGL设置为使用线性插值,预先乘以alpha值并使用适当的混合函数,纹理是一个精灵图集,在两个精灵之间填充了2个像素,每个精灵用1个像素的填充物绘制以说明采样坐标的调整。

在我编写了此着色器并注意到代码结束了多短之后,我决定其他人也必须做类似的事情。 因此,我搜索了更多内容,然后遇到了一篇博客文章,其中描述了一个着色器,以实现与我完全相同的效果,尽管算法有所不同。 我发现有趣的是,我如何从不同的角度得出相同的结果:尝试在不增加超级采样成本的情况下实现超级采样效果。

关于渲染像素艺术,这几乎就是我要说的。 我还想谈谈我如何处理拼贴问题。 如果不小心,则在绘制瓷砖网格时可能会出现图形伪像。 但是我想我会将讨论内容保存在将来的博客文章中。