Ejemplo - Creando un Plano de Cartelera
Agrupamiento de Draw Calls

Optimizando el Rendimiento Gráfico

Un buen rendimiento es crítico al éxito de muchos juegos. Abajo hay algunas guías simples para maximizar la velocidad de la renderización gráfica de su juego.

Ubique los gráficos que tienen alto impacto

Las partes gráficas de su juego pueden tener principalmente un precio en dos sistemas del computador: el GPU o el CPU. La primera regla de cualquier optimización es encontrar dónde el problema de rendimiento está; ya que las estrategias para optimizar el GPU vs. el CPU son bastantes diferentes (y puede inclusive ser opuestas - es bastante común hacer que el GPU trabaje más mientras se optimice para el CPU, y vice versa).

Problemas típicos y maneras de revisarlos:

  • El GPU es a menudo limitado por fillrate o el ancho de banda de memoria.
    • Baje la resolución de la pantalla y corra el juego. Si una resolución de pantalla más baja hace que el juego corra más rápido, usted puede estar limitado al fillrate en el GPU.
  • El CPU a veces es limitado por el número batches que necesitan ser renderizados.
    • Marque “batches” en la ventana Rendering Statistics. Entre mayor batches (lotes) están siendo renderizados, mayor será el costo del CPU.

Problemas menos comunes:

  • El GPU tiene demasiados vértices para procesar. La cantidad de vértices que son aceptables dependen en el GPU y la complejidad de los vertex shaders. De manera general, intente no tener más de 100,000 vértices en móviles. Un PC maneja muy bien varios millones de vértices, pero es todavía buena práctica mantener este número tan bajo como sea posible para optimizar.
  • El CPU tiene muchos vértices que procesar. Estos pueden estar en skinned meshes, simulación de telas, partículas, u otras game objects y meshes. Como se menciono arriba, por lo general es buena práctica mantener este número tan bajo como sea posible sin comprometer la calidad del juego. Ver la sección acerca de optimización del CPU abajo por guías acerca de cómo hacer esto.
  • Si Renderizar no es un problema para el GPU ni en el CPU puede que haya un problema en otro lado - Por ejemplo, en sus scripts o su físicas. Utilice el Profiler para ubicar el problema.

Optimización del CPU

Para renderizar cualquier objeto en la pantalla, el CPU tiene mucho trabajo que hacer: trabajar qué luces afecta ese objeto, configurar el shader y los parámetros del shader, y enviar comandos de dibujo al driver gráfico, que luego prepara los comandos para que sea enviado a la tarjeta gráfica.

Todo este uso del CPU “por objeto” es intensivo a los recursos, por lo que si usted tiene muchos objetos visibles, esto se puede agregar. Por ejemplo, si usted tiene miles de triángulos, es mucho más fácil en el CPU si todos están en un mesh, en vez de un mesh por triángulo (agregando hasta 1000 meshes). El precio de ambos escenarios en el GPU es muy similar, pero el trabajo hecho por el CPU para renderizar mil objetos (en vez de uno) es significativamente mayor.

Reduzca la cuenta visible de objetos. Para reducir la cantidad de trabajo que el CPU necesita hacer:

  • Combine objetos cerca juntos, ya sea manualmente, o utilizando el draw call batching de Unity.
  • Utilice menos materiales en sus objetos, colocando texturas en un texture atlas más grande.
  • Utilice menos cosas que cause que los objetos sean renderizados múltiples veces (como lo son los reflejos, sombras, y luces per-pixel).

Combine los objetos juntos para que cada mesh tenga al menos varios cientos de triángulos y utilice solo un Material para el mesh entero. Es importante que tenga en cuenta que combinar dos objetos que no compartan un material no le da a usted ningún incremento de rendimiento. La razón más común para tener múltiples materiales es que dos meshes no compartan la misma textura; entonces para optimizar el rendimiento del CPU, usted debe asegurarse que cualquier objeto que usted combine comparta las misma texturas.

Cuando se utilice muchas pixel lights (luces de pixel) en el Forward rendering path, hay situaciones dónde combinar objetos no tiene sentido. Ver la sección de rendimiento de Iluminación abajo para aprender a cómo manejar esto.

GPU: Optimizando la geometría de un modelo

Hay dos reglas básicas para optimizar la geometría de un modelo:

  • No utilice más triángulos de los necesarios
  • Intente mantener el número del UV mapping de seams y bordes duros (vértices duplicados) lo mas bajo posible.

