Renderización en Unreal Engine
Introducción a la renderización en motores de juego
Se entiende por renderización el proceso de generación de una imagen final (fotograma) en la pantalla a partir de una colección de objetos en una escena.
El software usado para renderizar un fotograma se conoce como un motor de renderización, y estos motores se categorizan típicamente de la siguiente manera:
Renderización fuera de línea: diseñado para una renderización de alta calidad que prioriza la calidad por sobre el tiempo de procesamiento. Se suelen usar en aplicaciones en las que el tiempo de renderización no es importante comparado con la calidad del fotograma renderizado final.
Renderización en tiempo real: diseñado teniendo el rendimiento en mente y renderiza fotogramas rápidamente. Los objetivos de tasa de fotogramas de tiempo real típicos son de 30 (33 ms), 60 (16 ms) y 120 (8 ms) fotogramas por segundo (FPS), pero las tasas de fotogramas reales pueden variar con el tiempo, según varios factores. Los proyectos desarrollados con renderización en tiempo real deben encontrar un equilibrio entre rendimiento y calidad para mantener las tasas de fotogramas consistentes. Los motores de renderización en tiempo real se usan generalmente para medios interactivos, como videojuegos, simulaciones y visualizaciones arquitectónicas.
Unreal Engine es un conjunto poderoso de herramientas para la renderización en tiempo real para satisfacer las necesidades de una variedad de plataformas, desde computadoras de escritorio potentes hasta móviles. Unreal Engine ofrece renderizados fuera de línea y en tiempo real de alta calidad. Se puede usar para crear cualquier cosa, desde experiencias 2D y 3D interactivas en plataformas de escritorio, de consola y móviles, hasta la renderización de fotogramas finales para producciones televisivas y cinemáticas.
A diferencia de otros motores en tiempo real en el mercado, Unreal Engine ofrece muchas características propias, diseñadas específicamente teniendo en cuenta el rendimiento y el renderizado en tiempo real. El objetivo es reducir la complejidad en el desarrollo y obtener resultados más rápidos, y al mismo tiempo mantener el rendimiento y la alta calidad.
Las características, como el sistema de reflejos e iluminación global de Lumen, la geometría virtualizada Nanite y los mapas de sombras virtuales son pasos importantes para alcanzar este objetivo de remover la complejidad durante el desarrollo con características que “simplemente funcionan” para aplicaciones de escritorio y consola. Las plataformas móviles soportan la iluminación dinámica y flujos de trabajo de iluminación precalculados que requieren que se haga bake de la iluminación en las texturas.
Introducción a la renderización en Unreal Engine
Los motores de juego realizan una serie de pasos, generalmente llamados pipeline de renderización, para renderizar una imagen (o fotograma) en el visor. Esta sección describe cómo Unreal Engine hace esto con una trayectoria de renderizado diferido predeterminada, y los compara con los pasos de trayectoria de renderizado diferido de Unity cuando sea apropiado.
El motor de Unity viene con tres pipelines de renderización diferentes: incorporado, universal y de alta definición. Cada pipeline está diseñado para casos de uso específico y se selecciona generalmente antes de comenzar un nuevo proyecto.
Unreal Engine viene con un pipeline de renderización unificada que escala características individuales basadas en la plataforma meta: desde dispositivos móviles o portátiles hasta la generación actual de consolas y PC. Esto significa que puedes elegir una trayectoria de renderizado y características compatibles que se ajusten mejor a tu proyecto sin quedar encerrado en un camino singular.
El pipeline de renderización del Unreal Engine puede usarse con su trayectoria de renderizado diferido predeterminada o puede configurarse para que se ejecute en una trayectoria de renderizado directo. Además, puede habilitar la trayectoria de renderizado móvil para acomodar los dispositivos de potencias inferiores, incluido el renderizador móvil Vulkan. Para obtener más información sobre las características de renderización compatibles para cada trayectoria de renderizado, lee la documentación de Supported Features by Rendering Path (Características compatibles al renderizar la ruta).
La imagen debajo muestra una visualización de alto nivel de los pasos que Unreal Engine realiza en cada fotograma para renderizar una imagen final usando la trayectoria de renderizado diferido:
El proceso fluye de izquierda a derecha, y los pasos 2 a 5 ocurren en paralelo.
Debajo, encontrarás más información sobre cada uno de estos pasos en el pipeline de renderizado y sobre lo que se necesita para renderizar cada fotograma.
Preparación de escena y obstrucciones
Unreal Engine tiene tres subprocesos principales: el del juego (CPU), el de trazado y el de la GPU.
Antes de comenzar con el proceso de renderización, la rama de juego (o CPU) reúne las transformaciones de todos los objetos en la escena. Esto incluye procesar todas las animaciones, las simulaciones físicas y la inteligencia artificial (IA) antes de reunir la transformación final de cada objeto.
La información de transformación luego se transmite a la rama de trazado en la CPU. La rama de trazado ejecuta el proceso de supresión, que construye una lista de objetos visibles en el visor de cámara actual y elimina todos los otros objetos que no son visibles para la cámara. No se necesita trazar estos objetos, y no renderizarlos mejora el rendimiento.
Este proceso realiza los siguientes pasos (en orden):
Supresión de distancia: elimina todos los objetos que están más lejos que una distancia específica de la cámara.
Supresión de tronco: elimina todos los objetos que no están visibles dentro del tronco (vista) de la cámara.
Eliminación de obstrucciones: verifica de forma precisa el estado de visibilidad de todos los objetos restantes en la escena. Este método es costoso y, por lo tanto, se realiza al final del proceso de oclusión, cuando los objetos visibles restantes se evalúan un poco más para ver si están ocluidos (ocultos) por otros objetos.
La lista final de objetos visibles se transmite a la rama de la GPU para comenzar el proceso de renderización.
Equivalente de Unity
Unity realiza la eliminación de obstrucciones y tronco durante el pipeline de renderización. Además, puede realizar la supresión de distancia con su API CullingGroup. Una combinación de estas técnicas crea una lista final de objetos visibles en la escena.
Renderización de geometría
En este paso, Unreal Engine recorre la lista de objetos visibles en la escena y los prepara para el próximo paso donde convierte datos de vértice 3D en datos de pixel que se muestran en la pantalla.
shaders de vértice
Un shader es un bit de código que se ejecuta directamente en la GPU y se usa para realizar una serie de cálculos. Son eficientes, y la GPU puede ejecutar muchos cálculos de shader en paralelo.
El shader de vértices realiza los siguientes pasos:
Convierte las posiciones de vértice locales en posición de entorno: los datos de vértice de objeto se almacenan en un espacio local, pero una vez que el objeto se ubica en el entorno, se debe convertir la información del vértice a coordenadas del espacio del entorno.
Controla la coloración y el sombreado del vértice: el shader de vértices controla el alisador del vértice así como los datos de colores del vértice en el objeto mismo.
Puede aplicar compensaciones adicionales a las posiciones del vértice: el shader de vértices puede compensar la posición de cualquier vértice en el visor para lograr efectos específicos. Esto se logra a través del material del objeto y se lo llama compensación de posición del entorno.
Pase de profundidad
Antes de renderizar objetos individuales, Unreal Engine realiza un pase de profundidad, o un pase Z temprano, para determinar la ubicación de los objetos en relación unos con otros. Esto evita una situación en la que Unreal Engine renderiza los mismos pixeles en el visor varias veces. Esto se conoce como superposición y puede tener un impacto significativo de rendimiento. El motor intenta evitar eso tanto como sea posible.
Peticiones de trazado
Después del pase de profundidad, la GPU renderiza cada objeto al trazar todos polígonos que comparten las mismas propiedades al mismo tiempo, como mallas y materiales. Esto se conoce como petición de trazado.
Todos los polígonos de un objeto a los que se les asigna el mismo material se cuentan como la misma petición de trazado. Sin embargo, cada material único necesita su propia petición de trazado independiente. Por ejemplo, cada objeto en el visor necesita por lo menos una petición de trazado, pero podría tener más dependiendo de la cantidad de materiales asignados al objeto.
La geometría virtualizada de Nanite de Unreal Engine renderiza todos los polígonos para todos los objetos con un material dado al mismo tiempo. Las estimaciones de fotogramas ya no están limitadas por el conteo de polígonos, las peticiones de trazado o el uso de memoria de malla.
Equivalente de Unity
El pipeline de renderización de Unity realiza pasos similares, donde hace un pase de profundidad y usa peticiones de trazado para trazar los objetos en la escena.
Rasterización y el búfer de geometría
El proceso de rasterización convierte los datos de vértice 3D en datos de pixel 2D que se muestran en su visor. Este proceso comienza luego de que el shader de vértices termina de procesar todos los datos.
El Búfer de geometría (GBuffer) de Unreal Engine incluye una serie de imágenes que almacena información sobre la geometría en la escena. Estas imágenes típicamente incluyen cosas como información de iluminación para el color base, entorno normal, metálico, rugosidad y especular en la escena. Estas imágenes en el GBuffer se componen para conformar la imagen final que ve en el visor.
El proceso de conversión de estas imágenes compuestas ocurre para cada fotograma que se renderiza, y para cada petición de trazado donde los datos de vértice se convierten a datos de pixel, y traza las partes correctas de las imágenes para el GBuffer.
Equivalente de Unity
La trayectoria de renderizado diferido de Unity también usa un GBuffer para almacenar información crítica sobre la escena. En el caso de Unity, el GBuffer almacena información similar sobre la escena (a la que se hace referencia con distintos nombres): información emisiva/de iluminación, albedo, especular y normal para los objetos.
Texturas de renderización
Unreal Engine renderiza texturas usando la transmisión de texturas para optimizar la carga de texturas en la escena. El sistema de transmisión de textura usa mapas de mip de textura. Estas son secuencias precalculadas de imágenes de la misma textura con resoluciones diferentes. Se puede pensar esto como niveles de detalle (LOD) para texturas en lugar de mallas. El motor crea automáticamente estos mapas de mip, donde cada imagen tiene la mitad de la resolución que la anterior.
Unreal Engine transmite el mapa de mip de textura durante la partida y se basa en la distancia de la cámara. Esto se realiza automáticamente para optimizar el consumo de memoria y el ancho de la banda, así como para reducir el ruido cuando se está lejos de la cámara.
Los tamaños de la textura deben tener una potencia de 2 para recibir mapas de mip. Los tamaños de textura comunes incluyen 3840 x 2160 pixeles (4K) y 1920 x 1080 pixeles (HD). Hay que tener en cuenta que no es necesario que las texturas tengan un radio específico, una textura de 1920 x 480 pixeles también recibirá mapas de mip.
Equivalente de Unity
El sistema de transmisión de mapa de mip de Unity usa mapas de mip de textura para transmitir sus texturas en el tiempo de ejecución. Al igual que Unreal Engine, este sistema transmite el mapa de mip de textura apropiado automáticamente basado en la distancia y el ángulo con la cámara.
Materiales y shaders de pixel
Una vez renderizados todos los objetos al GBuffer, Unreal Engine comienza a sombrear cada objeto en la pantalla mediante el uso de las propiedades del material de cada objeto con el shader de pixel.
Un shader de pixel realiza una serie de cálculos para modificar el color de un pixel en el visor. Los shaders de pixel se ejecutan en el GPU y son extremadamente eficientes. Ellos conducen el sistema de material de Unreal Engine y se usan cuando se calcula la iluminación, la niebla, los reflejos y los efectos de posprocesado.
El sistema de material usa plantillas de shader de lenguaje de shader de alto nivel (HLSL) junto con el editor de materiales para crear los materiales finales que se aplican a los objetos en el visor. Estos materiales pueden usar parámetros, como texturas, para definir el aspecto de cada objeto.
Equivalente de Unity
Unity incluye varios shaders prefabricados (equivalentes a materiales en Unreal Engine) junto con su Shader Graph para escribir shaders para tu proyecto. El editor de materiales (Material Editor) de Unreal Engine es el equivalente al Shader Graph de Unity.
Reflejos
Luego de sombrear todos los objetos en la escena, Unreal Engine comienza a renderizar los reflejos del objeto basado en las propiedades de su material.
Unreal Engine usa cuatro sistemas para renderizar reflejos a la escena. Estos sistemas se ejecutan en el siguiente orden:
Capturas de reflejo: precalculadas y almacenadas en un mapa de cubo estático en una ubicación específica.
Reflejos en plano: captura los reflejos desde un plano y hacia este.
Reflejos del espacio del visor (SSR): usa información del visor disponible para trazar reflejos precisos para los objetos en tiempo real.
Reflejos de Lumen: solucionan reflejos para el rango completo de valores de rugosidad en la escena. Estos reflejos incluyen soporte para la luz cenital, materiales de capa transparentes, translucidez, e incluso materiales de agua de capas únicos.
Unreal Engine integra estos tres métodos, le da prioridad a los reflejos en espacio de pantalla, luego recurre a los reflejos en plano y, finalmente, a las capturas de reflejo. El resultado de reflejo final se combina con las imágenes metálicas, el especular y la rugosidad en el GBuffer.
Si usas la Iluminación global de Lumen, los reflejos de Lumen se usarán automáticamente. Sin embargo, puedes usar los reflejos de Lumen sin Lumen GI, en cuyo caso Unreal Engine usará iluminación baked con reflejos de Lumen.
Equivalente de Unity
Las Reflection Probes de Unity otorgan una funcionalidad similar y suelen calcular de antemano los datos de reflejo para tu escena.
Sombras e iluminación estática
Luego de que los reflejos se renderizan, Unreal Engine renderiza las sombras y la iluminación estática para todos los objetos en la escena.
Unreal Engine usa su sistema de Iluminación global de Lightmass para calcular de antemano la información de iluminación para la escena. La información de sombra e iluminación se almacena en una textura de mapa UV de iluminación y esta textura se fusiona con el color de base del objeto cuando se renderiza el objeto en la escena.
Este sistema es muy rápido, pero requiere más memoria y se debe calcular de antemano cada vez que haya un cambio en la escena.
El sistema de Iluminación global de Lightmass es una buena opción para los proyectos que apuntan a dispositivos de bajo consumo y móviles.
Equivalente de Unity
Los sistemas Progressive Lightmapper y el Enlighten Baked Global Illumination de Unity otorgan una funcionalidad similar cuando la iluminación se calcula de antemano para tu escena.
Sombras e iluminación dinámica
Luego de que la iluminación estática se renderiza, Unreal Engine renderiza sombras e iluminación (en tiempo real) dinámica con Lumen, su sistema de iluminación global dinámico.
Lumen es un sistema completo de iluminación global dinámica y de reflejo que está diseñado para las consolas de la próxima generación y PC de alta gama. El sistema usa varios métodos de trazado de rayos para solucionar la iluminación global y los reflejos a escala.
Lumen otorga rebotes difusos infinitos y trabaja sin problemas con la Geometría virtualizada de Nanite. Además, el sistema trabaja en conjunto con los mapas de sombras virtuales para crear sombras suaves en tiempo real y de alta resolución.
Lumen otorga reflejos de Lumen que solucionan reflejos para el rango completo de valores de rugosidad en la escena. Estos reflejos incluyen soporte para la luz cenital, materiales de capa transparentes, translucidez, e incluso materiales de agua de capas únicos.
Lumen reemplaza reflejos del espacio en pantalla cuando se utilizan en la escena.
Equivalente de Unity
Unity usa Enlighten Realtime Global Illumination para otorgar iluminación dinámica en la escena. Este sistema otorga iluminación global en tiempo real usando la información de visibilidad precalculada junto con mapas de luz para calcular el rebote de luz indirecto al momento de ejecución.
Esto se diferencia de Lumen, ya que Lumen no necesita ningún dato calculado de antemano para otorgar rebotes de luz indirectos.
Niebla y transparencia
Después de renderizar las sombras y la iluminación dinámica, Unreal Engine renderiza los efectos de transparencia y niebla.
Unreal Engine renderiza efectos de niebla con su sistema de Niebla de altura exponencial, que renderiza la densidad de la niebla basado en la altura y la distancia de la cámara. Además, el sistema puede generar niebla volumétrica.
Los objetos transparentes usan un material traslúcido y se renderizan en esta etapa, en el proceso. Cuando se usa la trayectoria de renderizado diferido, Unreal Engine usa la información disponible en el GBuffer para renderizar transparencia. Como alternativa, puedes configurar el material para usar la trayectoria de renderizado directo para producir un efecto de transparencia más preciso.
Equivalente de Unity
Unity soporta niebla cuadrada exponencial, exponencial y lineal en la escena.
Efectos de postprocesamiento
Una vez que la transparencia y la niebla están renderizadas, Unreal puede aplicar efectos adicionales sobre la imagen. Estos efectos se conocen como efectos de posprocesamiento porque se aplican luego de procesada la imagen final. Los efectos dependen del shader de pixel y usan la información disponible en el GBuffer.
Algunos efectos de posprocesamiento comunes incluyen bloom de luz, profundidad de campo, haces de luz, mapeo de tono y desenfoque de movimiento.
Como parte de este paso de posprocesamiento, Unreal Engine puede aplicar la superresolución temporal (TSR). TSR es una plataforma agnóstica de conversión temporal que Unreal Engine usa para renderizar hermosas imágenes en 4K. Las imágenes vienen a una fracción del costo al amortizar algunos de los cálculos de renderización costosos a través de muchos fotogramas.
En la cadena de renderización, la Temporal Super Resolution ocurre luego de incrementar la profundidad de campo y todo lo que sigue a continuación, como el desenfoque de movimiento, el bloom y demás.
Una vez que estos efectos se aplican al GBuffer, Unreal Engine renderiza la imagen final en el visor.
Los pasos descritos anteriormente generan un único fotograma en el visor. Estos pasos se repiten usualmente entre 30 a 60 veces por segundo, según los fotogramas por segundo objetivos del juego.
Equivalente de Unity
Unity viene con soluciones de posprocesamiento que se basan en el pipeline de renderización elegido. Muchos de los efectos disponibles son similares a los que están disponibles en Unreal Engine.
Además, Unity 6 también viene con un posprocesamiento temporal-espacial (STP), un escalador nativo basado en un software que usa técnicas de sobremuestreo temporal y espacial para producir una imagen suavizada de alta calidad.
Presentación de las características de renderización en Unreal Engine
Ahora que entiendes los pasos que Unreal Engine toma para renderizar un fotograma en el visor, puede sobtener más información sobre las características de renderización específicas que vienen con el motor.
Para obtener más información sobre las funciones de renderizado de Unreal Engine consulta la documentación Lighting the Environment (Cómo iluminar el entorno).