如何以子像素为单位移动精灵?


11

像素打开或关闭。您可以移动精灵的最小数量是一个像素。那么如何使子画面每帧移动速度慢于1像素?

我这样做的方法是将速度添加到变量中,然后测试速度是否达到1(或-1)。如果确实如此,那么我将移动精灵并将变量重置为0,如下所示:

update(dt):
    temp_dx += speed * dt
    temp_dy += speed * dt

    if (temp_dx > 1)
        move sprite
        reset temp_dx to 0
    if (tempy_dy > 1)
        move sprite
        reset temp_dy to 0

我不喜欢这种方法,因为它感觉很傻,而且精灵的动作看起来非常生涩。那么,您将以哪种方式实现亚像素移动?


您唯一可以做的就是双线性过滤。
AturSams

如果您每帧移动的像素少于1像素,它看起来会变得生涩吗?

Answers:


17

有很多选择:

  1. 照做。您已经说过它看起来并不流畅。但是,您当前的方法存在一些缺陷。对于x,您可以使用以下代码:

    tempx += speed * dt
    
    while (tempx > 0.5)
      move sprite to x+1
      tempx -= 1
    while (tempx < -0.5)
      move sprite to x-1
      tempx += 1

    这应该更好。我已将if陈述改为使用0.5,因为当您传递0.5时,您将比前一个更接近下一个值。我已经使用了while循环来允许每个时间步长移动超过1个像素(这不是最好的方法,但是它使代码紧凑且可读)。对于慢速移动的物体,它仍然会显得有些跳动,因为我们还没有处理像素对齐的基本问题。

  2. 为子画面设置多个图形,并根据子像素偏移使用不同的图形。例如,要仅在x中进行亚像素平滑,可以在处为精灵创建图形x+0.5,如果位置在x+0.25和之间x+0.75,请使用该精灵代替原始图片。如果要更好地定位,只需创建更多的亚像素图形即可。如果你这样做的xy你渲染的数量迅速膨胀,与方形间隔数的数量鳞的间距0.5将需要4个渲染,0.25将需要16。

  3. 超样品。这是创建具有亚像素分辨率的图像的一种惰性方法(并且可能非常昂贵)。本质上,将渲染场景的分辨率提高一倍(或更高),然后在运行时将其按比例缩小。我会在这里建议您小心,因为性能可能会迅速下降。较不激进的方法是对Sprite进行超采样,然后在运行时将其缩小。

  4. 正如Zehelvion所建议的,可能是您所使用的平台已支持此功能。如果允许您指定非整数的x和y坐标,则可能有一些选项可以更改纹理过滤。默认情况下,最接近的邻居通常是默认邻居,这将导致“生涩”运动。其他滤波(线性/三次)将导致更平滑的效果。

2. 3.和4.之间的选择完全取决于图形的实现方式。图形的位图样式更适合预渲染,而矢量样式可能适合子图形超级采样。如果您的系统支持,则很可能是4.。


为什么在选项1中将阈值替换为0.5?我也不明白为什么将语句移动到while循环中。每更新一次,就会调用一次更新功能。
bzzr

1
好问题-我已根据您的评论澄清了答案。
Jez 2014年

超级采样可能是“懒惰的”,但是在很多情况下,2倍甚至4倍过采样的性能成本并不是一个特别的问题,特别是如果它允许简化渲染的其他方面。
2014年

在大型预算游戏中,我通常会在图形设置中看到3作为抗锯齿的选项。
凯文(Kevin)

1
@Kevin:您实际上可能没有。大多数游戏使用多重采样,这有些不同。也通常使用其他变体,例如具有变化的覆盖范围和深度样本。由于执行片段着色器的成本,几乎从未进行过超级采样。
imallett 2014年

14

许多老派游戏解决(或隐藏)此问题的一种方法是使Sprite动画化。

就是说,如果您的精灵每帧移动少于一个像素(或者,特别是如果像素/帧比率将变得奇怪,例如3帧中有2个像素),则可以通过设置n帧动画循环,在那n帧上,最终使子画面移动了k < n个像素。

关键是,只要子画面始终在每个帧中以某种方式移动,就永远不会出现任何一个帧,整个子画面会突然“急速”向前移动。


我无法从旧的视频游戏中找到一个真实的精灵来说明这一点(尽管我认为例如Lemmings的一些挖掘动画就是这样),但事实证明,Conway的《生活游戏中的“滑翔机”模式使非常好的插图:

在此处输入图片说明
动画Kieff / 维基共享资源,下使用CC-BY-SA 3.0许可。

在这里,滑行的黑色像素小块向右下方爬行。如果仔细看,您会注意到它们需要四个动画帧以对角线方向爬行一个像素,但是由于它们在每个帧上都以某种方式移动,因此移动看起来不会那么生涩(嗯,至少现在不再如此)无论如何,在任何情况下,该帧速率都比任何东西都快。


2
如果速度是固定的,或者每帧小于1/2像素,或者动画“足够大”且足够快以遮盖运动的变化,则这是一种极好的方法。
2014年

这是一个很好的方法,尽管如果相机必须遵循精灵,您仍然会遇到问题,因为它无法平稳移动。
Elden Abob 2014年

6

子画面的位置应保留为浮点数,并仅在显示之前舍入为整数。

您还可以将子画面保持为超分辨率,并在显示之前对子画面进行降采样。如果将子画面保持在3倍的显示分辨率,则根据子画面的子像素位置,将有9种不同的实际子画面。


3

唯一真正的解决方案是使用双线性滤波。想法是让GPU根据与之重叠的四个Sprite像素计算每个像素的值。这是一种常见且有效的技术。您只需要将精灵作为纹理放置在2d平面(广告牌)上即可;然后使用GPU渲染这些平原。这种方法效果很好,但是如果您打算这样做的话,期望得到的结果有些模糊,并且会丢失8位或16位外观。

优点:

已经实现,非常快速的基于硬件的解决方案。

缺点:

丢失8位/ 16位保真度。


1

浮点数是这样做的正常方法,尤其是当您最终渲染到GL目标时,硬件对带有浮点坐标的阴影多边形非常满意时,尤其如此。

但是,还有另一种方式可以完成当前的工作,但会比较不稳定:定点。32位位置值可以表示0到4,294,967,295的值,即使您的屏幕几乎可以确定沿任一轴的像素少于4k。因此,在中间放一个固定的“二进制点”(类似于小数点),就可以用0至65536的像素位置表示另外16位的子像素分辨率。然后,您可以像往常一样添加数字,只需要记住在转换为屏幕空间或将两个数字相乘时,通过右移16位来进行转换。

(当我有没有浮点单元的Intel 386的时候,我用16位固定点编写了3D引擎。它实现了每帧200个Phong阴影多边形的盲目速度。)


1

除了此处的其他答案外,您还可以在某种程度上使用抖动。抖动是对象像素边缘的颜色较浅/较暗,以匹配背景,从而使边缘更柔和。例如,假设您有一个4像素的正方形,我将与之接近:

OO
OO

如果将这个1/2像素向右移动,则什么也不会真正移动。但是,通过一些抖动,您可能会有点运动的错觉。将O视为黑色,将o视为灰色,因此您可以这样做:

oOo
oOo

一帧和

 OO
 OO

下一个。带有灰色边缘的实际“正方形”实际上需要3个像素,但是由于它们的灰色比黑色的浅,因此显得较小。但是,这可能很难编写代码,而且我现在还真的不知道有什么可以做到这一点。大约在他们开始使用抖动处理事物的时候,分辨率很快变得更好,除了图像处理之类的事情之外,没有太多的需求了。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.