Jak uniknąć „efektu schodów” w ruchu sztuki pikseli?

21

Renderuję duszki z dokładnymi współrzędnymi pikseli, aby uniknąć efektu rozmycia spowodowanego wygładzaniem krawędzi (duszki są pikselami i wyglądałyby okropnie, gdyby były filtrowane). Ponieważ jednak ruch obiektów obejmuje zmienną prędkość, grawitację i interakcje fizyczne, trajektoria jest obliczana z precyzją subpikseli.

Przy wystarczająco dużych prędkościach przestrzeni ekranu (vΔt większych niż 2 lub 3 piksele) działa to bardzo dobrze. Jednak gdy prędkość jest mała, może pojawić się zauważalny efekt schodowy, szczególnie wzdłuż linii ukośnych. Nie jest to już problemem przy bardzo niskich prędkościach przestrzeni ekranu (v << 1 piksel na sekundę), więc szukam tylko rozwiązania dla średnich wartości prędkości.

Po lewej stronie jest wykreślona trajektoria dla dużej prędkości, uzyskana przez proste zaokrąglenie współrzędnych obiektu. Na środku widać, co dzieje się, gdy prędkość staje się mniejsza, i efekt schodów, o którym mówię. Po prawej stronie chciałbym uzyskać miejsce trajektorii.

współrzędne pikseli dla trajektorii obiektu

Interesują mnie pomysły algorytmów do filtrowania trajektorii w celu zminimalizowania aliasingu, przy jednoczesnym zachowaniu oryginalnego zachowania przy dużych i małych prędkościach. Mam dostęp do Δt, natychmiastowej pozycji i prędkości, a także dowolnej liczby poprzednich wartości, ale ponieważ jest to symulacja w czasie rzeczywistym, nie wiem o przyszłych wartościach (choć w razie potrzeby oszacowanie można ekstrapolować przy pewnych założeniach) . Pamiętaj, że z powodu symulacji fizyki mogą również wystąpić nagłe zmiany kierunku.

sam hocevar
źródło

Odpowiedzi:

18

Oto krótki zarys algorytmu, który powinien działać całkiem dobrze.

  1. Najpierw oblicz kierunek, w którym porusza się obiekt, i sprawdź, czy jest bliżej poziomej czy pionowej.
  2. Jeśli kierunek jest bliższy pionowi (poziomo), dostosuj pozycję obiektu wzdłuż wektora kierunku do środka najbliższego rzędu pikseli (kolumny).
  3. Zaokrąglij pozycję do środka najbliższego piksela.

W pseudokodzie:

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);
}

Edycja: Tak, przetestowane, działa całkiem nieźle.

Ilmari Karonen
źródło
+1, działa zaskakująco dobrze! Zauważyłem dziwne skoki do tyłu z ruchem kołowym przy niskiej prędkości, ponieważ regulacji można dokonać w kierunku przeciwnym do wektora prędkości (co zwykle jest OK, ale nie przy małych krzywiznach trajektorii). Można to rozwiązać, mnożąc velocity.y / velocity.xwspółczynnik korekcji proporcjonalny do prędkości.
sam hocevar
@Sam: Masz na myśli mały promień skrętu (= wysoka krzywizna), prawda? To może rzeczywiście powodować problemy z ekstrapolacją liniową przy niskich prędkościach. (Zasadniczo działa tak długo, jak prędkość kwadratowa na przyspieszenie jest znacznie większa niż 1 piksel.) Jednym z możliwych (klugey) rozwiązań może być zapamiętanie ostatniej zaokrąglonej pozycji i ponowne użycie jej, jeśli jest ona bliższa prawdziwej pozycji niż nowo obliczona. (Można również spróbować ekstrapolacji wyższego rzędu, ale formuły stają się dość brzydkie.)
Ilmari Karonen
Rzeczywiście, miałem na myśli mały promień. Mój błąd. I dzięki za dodatkowe wskazówki; wydajność nie ma tam krytycznego znaczenia, więc mogę sobie pozwolić na poprawę jakości.
sam hocevar,
3

W świecie opartym na fizyce niewiele można tak naprawdę zrobić. Gdyby wszystkie twoje obiekty poruszały się wzdłuż linii lub określonych kręgów, możesz coś zrobić. Ale działasz w oparciu o rzeczywistą fizykę. Obiekt jest tam, gdzie umieszcza go fizyka; po prostu rysujesz przybliżoną lokalizację w pikselach.

Jest to na ogół coś, co musisz zaakceptować, jeśli chcesz trzymać się współrzędnych pikseli. Nie powinno to być zbyt zauważalne, chyba że wyświetlasz w niewiarygodnie małej rozdzielczości (mniejszej niż 640 x 480, choć zależy to od natywnej rozdzielczości i rozmiaru wyświetlacza).

Nicol Bolas
źródło
Nawet w wysokich rozdzielczościach renderowanie jest zwiększane (najbliższy sąsiad), aby poprawić wygląd oldschool. To decyzja dotycząca kierunku artystycznego.
sam hocevar
@SamHocevar: Jeśli chcesz mieć „oldschoolowy wygląd”, dlaczego nie chcesz mieć pełnego „oldschoolowego wyglądu”? Dlaczego wchodzenie po schodach, które miałaby każda gra „oldschoolowa”, nie jest częścią ogólnego efektu, jaki chcesz osiągnąć?
Nicol Bolas
Nie sądzę, że jakakolwiek przyzwoita gra oldschoolowa wprowadziłaby ruch po przekątnej, który miałby ten efekt schodowy, ponieważ wyglądałby jak bzdura. Nie wyglądanie jak bzdury to główna część efektu starej szkoły, którą chcę osiągnąć :-)
sam hocevar
@SamHocevar: Większość gier oldschoolowych to gry akcji, dlatego nie poruszaj się wystarczająco wolno, aby to zauważyć. Mają też tendencję do nie poruszania się po zakrętach. W szczególności gra, o której myślałem, to Solar Jetman, który ma bardzo duży wpływ na powolny ruch. To prawda, że ​​kamera jest zawsze wyśrodkowana na tobie, więc zauważasz ją w ruchu na całym świecie, ale bardzo tam jest.
Nicol Bolas
3

Gdy oczekujący ruch jest prostopadły do ​​ostatniego ruchu (w przestrzeni ekranu), zignoruj ​​go i użyj ostatnich współrzędnych ekranu. Jeśli prowadzi to do jąkania, które jest tak złe jak schody, możesz spróbować przesunąć sumę oczekującego i ostatniego ruchu.

Myślę, że problem leży w v <sqrt (2). v> sqrt (2) zawsze powinien przesuwać się przynajmniej o pełną przekątną, unikając efektu schodów. Może przydatny do przycinania, które wymagają wcześniejszych porównań ruchów.

mickicks
źródło
+1 za wskazanie górnej granicy dla sugestii w. Ilmari jest bardziej szczegółowe, ale podajesz przydatne informacje.
sam hocevar