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.
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:
Problemas menos comunes:
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 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.
Hay dos reglas básicas para optimizar la geometría de un modelo:
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).
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:
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).
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.
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.
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.
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:
Utilice el sistema Level Of Detail
Configure manualmente las distancias per-layer culling en la cámara
Coloque objetos pequeños en una layer (capa) separada y configure sus distancias cull per-layer utilizando la función script Camera.layerCullDistances
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)
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:
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.
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
Static
en un objeto que no se mueva para permitir optimizaciones internas como static batching.pixel light
(preferiblemente directional) afectando su geometría, en ves de varios.half
cuando sea posible.pow
, sin
y cos
en pixel shaders.