Tenga en cuenta que el número de vértices que el hardware de gráficas tiene que procesar usualmente no es lo mismo que el número reportado por una aplicación 3D. Las aplicaciones de modelado usualmente muestran la cantidad de puntos de esquina distinos que componen el modelo (conocido como la cantidad de vértices geométricos). Sin embargo, para una tarjeta gráfica algunos vértices geométricos necesitan ser divididos en dos o más vértices lógicos por propósitos de renderización. Un vértices debe estar dividido si tiene múltiples normales, coordenadas UV o colores de vértice. En consecuencia, la cuenta de vértices en Unity es usualmente superior que la cuenta dada por la aplicación 3D.

Mientras que la cantidad de geometría en los modelos es en su mayoría relevante para el GPU, algunas características en Unity también procesa modelos en el CPU (por ejemplo, mesh skinning).

Rendimiento de Iluminación

La opción más rápida es siempre crear una iluminación que no necesite ser calculada en absoluto. Para hacer esto, utilice Lightmapping para “bake” la iluminación estática solamente una vez, mas bien de calcular esto cada frame. El proceso de generar un ambiente lightmapped toma solamente un poco más que colocar una luz en la escena en Unity, pero:

  • Corre más rápido (2–3 veces más rápido que dos luces por-pixel)
  • Se verá mucho mejor, ya que puede bake la iluminación global y el lightmapper puede suavizar los resultados

En muchos casos usted puede aplicar varios trucos simples en vez de agregar varias luces extra. Por ejemplo, en vez de agregar una luz que brilla directamente a la cámara para darle un efecto de Rim Lighting , agregue un cálculo dedicado Rim Lighting directamente a sus shaders (ver Surface Shader Examples para aprender a cómo hacer esto).

Las luces en fordward rendering

También ver: Forward rendering

La iluminación dinámica por pixel agrega un trabajo significante de renderizado a cada pixel afectado, y puede llevar a objetos siendo renderizados en múltiples pases. Evite tener más de una una Pixel Light iluminando cualquier objeto sencillo en dispositivos menos poderosos, como móviles o GPUs de PCs de baja gama, y utilice lightmaps para iluminar objetos estáticos en vez de calcular su iluminación cada frame. La iluminación dinámica por vértice puede agregar trabajo significante a las transformaciones de vértice, entonces intente evitar situaciones dónde hay varias luces iluminando un solo objeto.

Evite combinar meshes que están lo suficiente lejos para ser afectados por diferentes conjuntos de pixel lights. Cuando usted utilice la pixel lighting, cada mesh tiene que estar renderizado tantas veces que haya pixel lights iluminándolo. Si usted combina dos meshes que están muy lejos, esto aumenta el tamaño efectivo del objeto combinado. Todas las pixel lights que iluminan cualquier parte de este objeto combinado son tomados en cuenta durante el renderizado, por lo que el número de pases que se debe hacer para renderizar el objeto combinado es la suma de la cantidad de pases para cada uno de los objetos separados, por lo que nada se gana al combinar los meshes.

Durante el renderizado, Unity encuentra todas las luces que rodean un mesh y calcula cuáles de esas luces lo afectan más. Los Quality Settings son utilizados para modificar qué tantas luces terminan como pixel lights, y qué tantas como vertex lights (luces de vértices). Cada luz calcula su importancia basándose en qué tan lejos está del mesh y qué tan intensa está su iluminación - y algunas luces son más importantes que otras puramente del contexto del juego. Por esta razón cada luz tiene una configuración del Render Mode que puede estar configurada a Important o Not Important; las luces marcadas como Not Important tienen una sobre-carga de renderizado menor.

Ejemplo: Considere un juego de manejar en la que el carro del jugador está manejando en la oscuridad con los faros de luz prendidos. Los faros de luz son probablemente la fuente de luz más significante que hay en el juego, por lo que su Render Mode debería estar configurado a Important. Puede que haya otras luces en el juego que son menos importantes, como las luces traseras de los otros carros o faros de luz, y que no mejoran el efecto visual al ser pixel lights. El Render Mode para esas luces con seguridad se pueden configurar como Not Important para evitar que se gaste capacidad de renderizado en lugares dónde no tiene casi beneficio.

Optimizar iluminación por pixel ahorra trabajo para el CPU y GPU: el CPU tiene menos draw calls que hacer, y el GPU tiene menos vértices que procesar y pixeles para rasterizar para todos los objetos adicionales de renderizado.

GPU: Compresión de Textura y mipmaps

Utilice Compressed textures(texturas comprimidas) para disminuir el tamaño de sus texturas. Esto puede resultar en tiempos de carga mayores, una huella de memoria menor, y un aumento dramático del rendimiento de renderizado. Las texturas comprimidas utilizan una fracción del ancho de banda de memoria necesitada para des-comprimir texturas RGBA de 32-bit.

Mipmaps de Textura

Siempre habilite Generate mipmaps para texturas utilizadas en una escena 3D. Una textura mipmap le permite al GPU utilizar una resolución de textura menor para triángulos pequeños. Esto es parecido a cómo la compresión de textura puede ayudar a limitar la cantidad de datos de textura transferidos cuando el GPU está renderizando.

