Aliasing en Texturas

  • Por
Vemos que es el Aliasing en texturas y como podemos solucionarlo.
Bienvenidos al séptimo post de la serie sobre aliasing que estoy realizando y que voy publicando en Desarrolloweb.com y en Speakingin.net, gracias a las contribuciones que Shawn Hargreaves hizo en su blog y que encuentro muy interesantes y merecedoras de ser puestas a disposición de la comunidad hispano hablante.

El aliasing puede ocurrir cada vez que reensamblamos una textura, como por ejemplo al escalarla, rotarla, o mapearla en un modelo 3D. Gracias a nuestro amigo Nyquist, los problemas pueden ser mayores a medida que disminuimos la frecuencia a la que la reensamblamos.

Atención a la imagen tan impresionante con un tamaño de 32 x32

Si la vemos con un zoom de 4, podemos ver más detalles:

Ahora, este código de prueba, dibuja seis copias, la primera sin ninguna transformación, otra rotada, seguida por una sucesión de otras cuatro escaladas y rotadas:

spriteBatch.Begin(0, null, SamplerState.PointClamp, null, null);

spriteBatch.Draw(testTexture, new Vector2(20, 24), null, Color.White, 0, new Vector2(16, 16), 1, 0, 0);
spriteBatch.Draw(testTexture, new Vector2(60, 24), null, Color.White, 1, new Vector2(16, 16), 1, 0, 0);

spriteBatch.Draw(testTexture, new Vector2(95, 24), null, Color.White, 0, new Vector2(16, 16), 0.5f, 0, 0);
spriteBatch.Draw(testTexture, new Vector2(120, 24), null, Color.White, 1, new Vector2(16, 16), 0.5f, 0, 0);

spriteBatch.Draw(testTexture, new Vector2(140, 24), null, Color.White, 0, new Vector2(16, 16), 0.25f, 0, 0);
spriteBatch.Draw(testTexture, new Vector2(155, 24), null, Color.White, 1, new Vector2(16, 16), 0.25f, 0, 0);

spriteBatch.End();

Fijáos en el uso de SamplerState.PointClamp, que desactiva el antialiasing del SpriteBatch que aplicaría por defecto.

La imagen obtenida es:

Haciéndole un zoom veremos:

Hay un montón de problemas de aliasing.Algunos especialmente molestos son: Las diagonales negras se muestran punteadas en la primera rotación. Las imágenes a la mitad de tamaño tienen otro montón de problemas: La vertical verde, y las horizontales rojas y azules, simplemente han desaparecido. Tan sólo hay dos líneas diagonales negras en lugar de tres. El borde negro se mantienen en la parte de abajo y en la derecha, las otras han desaparecido. Las últimas dos imágenes, son totalmente diferentes: La que no está rotada tan sólo tiene una línea horizontal azul. La versión rotada sólo muestra píxeles rojos y verdes pero ninguno azul. Imaginad lo horrible que sería animar este sprite haciéndolo girar. ¿Recordáis cómo el multisampling suaviza los vértices de los triángulos pero que no afecta al interior? Podemos confirmarlo activando el multisampling en la imagen escalada, y veremos que la única diferencia son unos bordes más suaves en las imágenes rotadas:

Sin embargo, con un supersampling de 4x el aliasing de la textura se ve claramente reducido:

Pero todavía está lejos de ser perfecto. La imagen a tamaño completo y la rotada y la de mitad de tamaño sin rotar se ven bien, pero las demás todavía tienen errores. Aún veríamos cambios de color desagradables si animamos las otras versiones. Esto es debido a que el supersampling de 4x tan sólo mejora en dos veces la frecuencia de muestre en cada uno de los ejes horizontales y verticales. Esto puede reducir el aliasing, pero no es suficiente para mantenernos por encima del límite de Nyquist si reducimos la imagen en un factor mayor de 2x.

Entonces ¿Qué podemos hacer?

Para empezar, no deberíamos hacernos la vida más difícil especificando la frecuencia de muestreo. Si desactivamos el supersampling y el multisampling, tenemos que cambiar la llamada del SpriteBatch.Begin para que use el SamplerState.LinearClamp, los filtros bilineales producen imágenes mejores que el usado en el ejemplo SamplerState.PointClamp:

Al contrario que el multisampling y el supersampling, el filtrado de texturas no suaviza los bordes de las imágnes rotadas, pero hace un buen trabajo reduciendo el aliasing dentro de la imagen completa, tanto en las versiones rotadas como en las no rotadas. Los filtros bilineales nos dejan en el mismo lugar que el supersampling. La GPU sólo toma cuatro etapas en el filtro de cada textura (dos horizontales y dos verticales), tenemos un colchón dos veces mayor a la hora de empezar a ver problemas de aliasing, pero esta mejora no elimina por completo el problema de Nyquist, por eso las versiones de la imagen cuatro veces menor siguen viéndose mal.

La solución es combinar el filtro bilineal con mipmaps precalculados (y si podemos, usar también filtros anti-isotrópicos). No importa lo pequeñas que sean las texturas, la GPU puede seleccionar el nivel apropiado, manteniéndolo siempre justo por encima del límite de Nyquist sin importar las transformaciones que se le hagan.

Con mipmaps habilidato, incluso las versiones más pequeñas mantienen la intensidad del color, sin obtener cambios aleatorios ni de la imagen ni de la sombra, de manera que podemos animar nuestro sprite rotándolo o escalándolo sin tener problemas de aliasing:

Espero que os sirva.
Fuente original

Autor

Juan María Laó Ramos

Project Manager at Syderis Technologies S.L.

Compartir