Como evitar o "efeito escada" no movimento de pixel art?

21

Estou renderizando sprites nas coordenadas exatas de pixels para evitar o efeito de desfoque causado pelo antialiasing (os sprites são pixel-art e ficariam horríveis se filtrados). No entanto, como o movimento dos objetos envolve velocidade variável, gravidade e interações físicas, a trajetória é calculada com precisão de subpixel.

Em velocidades de espaço na tela grandes o suficiente (vΔt maior que 2 ou 3 pixels), isso funciona muito bem. No entanto, quando a velocidade é pequena, um efeito notável da escada pode aparecer, especialmente ao longo das linhas diagonais. Isso não é mais um problema em velocidades de espaço de tela muito lentas (v << 1 pixel por segundo), portanto, estou apenas procurando uma solução para valores de velocidade intermediários.

À esquerda, encontra-se a trajetória plotada para uma grande velocidade, obtida pelo simples arredondamento das coordenadas do objeto. No meio, você pode ver o que acontece quando a velocidade diminui e o efeito da escada de que estou falando. À direita, o lugar da trajetória que eu gostaria de obter.

coordenadas de pixel para a trajetória do objeto

Estou interessado em idéias de algoritmos para filtrar a trajetória, a fim de minimizar o aliasing, mantendo o comportamento original em velocidades grandes e pequenas. Eu tenho acesso a Δt, posição instantânea e velocidade, além de um número arbitrário de valores anteriores, mas como é uma simulação em tempo real, não conheço valores futuros (embora, se necessário, uma estimativa possa ser extrapolada sob certas suposições) . Observe que, devido à simulação física, mudanças repentinas de direção também podem ocorrer.

sam hocevar
fonte

Respostas:

18

Aqui está um resumo rápido, em cima da minha cabeça, de um algoritmo que deve funcionar razoavelmente bem.

  1. Primeiro, calcule a direção em que o objeto está se movendo e verifique se está mais próximo da horizontal ou vertical.
  2. Se a direção estiver mais próxima da vertical (horizontal), ajuste a posição do objeto ao longo do vetor de direção para o centro da linha de pixels mais próxima (coluna).
  3. Arredonde a posição para o centro do pixel mais próximo.

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

Edit: Sim, testado, funciona muito bem.

Ilmari Karonen
fonte
+1, isso funciona surpreendentemente bem! Percebo saltos estranhos para trás com movimento circular em velocidades lentas, porque o ajuste pode ser feito na direção oposta ao vetor de velocidade (que geralmente é bom, mas não com pequenas curvaturas de trajetória). Isso pode ser resolvido multiplicando-se velocity.y / velocity.xpor um fator de correção proporcional à velocidade.
111311 Sam's Hocevar
@ Sam: Você quer dizer pequeno raio de viragem (= alta curvatura), certo? Isso poderia realmente causar problemas com a extrapolação linear em baixas velocidades. (Basicamente, funciona desde que a velocidade ao quadrado por aceleração seja muito maior que 1 pixel.) Uma solução possível (klugey) pode ser lembrar a última posição arredondada e reutilizá-la se estiver mais próxima da posição verdadeira do que a recém-calculada. (One também pode tentar de ordem superior extrapolação, mas as fórmulas ficar muito feio.)
Ilmari Karonen
Na verdade, eu quis dizer pequeno raio. Minha culpa. E obrigado pelas dicas adicionais; o desempenho não é crítico lá, então posso me dar ao luxo de melhorar a qualidade.
21711 Samu Hocevar
3

Não há muito o que você possa fazer sobre isso em um mundo geral baseado na física. Se todos os seus objetos estivessem se movendo ao longo de linhas ou círculos específicos, você poderia fazer algo. Mas você está operando sob a física real. O objeto é onde a física o coloca; você está simplesmente desenhando uma aproximação baseada em pixel desse local.

Geralmente, é algo que você deve aceitar se quiser manter as coordenadas de pixel. Não deve ser muito perceptível, a menos que você esteja exibindo em uma resolução incrivelmente pequena (inferior a 640x480, embora dependa da resolução e tamanho nativos da tela).

Nicol Bolas
fonte
Mesmo em altas resoluções, a renderização é aumentada (vizinho mais próximo) para melhorar a aparência da velha escola. Esta é uma decisão de direção artística.
21811 Sam Hocevar
@ SamHocevar: Se você quer uma "aparência oldschool", por que não quer uma "aparência oldschool" completa ? Por que o degrau da escada, que qualquer jogo da "velha escola" teria, não faz parte do efeito geral que você deseja alcançar?
Nicol Bolas 21/10
Eu não acho que nenhum jogo decente da velha escola implementaria um movimento diagonal que tem esse efeito de escada, porque teria parecido uma porcaria. Não olhando como lixo é uma parte importante do efeito oldschool I deseja alcançar :-)
Sam Hocevar
@ SamHocevar: A maioria dos jogos da velha escola são jogos de ação e, portanto, não se movem devagar o suficiente para serem notados. Eles também tendem a não se mover ao longo das curvas. O jogo em particular em que eu estava pensando era no Solar Jetman, que tem esse efeito quando se move lentamente. É verdade que a câmera está sempre centrada em você, então você a percebe no movimento mundial, mas ela está lá.
Nicol Bolas 21/10
3

Quando o movimento pendente é perpendicular ao último movimento (no espaço da tela), ignore-o e use as últimas coordenadas da tela. Se isso levar à gagueira tão ruim quanto a escada, tente mover a soma do último e do movimento pendente.

Eu acho que o problema está em v <sqrt (2). v> sqrt (2) deve sempre mover pelo menos uma diagonal completa, evitando o efeito de escada. Talvez útil para podas que precisam de comparações de movimentos anteriores.

mghicks
fonte
+1 para apontar um limite superior para a sugestão da v. Ilmari é mais detalhado, mas você está fornecendo informações úteis.
sam hocevar 21/10