La única excepción para esta regla es cuándo un texel (píxel de textura) es conocido de mapear 1:1 al píxel de la pantalla renderizada, tal como elementos UI o en un juego 2D.

Distancias LOD y cull per-layer

Hacerle cull (omitir/cortar) a los objetos involucra hacer de ellos invisibles. Esta es una manera efectiva de reducir ambas la carga del CPU y GPU.

En muchos juegos, una manera rápida y efectiva de hacer esto sin comprometer la experiencia del jugador es cull objetos pequeños de manera más agresiva que las más grandes. Por ejemplo, las rocas pequeñas y el escombro pueden volverse invisibles en distancias grandes, mientras que los edificios más grandes todavía serían visibles.

Hay un número de maneras en que se puede lograr:

Sombras en tiempo real

Las sombras en tiempo real son bonitas, pero pueden traer un impacto mayor en el rendimiento, en términos de draw calls extra para el CPU y un procesamiento extra en el GPU. Para más detalles, ver la página acerca del Light Performance (rendimiento de iluminación)

GPU: Tips para escribir shaders de alto rendimiento

Las diferentes plataformas que hay tienen diferentes capacidades de rendimiento; el GPU de un PC de alta gama puede manejar más en términos de gráficos y shaders que un GPU móvil de baja gama. Lo mismo es cierto incluso en una sola plataforma, un GPU rápido es una docena de veces más rápido que un GPU integrado más lento.

El probable que el rendimiento en las plataformas móviles y los Pcs de baja gama sea mas bajo que en su maquina de desarrollo. Se recomienda que usted optimice manualmente sus shaders para reducir los cálculos y lectura de texturas, con el fin de mantener un buen rendimiento a través de las maquinas GPU de baja gama. Por ejemplo, algunos Shaders integrados en Unity tienen equivalentes “móviles” que son mucho más rápido, pero tienen algunas limitaciones o aproximaciones.

Abajo hay algunas guías para tarjetas gráficas de baja gama para móviles y PC:

Operaciones matemáticas complejas

Funciones matemáticas trascendentales (como pow, exp, log, cos, sin, tan) son bastantes intensivas a los recursos, entonces evite utilizarlos cuando sea posible. Considere utilizar lookup textures como una alternativa para cálculos complejos de matemáticas si se puede.

Evite escribir sus propias operaciones (como lo son normalize, dot, inversesqrt). Las opciones integradas de Unity aseguran que el driver pueda generar mejor código. Recuerde que la operación Alpha Test (discard) hace que su fragment shader sea más lento.

Precisión Floating point

Mientras que la precisión (float vs half vs fixed) de variables floating point en su mayoría son ignorados en GPUs de escritorios, es bastante importante que obtenga un buen rendimiento en GPUs móviles. Ver la página Shader Data Types and Precision para detalles.

Para detalles adicionales acerca del rendimiento del shader, ver la página Shader Performance

Una simple lista de verificación para hacer su juego más rápido

  • Mantenga la cantidad de vértices debajo de 200k y 3M por frame cuando construya para PC (dependiendo en el GPU objetivo).
  • Si usted está utilizando uno de los shaders integrados, escoja uno de las categorías Mobile o Unlit. Estas funcionan en plataformas no necesariamente de móviles, pero son versiones simplificadas y aproximadas de más shaders complejos.
  • Mantenga el número de materiales diferentes por escena bajo, y comparta tantos materiales que pueda entre objetos como sea posible.
  • Configure la propiedad Static en un objeto que no se mueva para permitir optimizaciones internas como static batching.
  • Solo tiene una solo pixel light (preferiblemente directional) afectando su geometría, en ves de varios.
  • Bake la iluminación (static) en vez de usar una iluminación dinámica.
  • Utilice formatos de textura comprimidos cuando pueda, y utilice texturas 16-bit en vez de texturas de 32-bit.
  • Evite utilizar fog (niebla) cuando sea posible.
  • Utilice Occlusion Culling para reducir la cantidad de geometría que está visible y draw calls en casos de escenas estáticas complejas con mucho occlusion. Diseñe sus niveles con occlusion culling en mente.
  • Utilice skyboxes para distancias de geometría “falsas”.
  • Utilice sombreadores de píxel o combinadores de texturas para mezclar varias texturas en vez de un enfoque multi-pass.
  • Utilice variables de precisión half cuando sea posible.
  • Minimice el uso de operaciones matemáticas complejas como pow, sin y cos en pixel shaders.
  • Escoja fewer textures per fragment (menos texturas por fragmento).

Véase también

Ejemplo - Creando un Plano de Cartelera
Agrupamiento de Draw Calls