¿Debo usar hojas de sprites debido a mi gran cantidad de imágenes?

13

Estoy desarrollando un juego en 2D y tengo muchos sprites. Usé animaciones 3D y modelos para renderizar en 2D, para darles ese aspecto de "Fallout" o "Diablo". También es más fácil que dibujar a mano, jajaja.

Ya he tenido que reducir la velocidad de fotogramas a 15 fps, que fue la más baja que pude bajar sin hacer que tengan un aspecto entrecortado. Sin embargo, fue triste debido a lo increíblemente suave que se veían los 24 cuadros.

Hay dos razones por las que hice esto:

1) Reduzca el espacio en el disco duro. Cuantas menos imágenes, más pequeño será mi juego total.

2) Reducir el consumo de RAM. Cuantas menos imágenes se carguen, más probabilidades tendré de evitar problemas que hinchen mi limitación de RAM.

Sin embargo, si hubiera una manera de comprimir las imágenes tanto en el espacio del disco duro como en la RAM, lo haría. Lo he probado antes, y la mayoría no recibe ningún cambio en la calidad cuando doy de RGBA8888 a RGBA5555 y solo un pequeño golpe al convertir a RGBA4444 en mi programa TexturePacker. Actualmente no hago esto, porque SFML parece usar la misma cantidad de memoria independientemente del tipo de imagen .PNG que sea. Busqué investigar cómo cargarlo de manera diferente, pero no pude encontrar nada sobre el tema.

He leído mucho sobre cómo manejar los videojuegos en 2D. El consenso es abrumador: empaca tus Sprites en una textura más grande para un gran rendimiento. Así que empaco mis pequeños sprites en una hoja de sprites mucho más grande usando TexturePacker.

Sin embargo, planeo tener 10-15 animaciones por personaje, 5 direcciones para mover y 15-40 cuadros por animación (probablemente un promedio de 24). Con 15 animaciones, 5 direcciones y un promedio de 24 cuadros por animación; Eso es 1800 fotogramas individuales por personaje. Si se empaqueta en una hoja de sprite, solo son 75 imágenes. (Una hoja de sprites por Animación, por Dirección. 15 * 5)

Para el gran personaje jefe en el juego, no puedo usar una hoja de sprites y tengo que programar una forma de cargar simplemente una imagen a la vez. Todavía no sé si puedo hacer esto por rendimiento.

Para los personajes, ya los empaqué en una hoja de sprites. Para un solo personaje caminando, esto parece funcionar la mayor parte del tiempo, aunque a veces se detiene. Sin embargo, lo atribuyo a mi código mal concebido que intercambia texturas en lugar de precargar todas las texturas para ese personaje.

Si tuviera que precargar las texturas, tiene sentido para las hojas de sprites. Solo me imagino que es una mala idea precargar 1800 imágenes pequeñas para cada personaje.

Sin embargo, imagino que transmitirlos dentro y fuera de la memoria uno a la vez sería extremadamente rápido, por lo que solo necesitaría tener una sola imagen en la memoria al mismo tiempo. ¿No significa esto que en un momento dado solo haría que cada personaje consumiera unos pocos KB en lugar de 45 + MB?

Me imagino que esto mataría mi rendimiento, ya que la transmisión tendría que ser increíblemente rápida (15 imágenes que entran y salen de la memoria y la representación, por segundo) y aunque las imágenes serían muy pequeñas, podría ser una mejor idea cargar hojas de sprites de caracteres en memoria en su lugar. Pero tendré que codificar un sistema de renderizado de flujo de una sola imagen para mi personaje jefe más grande de todos modos.

He estado experimentando, pero no es un proceso simple. Especialmente dado el hecho de que estoy trabajando en otras partes del motor del juego que no manejan gráficos en este momento.

Carter81
fuente
1. No especificó sus restricciones de RAM o HDD. ¿Cuántos personajes deben tener acceso rápido? 2. Hay varias preguntas a lo largo del texto, ¿tal vez podría enfocarlas en negrita o incluso dividir las preguntas en partes?
Kromster dice que apoya a Mónica el
Oh lo siento. No muchos. Me imagino que el número máximo de caracteres individuales en la pantalla en cualquier momento sería de aproximadamente 40. Si la gente decidiera tratar de bloquear a sus clientes, entonces ... 130 es el máximo absoluto. Típicamente, solo necesitaría ser 10 para el máximo típico, y el máximo absoluto no sería más de <40. Cualquier cosa por encima de 40 sería una rareza extrema, extrema, con los usuarios tratando intencionalmente de meter caracteres sin otra razón que una captura de pantalla o por la diversión de meter caracteres. Cualquier cosa por encima de 10 es rara, y alrededor de 40 es extremadamente, extremadamente rara.
Carter81
El juego es un juego de rol 2D solo para PC (no móvil), sin embargo, no me gustaría descartar una plataforma móvil a menos que simplemente no sea factible. Me imagino que el espacio de RAM que tengo está limitado a cualquier RAM que esté en la PC del usuario y la VRAM del usuario. Dudo mucho que sea un disco duro muy grande. Es solo que siempre es cierto para el consumo de HDD que cuanto más pequeño sea, mejor.
Carter81
¿Cuántos personajes ÚNICOS necesitas tener en la pantalla?
Kromster dice que apoya a Mónica el
No más de 20. Cualquier cosa por encima de eso sería casi imposible a menos que hicieran trampa. Típicamente 5-10.
Carter81

