¿Cómo se evita el "efecto escalera" en el movimiento de pixel art?

21

Estoy renderizando sprites en coordenadas de píxeles exactas para evitar el efecto de desenfoque causado por el antialiasing (los sprites son pixel art y se verían horribles si se filtran). Sin embargo, dado que el movimiento de los objetos involucra velocidad variable, gravedad e interacciones físicas, la trayectoria se calcula con precisión de subpíxeles.

A velocidades de espacio de pantalla suficientemente grandes (vΔt mayores de 2 o 3 píxeles) esto funciona muy bien. Sin embargo, cuando la velocidad es pequeña, puede aparecer un efecto de escalera notable, especialmente a lo largo de líneas diagonales. Esto ya no es un problema a velocidades de espacio de pantalla muy lentas (v << 1 píxel por segundo), por lo que solo estoy buscando una solución para valores de velocidad intermedios.

A la izquierda está la trayectoria trazada para una gran velocidad, obtenida por simple redondeo de las coordenadas del objeto. En el centro puedes ver lo que sucede cuando la velocidad se vuelve más pequeña y el efecto de escalera del que estoy hablando. A la derecha, el lugar de la trayectoria que me gustaría obtener.

coordenadas de píxeles para la trayectoria del objeto

Estoy interesado en ideas de algoritmos para filtrar la trayectoria a fin de minimizar el alias, mientras se conserva el comportamiento original a velocidades grandes y pequeñas. Tengo acceso a Δt, posición instantánea y velocidad, así como a un número arbitrario de valores anteriores, pero dado que es una simulación en tiempo real, no sé acerca de los valores futuros (aunque si es necesario, una estimación podría extrapolarse bajo ciertos supuestos) . Tenga en cuenta que debido a la simulación física, también pueden ocurrir cambios repentinos de dirección.

sam hocevar
fuente

Respuestas:

18

Aquí hay un resumen rápido, fuera de mi cabeza, de un algoritmo que debería funcionar razonablemente bien.

  1. Primero, calcule la dirección en que se mueve el objeto y verifique si está más cerca de la horizontal o vertical.
  2. Si la dirección está más cerca de la vertical (horizontal), ajuste la posición del objeto a lo largo del vector de dirección al centro de la fila (columna) de píxeles más cercana.
  3. Redondea la posición al centro del píxel más cercano.

En pseudocódigo:

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

Editar: Sí, probado, funciona bastante bien.

Ilmari Karonen
fuente
+1, ¡esto funciona sorprendentemente bien! Noto saltos hacia atrás extraños con movimiento circular a velocidades lentas, porque el ajuste se puede hacer en la dirección opuesta al vector de velocidad (que generalmente está bien, pero no con pequeñas curvaturas de trayectoria). Eso se puede resolver multiplicando velocity.y / velocity.xpor un factor de corrección proporcional a la velocidad.
sam hocevar
@Sam: ¿Te refieres a un pequeño radio de giro (= alta curvatura), ¿verdad? De hecho, eso podría causar problemas con la extrapolación lineal a bajas velocidades. (Básicamente, funciona siempre que la velocidad al cuadrado por aceleración sea mucho mayor que 1 píxel). Una posible solución (klugey) podría ser recordar la última posición redondeada y reutilizarla si está más cerca de la posición verdadera que la recién calculada. (También se podría intentar una extrapolación de orden superior, pero las fórmulas se ponen bastante feas.)
Ilmari Karonen
De hecho, quise decir pequeño radio. Culpa mía. Y gracias por las sugerencias adicionales; el rendimiento no es crítico allí, así que puedo permitirme mejorar la calidad.
Sam Hocevar
3

No hay mucho que puedas hacer al respecto para un mundo basado en la física general. Si todos sus objetos se movieran a lo largo de líneas o círculos específicos, podría hacer algo. Pero estás operando bajo la física real. El objeto es donde lo pone la física; simplemente está dibujando una aproximación basada en píxeles de esa ubicación.

En general, es algo que debe aceptar si desea seguir con las coordenadas de píxeles. No debería ser demasiado notable a menos que esté mostrando una resolución increíblemente pequeña (menos de 640x480, aunque depende de la resolución y el tamaño nativos de la pantalla).

Nicol Bolas
fuente
Incluso a altas resoluciones, el renderizado se mejora (el vecino más cercano) para mejorar la apariencia de la vieja escuela. Esta es una decisión de dirección artística.
Sam Hocevar
@SamHocevar: Si desea una "apariencia de la vieja escuela", ¿por qué no quieres un completo "de la vieja escuela apariencia"? ¿Por qué el escalón, que habría tenido cualquier juego de la "vieja escuela", no forma parte del efecto general que quieres lograr?
Nicol Bolas
No creo que ningún juego decente de la vieja escuela hubiera implementado un movimiento diagonal que tenga ese efecto de escalera, porque habría parecido una mierda. No parecer basura es una parte importante del efecto de la vieja escuela que deseo lograr :-)
sam hocevar
@SamHocevar: La mayoría de los juegos de la vieja escuela son juegos de acción y, por lo tanto, no se mueven lo suficientemente lento como para darse cuenta. También tienden a no moverse a lo largo de las curvas. El juego en particular en el que estaba pensando era Solar Jetman, que tiene mucho efecto cuando se mueve lentamente. Por supuesto, la cámara siempre está centrada en ti, por lo que lo notas en movimiento mundial, pero está ahí.
Nicol Bolas
3

Cuando el movimiento pendiente es perpendicular al último movimiento (en el espacio de la pantalla), ignórelo y use las últimas coordenadas de la pantalla. Si eso provoca un tartamudeo tan malo como la escalera, puede intentar mover la suma del último movimiento pendiente.

Creo que el problema radica en v <sqrt (2). v> sqrt (2) siempre debe moverse al menos una diagonal completa, evitando el efecto de escalera. Quizás sea útil para la poda que necesita las comparaciones de movimientos previos.

mghicks
fuente
+1 para señalar un límite superior para la sugerencia de v. Ilmari es más detallada pero está proporcionando información útil.
sam hocevar