Как избежать «эффекта лестницы» в движении пиксельной графики?

21

Я рендерил спрайты с точными пиксельными координатами, чтобы избежать эффекта размытия, вызванного сглаживанием (спрайты являются пиксельной графикой и выглядят ужасно, если их отфильтровать). Однако, поскольку движение объектов включает переменную скорость, гравитацию и физические взаимодействия, траектория вычисляется с точностью до субпикселя.

При достаточно больших скоростях пространства экрана (vΔt больше, чем 2 или 3 пикселя) это работает очень хорошо. Однако, когда скорость мала, может появиться заметный эффект лестницы, особенно вдоль диагональных линий. Это больше не проблема при очень медленных скоростях пространства экрана (v << 1 пиксель в секунду), поэтому я ищу только решение для промежуточных значений скорости.

Слева - построенная траектория для большой скорости, полученная простым округлением координат объекта. В середине вы можете увидеть, что происходит, когда скорость становится меньше, и эффект лестницы, о котором я говорю. Справа локус траектории я бы хотел получить.

пиксельные координаты для траектории объекта

Меня интересуют идеи алгоритмов для фильтрации траектории, чтобы минимизировать алиасы, сохраняя при этом исходное поведение при больших и малых скоростях. У меня есть доступ к Δt, мгновенному положению и скорости, а также к произвольному числу предыдущих значений, но, поскольку это симуляция в реальном времени, я не знаю о будущих значениях (хотя при необходимости оценка может быть экстраполирована при определенных предположениях) , Обратите внимание, что из-за физического моделирования могут также произойти внезапные изменения направления.

Сэм Хоцевар
источник

Ответы:

18

Вот быстрый набросок, вне моей головы, алгоритма, который должен работать достаточно хорошо.

  1. Сначала вычислите направление движения объекта и проверьте, ближе ли он к горизонтальному или вертикальному.
  2. Если направление ближе к вертикальному (горизонтальному), отрегулируйте положение объекта вдоль вектора направления относительно центра ближайшей строки пикселей (столбца).
  3. Округлите положение до центра ближайшего пикселя.

В псевдокоде:

if ( abs(velocity.x) > abs(velocity.y) ) {
    x = round(position.x);
    y = round(position.y + (x - position.x) * velocity.y / velocity.x);
} else {
    y = round(position.y);
    x = round(position.x + (y - position.y) * velocity.x / velocity.y);
}

Редактировать: Да, проверено, работает довольно хорошо.

Илмари Каронен
источник
+1, это работает на удивление хорошо! Я замечаю странные скачки назад при круговом движении на медленных скоростях, потому что регулировка может быть выполнена в направлении, противоположном вектору скорости (что обычно нормально, но не с небольшими кривизнами траектории). Это можно решить путем умножения velocity.y / velocity.xна поправочный коэффициент, пропорциональный скорости.
Сэм Хочевар
@ Сэм: Вы имеете в виду небольшой радиус поворота (= высокая кривизна), верно? Это действительно может вызвать проблемы с линейной экстраполяцией при низких скоростях. (По сути, это работает до тех пор, пока квадрат скорости на ускорение намного превышает 1 пиксель.) Одним из возможных (клугейских) решений может быть запоминание последней округленной позиции и ее повторное использование, если она ближе к истинной позиции, чем недавно вычисленная. (Можно также попробовать экстраполяцию высшего порядка, но формулы получаются довольно уродливыми.)
Илмари Каронен,
Действительно, я имел ввиду маленький радиус. Виноват. И спасибо за дополнительные советы; производительность там не критична, поэтому я могу позволить себе улучшить качество.
Сам Хочевар
3

Вы ничего не можете с этим поделать для основанного на физике мира. Если бы все ваши объекты двигались вдоль линий или определенных кругов, вы могли бы что-то сделать. Но вы работаете в реальной физике. Объект - это место, куда его ставит физика; вы просто рисуете пиксельное приближение этого местоположения.

Как правило, это то, что вы должны принять, если хотите придерживаться пиксельных координат. Это не должно быть слишком заметно, если вы не показываете в невероятно маленьком разрешении (менее 640x480, хотя это зависит от собственного разрешения и размера дисплея).

Николь Болас
источник
Даже при высоком разрешении рендеринг масштабируется (ближайший сосед), чтобы улучшить внешний вид старой школы. Это художественное решение.
Сэм Хоцевар
@SamHocevar: Если вы хотите «внешний вид олдскул», почему вы не хотите полный «внешний вид олдскул»? Почему ступенька, которую имела бы любая «олдскульная» игра, не является частью общего эффекта, которого вы хотите достичь?
Николь Болас
Я не думаю, что в какой-нибудь приличной игре для старой школы было бы реализовано диагональное движение с эффектом лестницы, потому что это выглядело бы как дерьмо. Не выглядеть как дерьмо - основная часть эффекта старой школы, которого я хочу достичь :-)
Сэм Хоцевар
@SamHocevar: Большинство игр старой школы - это экшн-игры, поэтому они не двигаются достаточно медленно, чтобы заметить это. Они также имеют тенденцию не двигаться по кривым. В частности, я думал о Solar Jetman, который очень сильно влияет на медленное движение. Конечно, камера всегда сосредоточена на вас, поэтому вы замечаете это в мировом движении, но она очень сильно присутствует.
Николь Болас
3

Когда ожидающее движение перпендикулярно последнему движению (в пространстве экрана), игнорируйте его и используйте последние координаты экрана. Если это приводит к заиканию, так же плохо, как и к лестнице, вы можете попытаться переместить сумму ожидающего и последнего движения.

Я думаю, что проблема заключается в v <sqrt (2). v> sqrt (2) всегда должен двигаться как минимум на полную диагональ, избегая эффекта лестницы. Может быть полезно для обрезки, которые требуют предварительного сравнения движений.

mghicks
источник
+1 за указание на верхнюю границу для v. Предложение Илмари более детально, но вы предоставляете полезную информацию.
Сэм Хоцевар