Як можна уникнути "ефекту сходів" у русі піксельної графіки?


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на поправочний коефіцієнт, пропорційний швидкості.
sam hocevar

@Sam: Ви маєте на увазі малий радіус повороту (= висока кривизна), правда? Це дійсно могло б викликати проблеми з лінійною екстраполяцією при малих швидкостях. (В основному, він працює до тих пір, поки швидкість у квадраті на прискорення набагато більша, ніж 1 піксель.) Одним з можливих рішень (klugey) може бути запам’ятовування останньої округлої позиції та повторне її використання, якщо воно ближче до справжнього положення, ніж новоспечене. (Можна також спробувати екстраполяцію вищого порядку, але формули стають досить потворними.)
Ільмарі Каронен

Дійсно, я мав на увазі малий радіус. Моє ліжко. І дякую за додаткові підказки; продуктивність там не є критичною, тому я можу дозволити собі покращити якість.
sam hocevar

3

Насправді з цим робити не так багато для загального фізичного світу. Якщо всі ваші об’єкти рухалися по лініях або певних колах, ви могли щось зробити. Але ти працюєш за фактичною фізикою. Об'єкт - там, де його ставить фізика; ви просто малюєте наближення пікселя до цього місця.

Це взагалі щось, що ви повинні прийняти, якщо хочете дотримуватися піксельних координат. Це не повинно бути надто помітним, якщо ви не відображаєтеся з неймовірно невеликою роздільною здатністю (менше 640x480, хоча це залежить від назви роздільної здатності та розміру дисплея).


Навіть при високій роздільній здатності візуалізація збільшується (найближчий сусід) для покращення зовнішнього вигляду старої школи. Це рішення художнього спрямування.
sam hocevar

@SamHocevar: Якщо ви хочете "старий вигляд", чому б вам не хотілося повноцінного "зовнішнього вигляду"? Чому ступінчаста сходи, яку мала би будь-яка гра "oldschool", не є частиною загального ефекту, якого ви хочете досягти?
Нікол Болас

Я не думаю, що будь-яка порядна гра в старій школі могла б реалізувати діагональний рух, що має такий східний ефект, тому що це було б схоже на лайно. Не схожий на лайно - це головна частина ефекту
старої школи, якого

@SamHocevar: Більшість ігор для старих шкіл - це екшн, тому не рухайтеся досить повільно, щоб помітити. Вони також мають тенденцію не рухатися по кривих. Гра, зокрема, про яку я думав, - Solar Jetman, що дуже сильно впливає на рух. Зрозуміло, камера завжди зосереджена на вас, тому ви помічаєте її у світовому русі, але він дуже великий.
Нікол Болас

3

Коли відкладений рух перпендикулярний останньому руху (в екрані), ігноруйте його та використовуйте останні координати екрана. Якщо це призведе до заїкання, яке так само погано, як сходи, ви можете спробувати перемістити суму очікуваного та останнього руху.

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


+1 для вказівки верхньої межі для пропозиції проти Ілмарі більш детально, але ви надаєте корисну інформацію.
sam hocevar
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.