Respuestas:

16

Tenemos un caso similar con nuestro RTS Remake. Todas las unidades y casas son sprites. Tenemos 18 000 sprites para unidades y casas y terreno, más otros ~ 6 000 para colores de equipo (aplicados como máscaras). De largo alcance también tenemos unos ~ 30 000 caracteres utilizados en las fuentes.

Entonces, la razón principal detrás de los atlas son:

  • menos RAM desperdiciada (en los días anteriores, cuando cargaba NPOT a la GPU, se estiraba / acolchaba a POT, leí que sigue siendo lo mismo con iOS y algunos marcos. Es mejor verificar el rango de hardware al que apunta)
  • menos interruptores de textura
  • carga más rápida de todo en menos trozos más grandes

Lo que no funcionó para nosotros:

  • texturas paletizadas. La característica solo existía en OpenGL 1.x 2.xy incluso entonces fue eliminada principalmente por los fabricantes de GPU. Sin embargo, si apunta a OpenGL + Shaders, ¡puede hacerlo en el código de sombreadores usted mismo!
  • Texturas NPOT, tuvimos problemas con bordes incorrectos y sprites borrosos, lo cual es inaceptable en pixel art. El uso de RAM también fue mucho mayor.

Ahora tenemos todo empaquetado en varias docenas de atlas de 1024x1024 (las GPU modernas admiten dimensiones aún más grandes) y eso funciona bien comiendo solo ~ 300 mb de memoria, lo cual es bastante bueno para un juego de PC. Algunas optimizaciones que tuvimos:

  • agregue la opción de usuario para usar RGB5_A1 en lugar de RGBA8 (sombras de tablero de ajedrez)
  • evite Alpha de 8 bits cuando sea posible y use el formato RGB5_A1
  • empaquete bien los sprites en atlas (vea Algoritmos de Empaque de Bin)
  • almacenar y cargar todo en un fragmento desde el disco duro (los archivos de recursos deben generarse sin conexión)
  • También puede probar los formatos de compresión de hardware (DXT, S3TC, etc.)

Cuando considere seriamente cambiar a dispositivos móviles, se preocupará por las restricciones. ¡Por ahora solo haz que el juego funcione y atrae a los jugadores! ;)

Kromster dice que apoya a Mónica
fuente
¡Esta fue la mejor solución con diferencia! Mis sprites ni siquiera se ven diferentes en RGB5_A1 o RGBA4444, pero ahorra memoria. Y su sugerencia en el chat para precargar todos mis activos en RAM y VRAM es perfecta. Aún más, sugirió tener niveles gráficos opcionales, como un cliente de alta definición para aquellos que tienen RAM, o una opción para reducir la velocidad de fotogramas a la mitad, etc. ¡Excelentes sugerencias, y exactamente lo que necesitaba!
Carter81
5

Tengo una respuesta tangencialmente relacionada en aquí , pero la idea general es que, si está cargando y dibujando texturas en diferentes momentos (no está cargando texturas adicionales mientras está renderizando), entonces hay dos lugares donde hacer afectará su rendimiento:

Tiempo de carga:

Este es el momento en que carga sus texturas en la memoria. la totalidad cantidad de datos que está enviando a VRAM es lo que definirá principalmente cuánto tiempo durará su carga. Hacer que sus texturas tengan formatos más pequeños, como RGBA4444, lo hará más rápido. Sin embargo, a menos que esté cargando texturas en los cientos de megabytes a VRAM, probablemente no tendrá un cuello de botella aquí. Si lo hace, una buena pantalla de carga puede facilitar la espera.

Unir sus texturas en atlas tendrá poco efecto, ya que la cantidad total de información que envía a VRAM será la misma. De hecho, si está clasificando sus texturas y tiene que dejar espacios vacíos en sus atlas, entonces realmente enviará más datos a VRAM y, por lo tanto, esta parte será más lenta.

Rendimiento de renderizado:

