Sprites tornando embaçado com velocidade

9

Depois de adicionar velocidade ao meu jogo, sinto que minhas texturas estão tremendo. Eu pensei que eram apenas meus olhos, até que finalmente capturei em uma captura de tela:

insira a descrição da imagem aqui

O da esquerda é o que renderiza no meu jogo; o da direita é o sprite original, colado. (Esta é uma captura de tela do Photoshop, ampliada em 6x.)

Observe que as bordas estão com alias - parece quase como renderização sub-pixel. De fato, se eu não tivesse forçado meus sprites (que têm posição e velocidade como ints) a desenhar usando valores inteiros, eu juraria que o MonoGame está desenhando com valores de ponto flutuante. Mas não é.

Qual poderia ser a causa dessas coisas parecerem borradas? Isso não acontece sem a velocidade aplicada.

Para ser mais preciso, minha SpriteComponentturma tem um Vector2 Positioncampo. Quando ligo Draw, uso essencialmente new Vector2((int)Math.Round(this.Position.X), (int)Math.Round(this.Position.Y))para a posição.

Eu já tinha um bug antes em que objetos estacionários tremiam - isso se deve ao fato de eu usar o Positionvetor direto e não arredondar os valores para ints. Se eu usar Floor/ em Ceilingvez de redondo, o sprite afunda / paira (diferença de um pixel de qualquer maneira), mas ainda fica embaçado.

ashes999
fonte
11
Isso pode estar relacionado à filtragem de textura sendo aplicada?
OriginalDaemon
Você tem o código para o seu shader? Algumas pessoas adicionam meio pixel nos dois eixos para centralizá-los. Não tenho certeza, acho que isso é uma coisa única do DirectX.
William Mariager
3
Desativar a filtragem é uma solução alternativa, não uma solução.
Archy
11
A renderização não sabe nada sobre sua velocidade; portanto, a posição, tamanho ou qualquer outra coisa que você passe para o SpriteBatch.Draw é diferente ou o erro estava presente antes de você adicionar velocidade. Como você chama SpriteBatch.Draw exatamente?
Archy
11
@ ashes999 você depurou esta chamada e verificou this.X, this.Y e this.origin? O que é this.origin definido como? Basicamente, não há como o resultado da renderização ser diferente quando essa chamada do Draw for a mesma.
Archy

Respostas:

3

Bem, isso é constrangedor.

Acontece que eu não estava desenhando usando posições inteiras, mas posições flutuantes. Archy me apontou o caminho certo com seu comentário@ashes999 did you debug this call and checked this.X, this.Y and this.origin?

Assim que adicionei uma declaração de rastreamento, notei que nada era traçado. Acontece que minha SpriteComponentclasse usava corretamente valores inteiros, mas SpriteSheetComponentainda utilizava valores flutuantes do bruto Vector2 Position.

Embora eu não tenha tentado filtrar e fixar a textura, suspeitava que essas não eram a alteração correta, porque estava desenhando uma imagem 2D na mesma largura e altura da imagem de origem (sem redimensionamento).

ashes999
fonte
11
Feito, feliz que você encontrou!
Archy
2

O XNA usa o DirectX9, que usa o centro do pixel como sua localização. Isso é perceptível ao usar as sobrecargas baseadas em Vector2 da classe draw. Acredito que subtrair (.5, .5) deve resolver seu problema.

Mais informações aqui.

ClassicThunder
fonte
Tecnicamente, não posso fazer isso, porque estou passando um Vector2 com int, intos argumentos. Além disso, as coisas parecem perfeitamente bem quando não tenho nenhum objeto em movimento.
ashes999
O que você quer dizer com objeto está em movimento? O XNA não tem noção de movimento, mas também desenha coisas onde você diz. É uma série de instantâneos fixos. Tente também usar as sobrecargas de retângulo, pois você está arredondando para um retângulo de qualquer maneira.
precisa
Eu tenho minha própria implementação de velocidade. SpriteComponenttem uma Vector2posição, que aumenta à medida que flutua, dependendo da velocidade; quando desenho, crio uma nova Vector2com versões inteiras (arredondadas) dos meus positionX e Y. Esse não é o problema, pois objetos estacionários sem velocidade parecem bem.
ashes999
Portanto, você está dizendo que possui um vetor de posição p e um vetor de velocidade v, adicione-os como p + = v e crie uma cópia de p usando valores arredondados. E que isso produz valores diferentes, em seguida, tendo uma posição igual à anterior p + = v, apesar das chamadas de empate serem idênticas? Porque isso não faz sentido.
usar o seguinte
Sim, é o que @Archy está dizendo também. Deixe-me depurar e verificar novamente. Eu adiciono p += vdurante Updatee renderizo com new Vector2((int)Math.Round(p.X), (int)Math.Round(p.Y))).
ashes999
2

A renderização não sabe nada sobre sua velocidade; portanto, a posição, tamanho ou qualquer outra coisa que você passe para o SpriteBatch.Draw é diferente ou o erro estava presente antes de você adicionar velocidade. Como você chama SpriteBatch.Draw exatamente?

Você depurou esta chamada e verificou this.X, this.Y e this.origin? O que é this.origin definido como? Basicamente, não há como o resultado da renderização com velocidade ser diferente quando essa chamada do Draw for a mesma.

Archy
fonte
1

O problema é provável que a filtragem de textura esteja ativada. Se você não tiver certeza do que isso significa, considere uma situação em que você tem uma imagem com 2 pixels de largura: o primeiro pixel é preto, o segundo pixel é branco. Se você ampliar esta textura, se a filtragem de textura estiver ativada, verá que ela fica embaçada e que a área entre o pixel preto e branco usa uma cor cinza degradê.

Um exemplo clássico de jogos com e sem filtragem é Super Mario 64, que teve filtragem enquanto Doom não.

Quando você não usa velocidade, é provável que o seu Sprite esteja posicionado de forma que o centro da textura esteja no ponto de amostra em que o XNA (ou qualquer API subjacente que esteja sendo usada) está capturando a cor. Quando você se move com uma velocidade de flutuação, a posição do seu Sprite muda, portanto, o ponto de amostra pode não se alinhar diretamente com o centro de um pixel; portanto, o resultado final é que uma cor que é uma média dos pixels mais próximos está sendo usado para renderizar.

Você pode verificar se a filtragem está ativada simplesmente tornando seu Sprite muito grande na tela. Se estiver embaçado, esse é o problema.

Se você precisa ter uma precisão de pixel perfeita na renderização, convém ter um valor flutuante para a posição armazenada em algum lugar, mas use valores inteiros para a posição do objeto que você está renderizando ... ou, é claro, você pode simplesmente desativar a filtragem se o seu jogo não precisar dele.

Você também pode querer ler isso .

Victor Chelaru
fonte
Como mencionei na minha pergunta, já estou usando números inteiros para minha localização e o tamanho da imagem original quando ligo Draw. Portanto, não tenho certeza de como poderia ser o caso - mas tentarei renderizar em larga escala e ver se a desativação da filtragem de textura ajudará.
ashes999
1

Tente definir seu SamplerState como SamplerState.PointClamp na chamada SpriteBatch.Begin.

craftworkgames
fonte