Tengo un motor de juego 2D que dibuja mapas de mosaicos dibujando mosaicos de una imagen de conjunto de mosaicos. Debido a que, por defecto, OpenGL solo puede envolver toda la textura ( GL_REPEAT
), y no solo una parte, cada mosaico se divide en una textura separada. Luego, las regiones del mismo mosaico se vuelven adyacentes entre sí. Así es como se ve cuando funciona según lo previsto:
Sin embargo, tan pronto como introduzca la escala fraccional, aparecerán las costuras:
¿Por qué pasó esto? Pensé que se debía al filtrado lineal que mezclaba los bordes de los quads, pero aún sucede con el filtrado de puntos. La única solución que he encontrado hasta ahora es asegurar que todo el posicionamiento y la escala solo ocurran en valores enteros, y usar el filtrado de puntos. Esto puede degradar la calidad visual del juego (particularmente que el posicionamiento de subpíxeles ya no funciona, por lo que el movimiento no es tan suave).
Cosas que he probado / considerado:
- antialiasing reduce, pero no elimina por completo, las costuras
- desactivar mipmapping, no tiene efecto
- renderice cada mosaico individualmente y extruya los bordes en 1px, pero esto es una des-optimización, ya que ya no puede procesar regiones de mosaicos de una sola vez, y crea otros artefactos a lo largo de los bordes de las áreas de transparencia
- agregue un borde de 1px alrededor de las imágenes de origen y repita los últimos píxeles, pero ya no son la potencia de dos, causando problemas de compatibilidad con sistemas sin soporte NPOT
- escribir un sombreador personalizado para manejar imágenes en mosaico, pero entonces, ¿qué haría diferente?
GL_REPEAT
debe tomar el píxel desde el lado opuesto de la imagen en los bordes, y no elegir la transparencia. - la geometría es exactamente adyacente, no hay errores de redondeo de coma flotante.
- Si el sombreador de fragmentos está codificado para devolver el mismo color, las costuras desaparecerán .
- si las texturas se establecen en
GL_CLAMP
lugar deGL_REPEAT
, las costuras desaparecen (aunque el renderizado es incorrecto). - si las texturas están configuradas en
GL_MIRRORED_REPEAT
, las costuras desaparecen (aunque el renderizado vuelve a ser incorrecto). - Si hago el fondo rojo, las costuras siguen siendo blancas. Esto sugiere que está muestreando blanco opaco de algún lugar en lugar de transparencia.
Por lo tanto, las costuras aparecen solo cuando GL_REPEAT
está configurado. Por alguna razón solo en este modo, en los bordes de la geometría hay algo de sangrado / fuga / transparencia. ¿Como puede ser? Toda la textura es opaca.
GL_NEAREST
muestreo en laR
dirección de coordenadas también funcionan tan bien como las texturas de matriz para la mayoría de las cosas en este escenario. Mipmapping no va a funcionar, pero a juzgar por su aplicación, probablemente no necesite mipmaps de todos modos.Respuestas:
Las costuras son el comportamiento correcto para el muestreo con GL_REPEAT. Considere el siguiente mosaico:
El muestreo en los bordes del mosaico utilizando valores fraccionales mezcla colores del borde y el borde opuesto, lo que da como resultado colores incorrectos: el borde izquierdo debe ser verde, pero es una mezcla de verde y beige. El borde derecho debe ser beige, pero también tiene un color mixto. Especialmente las líneas de color beige en el fondo verde son muy visibles, pero si miras de cerca puedes ver el sangrado verde en el borde:
MSAA funciona tomando más muestras alrededor de los bordes de los polígonos. Las muestras tomadas a la izquierda o derecha del borde serán "verdes" (nuevamente, considerando el borde izquierdo de la primera imagen), y solo una muestra estará "en" el borde, tomando muestras parcialmente del área "beige". Cuando las muestras se mezclan, el efecto se reducirá.
Soluciones posibles:
En cualquier caso , debe cambiar a
GL_CLAMP
para evitar el sangrado del píxel en el lado opuesto de la textura. Entonces tienes tres opciones:Habilite el suavizado para suavizar un poco la transición entre los mosaicos.
Establece el filtrado de textura en
GL_NEAREST
. Esto da bordes duros a todos los píxeles, por lo que los bordes de polígono / sprite se vuelven indistinguibles, pero obviamente cambia bastante el estilo del juego.Agregue el borde de 1px ya discutido, solo asegúrese de que el borde tenga el color del mosaico adyacente (y no el color del borde opuesto).
GL_LINEAR
filtrado similar a los bordes de polígonos / sprites.fuente
Considere la visualización de un único quad con textura ordinaria en OpenGL. ¿Hay costuras a alguna escala? No nunca. Tu objetivo es juntar todos los mosaicos de tu escena en una sola textura y luego enviarlos a la pantalla. EDITAR Para aclarar esto aún más: si tiene vértices delimitadores discretos en cada cuadrante de baldosas, tendrá costuras aparentemente injustificadas en la mayoría de las circunstancias. Así es como funciona el hardware de la GPU ... los errores de coma flotante crean estas brechas en función de la perspectiva actual ... errores que se evitan en una variedad unificada, ya que si dos caras son adyacentes dentro de la misma variedad(submesh), el hardware los renderizará sin costuras, garantizado. Lo has visto en innumerables juegos y aplicaciones. Debe tener una textura compacta en una sola malla sin vértices duplicados para evitar esto de una vez por todas. Es un problema que surge una y otra vez en este sitio y en otros lugares: si no combina los vértices de las esquinas de sus mosaicos (cada 4 mosaicos comparten un vértice de la esquina), espere uniones.
(Tenga en cuenta que es posible que ni siquiera necesite vértices, excepto en las cuatro esquinas de todo el mapa ... depende de su enfoque del sombreado).
Para resolver: renderice (a 1: 1) todas sus texturas de mosaico en un FBO / RBO sin espacios, luego envíe ese FBO al framebuffer predeterminado (la pantalla). Debido a que el FBO en sí mismo es básicamente una textura única, no puede terminar con brechas en la escala. Todos los límites de texel que no caen en un límite de píxel de la pantalla se combinarán si está utilizando GL_LINEAR ... que es exactamente lo que desea. Este es el enfoque estándar.
Esto también abre una serie de rutas diferentes para escalar:
fuente
Si las imágenes deben aparecer como una superficie, renderícelas como una textura de superficie (escala 1x) en una malla / plano 3D. Si renderiza cada uno como un objeto 3D separado, siempre tendrá costuras debido a errores de redondeo.
La respuesta de @ Nick-Wiggill es correcta, creo que la malinterpretaste.
fuente
2 posibilidades que vale la pena probar:
Úselo en
GL_NEAREST
lugar deGL_LINEAR
para filtrar su textura. (Como señaló Andon M. Coleman)GL_LINEAR
(en efecto) desenfocará la imagen muy ligeramente (interpolando entre los píxeles más cercanos), lo que permite una transición suave del color de un píxel al siguiente. Esto puede ayudar a que las texturas se vean mejor en muchos casos, ya que evita que sus texturas tengan esos píxeles en bloque por todas partes. También permite que un poco de color marrón de un píxel de un mosaico se pueda difuminar al píxel verde adyacente del mosaico adyacente.GL_NEAREST
solo encuentra el píxel más cercano y usa ese color. Sin interpolación Como resultado, en realidad es un poco más rápido.Reduce un poco las coordenadas de textura para cada mosaico.
Algo así como agregar ese píxel adicional alrededor de cada mosaico como un búfer, excepto que en lugar de expandir el mosaico en la imagen, reduce el mosaico en el software. Mosaicos de 14x14px mientras se renderiza en lugar de mosaicos de 16x16px.
Incluso podría salirse con la suya con baldosas de 14.5x14.5 sin mezclar demasiado marrón.
--editar--
Como se muestra en la imagen de arriba, aún puede usar fácilmente una potencia de dos texturas. Por lo tanto, puede admitir hardware que no admite texturas NPOT. Lo que cambia son tus coordenadas de textura, en lugar de ir de
(0.0f, 0.0f)
a(1.0f, 1.0f)
Irías de(0.03125f, 0.03125f)
a(0.96875f, 0.96875f)
.Eso coloca sus coordenadas de texto ligeramente dentro del mosaico, lo que reduce la resolución efectiva en el juego (aunque no en el hardware para que aún tenga texturas de potencia de dos), sin embargo, debería tener el mismo efecto de representación que expandir la textura.
fuente
Esto es lo que creo que podría estar sucediendo: donde dos fichas colisionan, el componente x de sus bordes debería ser igual. Si eso no es cierto, podrían redondearse en la dirección opuesta. Así que asegúrese de que tengan exactamente el mismo valor x. ¿Cómo se asegura de que esto suceda? Puede intentar, digamos, multiplicar por 10, redondear y luego dividir por 10 (solo haga esto en la CPU, antes de que sus vértices entren en el sombreador de vértices). Eso debería dar resultados correctos. Además, no dibuje mosaico por mosaico utilizando matrices de transformación, sino colóquelas en un VBO por lotes para asegurarse de que no se obtengan resultados incorrectos del sistema de coma flotante IEEE con pérdida en combinación con multiplicaciones de matrices.
¿Por qué sugiero esto? Porque, según mi experiencia, si los vértices tienen las mismas coordenadas exactas cuando salen del sombreador de vértices, llenar los triángulos correspondientes creará resultados perfectos. Tenga en cuenta que algo que es matemáticamente correcto, podría estar un poco fuera de lugar debido a IEEE. Cuantos más cálculos realice en sus números, menos preciso será el resultado. Y sí, las multiplicaciones matriciales requieren bastantes operaciones, que podrían realizarse solo en una multiplicación y una adición al crear su VBO, lo que dará resultados más precisos.
Lo que también podría ser el problema es que está utilizando una hoja de sprites (también llamada atlas) y que al muestrear la textura, se recogen los píxeles de la textura de mosaico adyacente. Asegurarse de que esto no suceda es crear un pequeño borde en las asignaciones de UV. Entonces, si tiene un mosaico de 64x64, su mapeo UV debería cubrir un poco menos. ¿Cuánto cuesta? Creo que usé en mis juegos 1 cuarto de píxel a cada lado del rectángulo. Por lo tanto, compensa tus UV por 1 / (4 * widthOfTheAtlas) para componentes x y 1 / (4 * heightOfTheAtlas) para componentes y.
fuente
Para resolver esto en el espacio 3D, mezclé matrices de texturas con la sugerencia de Wolfgang Skyler. Puse un borde de 8 píxeles alrededor de mi textura real para cada mosaico (120x120, 128x128 en total) que, para cada lado, podría llenar con una imagen "envuelta" o el lado que se acaba de extender. La muestra lee esta área cuando está interpolando la imagen.
Ahora con filtrado y mipmapping, la muestra aún puede leer fácilmente más allá del borde completo de 8 píxeles. Para detectar ese pequeño problema (digo pequeño porque solo ocurre en unos pocos píxeles cuando la geometría está realmente sesgada o muy lejos), dividí los mosaicos en una matriz de textura, por lo que cada mosaico tiene su propio espacio de textura y puede usar la sujeción en el bordes
En su caso (2D / plano), sin duda elegiría renderizar una escena perfecta de píxeles y luego escalar la parte deseada del resultado en la ventana gráfica, como lo sugirió Nick Wiggil.
fuente