Una vez que todas sus texturas estén en VRAM, la cantidad de texturas que tenga no afectará el rendimiento del renderizado. Hay cuatro elementos que afectan su rendimiento de representación:

  1. Renderizar cambios de estado : cada vez que cambie la imagen desde la que desea renderizar, aumentará seriamente el tiempo requerido para renderizarla. En general, desea minimizar la cantidad de cambios de estado, y puede reducir la cantidad de cambios de estado al agrupar varias imágenes que dibujará consecutivamente, en un atlas de texturas.

    Solo atlas no es suficiente. Debe atlas de una manera en que se reducen los cambios de estado, para obtener ganancias de rendimiento. Por ejemplo, uno puede pensar que tener su personaje principal en una hoja de sprites le dará una ganancia de rendimiento, pero si solo está dibujando un sprite de esa hoja de sprites por cuadro, no obtendrá ninguna ganancia de rendimiento en comparación con tener cada sprite en un archivo separado.

    El atlas adecuado no es trivial, pero en general puede agrupar sprites de forma segura desde la misma capa. Por ejemplo, tener todos los elementos de la GUI en una hoja de sprites es una idea muy prometedora, mientras que agrupar monstruos alfabéticamente puede no serlo.

  2. Llamadas de sorteo: en general, es posible que desee mantener sus llamadas de sorteo al mínimo. Una buena regla general es que si no hay cambios de estado de representación entre dos llamadas de extracción, puede unirlas en una sola llamada de extracción. Para obtener ganancias de rendimiento más avanzadas, puede usar, por ejemplo, 8 muestreadores de textura y llamadas de dibujo grupales por cada 8 texturas, por lo que solo tiene que cambiar las texturas cada 8 texturas.

  3. Recuento de triángulos: de hecho, cuantos más triángulos dibujes, más tiempo tomará dibujarlos. Sin embargo, en las computadoras modernas, y para la mayoría de los juegos en 2D, estarás muy lejos de maximizar esto. Puede dibujar de forma segura cientos de miles de sprites por cuadro y aún así obtener tasas de fotogramas increíblemente buenas. Probablemente estará más vinculado a la CPU si está dibujando cantidades extremas de sprites antes de tener problemas con su GPU.

  4. Configuración de la API: si está haciendo todo bien y sigue obteniendo velocidades de fotogramas extrañamente bajas, verifique la configuración con la que está dibujando sus sprites. No conozco SFML, pero, por ejemplo, en Direct3D 9, la creación de un búfer de vértices con D3DUSAGE_DYNAMIC, o en D3DPOOL_MANAGEDpuede aumentar fácilmente sus tiempos de representación por diez. Por supuesto, el uso de vSync limitará su velocidad de cuadros a la frecuencia de actualización de su monitor. Además, el uso de FVF no alineados puede disminuir el rendimiento en algunas GPU. Esto también es para Direct3D 9.

    En su caso, consulte la documentación de la API que está utilizando.

Si solo tiene una cantidad de texturas baja a moderada (menos de 1GB), y está dibujando cantidades bajas de sprites (menos de un millón por cuadro), entonces lo primero que miraría sería cambiar la configuración de la API, y luego reduciendo la cantidad de estados de renderizado y llamadas atraídas.

Pijama Panda
fuente
Si no me importan los tiempos de carga, ¿debo suponer que a menos que me quede sin RAM o VRAM, debería cargar todo de antemano?
Carter81
Solo me preocupa todo PORQUE tengo miedo de quedarme sin RAM / VRAM. No sé por qué, pero me horroriza que los usuarios que juegan mi juego se bloqueen cada vez que intentan cargar en un área que tiene demasiados sprites únicos, o se bloquean cada vez que demasiados personajes entran en su pantalla. Si no me equivoco y cada sprite individual consume 96 KB, entonces, si cada personaje único tiene 15 animaciones, 5 direcciones y un promedio de 24 fotogramas por animación, cada personaje individual cargado por completo es 173 MB. Puede haber 10, tal vez incluso más, personajes únicos en la pantalla a la vez.
Carter81
@KromStern: si tiene un atlas y tiene que dejar espacios vacíos (relleno), los datos serán más grandes y, por lo tanto, los tiempos de carga serán más largos. Está claro que la causa de los tiempos de carga más largos es el espacio vacío, y que el tiempo de carga total está relacionado con la cantidad total de datos y no con la cantidad de texturas. No veo nada engañoso allí, y creo que una persona con suficiente conocimiento para comprender la pregunta original, podrá unir los puntos y sacar sus propias conclusiones para todos los casos en que las texturas y atlas sean PoT y nPoT.
Panda Pyjama
1
@ Carter81: debe elegir la configuración de hardware de destino, como "i5, 1 gb de RAM, NVidia GT260, disco duro de 400 mb" y trabajar a partir de eso. Siempre habrá PC que sean más débiles y tengan menos RAM.
Kromster dice que apoya a Mónica el
Obviamente, no necesitan las 15 animaciones en un momento dado. Sin embargo, ¿qué pasa si los 10 personajes únicos entran en "Modo de combate" al mismo tiempo, y por lo tanto requieren 5 juegos de animaciones (caminar, correr, inactivo, no combate, etc.) para intercambiarse con 5 más (caminar de combate, combate inactivo, combate, etc.)? Tengo miedo de intercambiar texturas porque cuando intenté hacer eso con SFML, creó una congelación o pausa notable del cliente al cambiar atlas de textura. No sé cuál sería mi cuello de botella con las diversas estrategias para manejar tantos sprites.
Carter81