Cómo se representa el marco de Rise of the Tomb Raider


Rise of the Tomb Raider (2015) es la secuela del excelente reinicio de Tomb Raider (2013). Personalmente, encuentro ambas partes interesantes porque se alejaron de la serie original estancada y volvieron a contar la historia de Lara. En este juego, como en la precuela, el lugar central está ocupado por la trama, proporciona una mecánica fascinante de elaboración, caza y escalada / investigación.

Tomb Raider usó Crystal Engine, desarrollado por Crystal Dynamics, también usado en Deus Ex: Human Revolution . La secuela utilizó un nuevo motor llamado Foundation, desarrollado previamente para Lara Croft y el Templo de Osiris (2014). Su representación generalmente se puede describir como un motor de mosaico con un pase de iluminación preliminar, y luego descubriremos lo que esto significa. El motor le permite elegir entre los renderizadores DX11 y DX12; Elegí este último, por las razones que discutimos a continuación. Para capturar el marco, se utilizó Renderdoc 1.2 en la Geforce 980 Ti, el juego incluye todas las funciones y decoraciones.

Marco analizado



Para evitar spoilers, diré que en este marco los malos están persiguiendo a Lara, porque ella está buscando un artefacto que están buscando. Este conflicto de intereses no puede resolverse sin armas. Lara se coló en la base enemiga por la noche. Elegí un marco con iluminación atmosférica y de contraste, en el que el motor puede mostrarse.

Profundidad de avance


Aquí, se realiza la optimización habitual para muchos juegos: un pequeño paso preliminar de la profundidad (aproximadamente 100 llamadas de sorteo). El juego representa los objetos más grandes (y no los que ocupan más espacio en la pantalla) para aprovechar las características del procesador de video Early-Z. Lea más sobre esto en un artículo de Intel . En resumen, las GPU pueden evitar ejecutar un sombreador de píxeles si pueden determinar que se superpone con el píxel anterior. Este es un pasaje de bastante bajo costo, que llena previamente el búfer Z con valores de profundidad.

En este punto, descubrí una técnica interesante de nivel de detalle (LOD) llamada "fizzle" o "tablero de ajedrez". Esta es una forma común de mostrar u ocultar gradualmente objetos a distancia, para que luego puedan reemplazarse con una malla de menor calidad u ocultarlos por completo. Mira este camión. Parece que está renderizando dos veces, pero de hecho está renderizando con un LOD alto y un LOD bajo en la misma posición. Cada uno de los niveles representa aquellos píxeles que el otro no renderizó. El primer LOD tiene 182,226 vértices, y el segundo LOD tiene 47,250. A gran distancia son indistinguibles, pero uno de ellos es tres veces menos costoso. En este marco, LOD 0 casi desaparece y LOD 1 se renderiza casi por completo. Después de la desaparición completa de LOD 0, solo se representará LOD 1.


LOD 0


LOD 1


La textura pseudoaleatoria y el coeficiente de probabilidad nos permite descartar píxeles que no han pasado el valor umbral. Esta textura se usa en ROTR. Uno puede preguntarse por qué no se usa la mezcla alfa. La mezcla alfa tiene muchos inconvenientes en comparación con el desvanecimiento por fizzle.

  1. Conveniencia para el pasaje preliminar de las profundidades: gracias a la representación de un objeto opaco con agujeros hechos en él, podemos renderizar en el pasaje preliminar y usar temprano-z. Los objetos con mezcla alfa en una etapa tan temprana no se procesarán en el búfer de profundidad debido a problemas de clasificación.
  2. La necesidad de sombreadores adicionales : si se usa un renderizador diferido, el sombreador de los objetos opacos no contiene ninguna iluminación. Si necesita reemplazar un objeto opaco por uno transparente, entonces necesita una opción separada en la que haya iluminación. Además de aumentar la cantidad de memoria necesaria y la complejidad debido a al menos un sombreador adicional para todos los objetos opacos, deben ser precisos para evitar que los objetos avancen. Esto es complicado por muchas razones, pero todo se reduce al hecho de que el renderizado ahora se realiza en una ruta de código diferente.
  3. Redibujos más grandes : la combinación alfa puede crear redibujos grandes, y con un cierto nivel de complejidad de los objetos, puede ser necesaria una gran fracción del ancho de banda para sombrear el LOD.
  4. Conflictos Z : los conflictos z son un efecto de parpadeo cuando dos polígonos se representan a una profundidad muy cercana entre sí. En este caso, la inexactitud de los cálculos de coma flotante obliga a que se procesen a su vez. Si renderizamos dos LOD consecutivos, ocultando gradualmente uno y mostrando el segundo, entonces pueden causar un conflicto z, porque están muy cerca el uno del otro. Siempre hay formas de evitar esto, por ejemplo, prefiriendo un polígono a otro, pero este sistema es complejo.
  5. Efectos de Z-Buffer : Muchos efectos como SSAO usan solo el buffer de profundidad. Si renderizamos objetos transparentes al final de la tubería cuando la oclusión ambiental ya se completó, no podríamos tenerlo en cuenta.

La desventaja de esta técnica es que se ve peor que la mezcla alfa, pero un buen patrón de ruido, desenfoque después de la efervescencia o anti-aliasing temporal puede ocultarlo casi por completo. En este sentido, ROTR no hace nada particularmente inusual.

Pase normal


Crystal Dynamics utiliza un patrón de iluminación bastante inusual en sus juegos, que cubriremos en el pasillo de iluminación. Por ahora, basta con decir que el motor no tiene un pase G-buffer; al menos en la medida en que es familiar en otros juegos. En este pasaje, los objetos transmiten solo información sobre la profundidad y las normales a la salida. Las normales se registran en el destino de representación del formato RGBA16_SNORM en el espacio mundial. Es curioso que este motor utilice el esquema Z-up, no el Y-up (el eje Z se dirige hacia arriba, no el eje Y), que se usa con mayor frecuencia en otros motores / paquetes de modelado. El canal alfa contiene brillo, que luego se desempaqueta como exp2(glossiness * 12 + 1.0) . El valor de brillo también puede ser negativo, porque el signo se usa como una bandera que indica si la superficie es metálica. Esto se puede notar por sí solo, porque todos los colores oscuros en el canal alfa están relacionados con objetos metálicos.

RGB
Normal.xNormal.yNormal.zBrillo + Metalidad


Normal


Brillo / metalidad

Ventajas de la partida


¿Recuerda que en la sección "Profundidad preliminar", hablamos sobre el ahorro de costos de píxeles? Volveré un poco para ilustrarlo. Toma la siguiente imagen. Esto representa la parte detallada de la montaña en el búfer normal. Renderdoc resaltó amablemente los píxeles que pasaron la prueba de profundidad con verde, y los que no la pasaron con rojo (no se mostrarán). El número total de píxeles que se representarían sin este pase preliminar es aproximadamente igual a 104518 (calculado en Photoshop). El número total de píxeles que realmente se representan es 23858 (calculado por Renderdoc). ¡Ahorre alrededor del 77%! Como vemos, con un uso inteligente, este pase preliminar puede dar una gran ganancia, y solo requiere alrededor de cien llamadas de extracción.

Grabar comandos multiproceso


Vale la pena señalar un aspecto interesante, por lo que elegí el renderizador DX12: grabar comandos de subprocesos múltiples. En API anteriores, como DX11, la representación generalmente se realiza en un solo hilo. El controlador de gráficos recibió comandos de representación del juego y transmitió constantemente solicitudes de GPU, pero el juego no sabía cuándo ocurriría esto. Esto conduce a la ineficiencia, porque el controlador debe de alguna manera adivinar lo que la aplicación está tratando de hacer, y no escala a múltiples hilos. Las API más nuevas, como DX12, transfieren el control a un desarrollador que puede decidir cómo escribir comandos y cuándo enviarlos. Aunque Renderdoc no puede mostrar cómo se realiza la grabación, verá que hay siete pases de color marcados como Color Pass N, y cada uno de ellos está envuelto en un par de ExecuteCommandList: Reset / Close. Marca el comienzo y el final de la lista de comandos. La lista representa aproximadamente 100-200 llamadas de sorteo. Esto no significa que fueron grabados usando varias transmisiones, pero lo insinúa.

Huellas en la nieve


Si miras a Lara, puedes ver que cuando se mueve frente a la captura de pantalla, deja huellas en la nieve. En cada cuadro, se ejecuta un sombreador de cálculo, que registra las deformaciones en ciertas áreas y las aplica en función del tipo y la altura de la superficie. Aquí, solo se aplica el mapa normal a la nieve (es decir, la geometría no cambia), pero en algunas áreas donde la nieve es más gruesa, la deformación se lleva a cabo. También puedes ver cómo la nieve "cae" en su lugar y llena las huellas dejadas por Lara. Esta técnica se describe con mucho más detalle en la GPU Pro 7 . La textura de urdimbre de nieve es un tipo de mapa de elevación que rastrea los movimientos de Lara y se pega alrededor de los bordes para que el sombreador de muestreo pueda aprovechar este plegado.

Atlas de sombras


Al crear mapeo de sombras, se utiliza un enfoque bastante común: empacar tantas tarjetas de sombras como sea posible en una textura de sombra común. Tal atlas de sombras es en realidad una gran textura de 16 bits con un tamaño de 16384 × 8196. Esto le permite reutilizar y escalar de manera muy flexible los mapas de sombras en el atlas. En el marco que estamos analizando, se registran 8 mapas de sombras en el atlas. Cuatro de ellos se relacionan con la fuente principal de iluminación direccional (la luna, porque sucede en la noche), porque usan mapas de sombras en cascada, una técnica bastante estándar de sombras de larga distancia para iluminación direccional, que ya expliqué un poco antes . Más interesante aún, varias fuentes de proyección y foco también se incluyen en la captura de este marco. El hecho de que se graben 8 mapas de sombras en este marco no significa que solo haya 8 fuentes de iluminación de sombras proyectadas en él. El juego puede almacenar en caché los cálculos de sombras, es decir, la iluminación que no ha cambiado ni la posición de la fuente ni la geometría del alcance no debe actualizar su mapa de sombras.


Parece que la representación de mapas de sombras también se beneficia al escribir comandos de subprocesos múltiples en la lista, y en este caso, se escribieron hasta 19 listas de comandos para representar mapas de sombras.

Sombras de la iluminación direccional.

Las sombras de la iluminación direccional se calculan antes del paso de la iluminación y luego se muestrean. No sé qué pasaría si hubiera varias fuentes de iluminación direccional en la escena.


Oclusión ambiental


Para la oclusión ambiental, ROTR le permite usar HBAO o una variante de HBAO + (esta técnica fue publicada originalmente por NVIDIA). Hay varias variaciones de este algoritmo, por lo que consideraré el que encontré en ROTR. Primero, el búfer de profundidad se divide en 16 texturas, cada una de las cuales contiene 1/16 de todos los valores de profundidad. La separación se realiza de tal manera que cada textura contiene un valor de un bloque 4 × 4 de la textura original que se muestra en la figura a continuación. La primera textura contiene todos los valores marcados en rojo (1), la segunda contiene los valores marcados en azul (2), y así sucesivamente. Si desea saber más sobre esta técnica, aquí hay un artículo de Louis Bavoil , quien también fue uno de los autores del artículo sobre HBAO.


El siguiente paso calcula la oclusión ambiental para cada textura, lo que nos da 16 texturas AO. Se genera una oclusión ambiental de la siguiente manera: el tampón de profundidad se muestrea varias veces, recreando la posición y acumulando el resultado del cálculo para cada una de las muestras. Cada textura de oclusión ambiental se calcula utilizando diferentes coordenadas de muestreo, es decir, en un bloque de 4x4 píxeles, cada píxel cuenta su propia parte de la historia. Esto se hace por razones de rendimiento. Cada píxel ya muestrea el búfer de profundidad 32 veces, y el efecto completo requerirá 16 × 32 = 512 muestras, lo que es un fracaso incluso para las GPU más potentes. Luego se recombinan en una textura de pantalla completa, que resulta ser bastante ruidosa, por lo que para suavizar los resultados justo después de eso, se realiza un pase de desenfoque de pantalla completa. Vimos una solución muy similar en Shadow of Mordor .

imagen

Piezas HBAO

imagen

HBAO completo con ruido

imagen

Desenfoque horizontal completo HBAO

imagen

Listo HBAO

Pre-pase de iluminación en mosaico


Light Prepass es una técnica bastante inusual. La mayoría de los equipos de desarrollo usan una combinación de cálculo de iluminación diferida + directa (con variaciones, por ejemplo, con mosaico, agrupación) o completamente directa para algunos efectos del espacio de la pantalla. La técnica de pre-iluminación es tan inusual que merece una explicación. Si el concepto de iluminación tradicional retrasada es separar las propiedades de los materiales de la iluminación, entonces la idea de separar la iluminación de las propiedades de los materiales es la piedra angular del paso preliminar de la iluminación. Aunque esta redacción parece un poco tonta, la diferencia con la iluminación diferida tradicional es que almacenamos todas las propiedades del material (como albedo, color especular, rugosidad, metalidad, microoclusión, emisivo) en un enorme G-buffer, y lo usamos más tarde como datos de entrada para pases de iluminación posteriores. La iluminación diferida tradicional puede presentar una gran carga en el rendimiento; cuanto más complejos son los materiales, más información y operaciones se necesitan en el G-buffer. Sin embargo, en el pase de iluminación preliminar, primero acumulamos toda la iluminación por separado, utilizando la cantidad mínima de datos, y luego los aplicamos en pases posteriores a los materiales. En este caso, la iluminación es suficiente solo para condiciones normales, aspereza y metalidad. Los sombreadores (aquí se usan dos pases) generan datos en tres formatos de renderizado RGBA16F. Uno contiene iluminación difusa, el segundo contiene iluminación especular y el tercero contiene iluminación ambiental. En este punto, todos los datos sombra se tienen en cuenta. Es curioso que en el primer paso (iluminación difusa + espejo) para un pase de pantalla completa, se use un cuadrángulo de dos triángulos, y en otros efectos, se usa un triángulo de pantalla completa (por qué esto es importante, puede averiguarlo aquí ). Desde este punto de vista, todo el marco no es integral.

imagen

Iluminación difusa

imagen

Iluminación del espejo

imagen

Iluminación ambiental

Optimización de azulejos

La iluminación de azulejos es una técnica de optimización diseñada para representar una gran cantidad de fuentes de luz. ROTR divide la pantalla en mosaicos de 16 × 16, y luego guarda información sobre qué fuentes se cruzan con cada mosaico, es decir, los cálculos de iluminación se realizarán solo para aquellas fuentes relacionadas con los mosaicos. Al comienzo del marco, se inicia una secuencia de sombreadores computacionales, que determinan qué fuentes se relacionan con los mosaicos. En la etapa de iluminación, cada píxel determina en qué mosaico se encuentra y recorre cada fuente de luz en el mosaico, realizando todos los cálculos de iluminación. Si las fuentes están vinculadas a los mosaicos de manera eficiente, puede ahorrar una gran cantidad de cómputo y la mayor parte del ancho de banda, así como aumentar la productividad.

Zoom de profundidad

El muestreo ascendente basado en profundidad es una técnica interesante útil en este y pases posteriores. A veces, los algoritmos computacionalmente caros no se pueden representar a resolución completa, por lo que se representan a una resolución más baja y luego se amplían. En nuestro caso, la iluminación ambiental se calcula en la mitad de la resolución, es decir, después de los cálculos, la iluminación debe recrearse correctamente. En la forma más simple, se toman 4 píxeles de baja resolución y se interpolan para obtener algo parecido a la imagen original. Esto funciona para transiciones suaves, pero no se ve bien en las discontinuidades, porque allí mezclamos valores no relacionados que pueden ser adyacentes en el espacio de la pantalla, pero distantes entre sí en el espacio mundial. En soluciones a este problema, generalmente se toman varias muestras de búfer de profundidad y se comparan con la muestra de profundidad que queremos recrear. Si la muestra está demasiado lejos, entonces no la tenemos en cuenta al reconstruir. Tal esquema funciona bien, pero significa que el sombreador de recreación requiere mucho ancho de banda.

ROTR hace un movimiento complicado con el descarte temprano de la plantilla. Después de pasar las normales, el búfer de profundidad está completamente lleno, por lo que el motor realiza un pase de pantalla completa, marcando todos los píxeles interrumpidos en el búfer de plantilla. Cuando llega el momento de recrear el búfer de iluminación ambiental, el motor usa dos sombreadores: uno es muy simple para áreas sin espacios de profundidad, el otro es más complejo para píxeles con espacios. La plantilla temprana descarta píxeles si no pertenecen a la región correspondiente, es decir, solo hay costos en las regiones necesarias. Las siguientes imágenes son mucho más claras:

imagen

Iluminación ambiental de media resolución

imagen

Escalando las profundidades del interior

imagen

Iluminación ambiental de resolución completa, sin costillas

imagen

Escalando las profundidades de las costillas

imagen

Listo iluminación ambiental

imagen

Vista de media resolución

imagen

Una vista de primer plano de la imagen recreada

Después del paso preliminar de la iluminación, la geometría se transfiere al transportador, solo que esta vez cada objeto muestra texturas de iluminación, textura de oclusión ambiental y otras propiedades de materiales que no escribimos en el G-buffer desde el principio. Esto es bueno, porque el ancho de banda se guarda en gran medida aquí debido al hecho de que no es necesario leer un montón de texturas para escribirlas en un gran G-buffer, y luego leerlas / decodificarlas nuevamente. El inconveniente obvio de este enfoque es que toda la geometría necesita retransmitirse, y la textura del paso preliminar de la iluminación en sí misma representa una gran carga en el rendimiento. Me preguntaba por qué no usar un formato más ligero, por ejemplo R11G11B10F, para las texturas preliminares del pase de iluminación, pero hay información adicional en el canal alfa, por lo que esto sería imposible. Sea como fuere, esta es una solución técnica interesante. En este punto, toda la geometría opaca ya está renderizada y encendida. Tenga en cuenta que incluye objetos emisores de luz, como el cielo y la pantalla del portátil.


Reflexiones


Esta escena no es un buen ejemplo para demostrar reflexiones, así que elegí otra. El sombreador de reflejos es una combinación bastante complicada de ciclos que se puede reducir a dos partes: uno muestra mapas cúbicos y el otro realiza SSR (Reflexión del espacio de la pantalla - cálculo de reflexiones en el espacio de la pantalla); todo esto se hace en una pasada y al final se mezcla teniendo en cuenta el coeficiente que determina si el SSR detectó reflexión (probablemente el coeficiente no es binario, sino que es un valor en el intervalo [0, 1]). SSR funciona de manera estándar para muchos juegos: rastrea repetidamente el búfer de profundidad, tratando de encontrar la mejor intersección entre el rayo reflejado por la superficie sombreada y otra superficie en cualquier lugar de la pantalla. SSR funciona con la cadena mip de la escala previamente reducida del búfer HDR actual, y no con todo el búfer.

También existen factores de corrección como el brillo de la reflexión, así como la peculiar textura de Fresnel, que se calculó antes de este pasaje, en función de las condiciones normales y la rugosidad. No estoy completamente seguro, pero después de estudiar el código de ensamblaje, me parece que ROTR solo puede calcular SSR para superficies lisas. El motor no tiene una cadena de mip borrosa después de la etapa SSR, que existe en otros motores , y ni siquiera hay nada como rastrear el búfer de profundidad utilizando rayos, que varía según la rugosidad . En general, las superficies más rugosas reciben reflejos de los mapas cúbicos, o no los reciben en absoluto. Sin embargo, donde SSR funciona, su calidad es muy alta y estable, teniendo en cuenta el hecho de que no se acumula con el tiempo y no se realiza el desenfoque espacial. Los datos alfa también admiten SSR (en algunos templos se pueden ver reflejos muy hermosos en el agua) y esta es una buena adición que no se ve con frecuencia.

imagen

Reflexiones para

imagen

Tampón de reflexión

imagen

Reflexiones después

Niebla encendida



En nuestra escena, la niebla está mal representada porque oscurece el fondo y, por lo tanto, es creada por partículas, por lo que nuevamente utilizamos el ejemplo con reflejos. La niebla es relativamente simple, pero bastante efectiva. Hay dos modos: global, el color general de la niebla y el color de la dispersión interna obtenida del mapa cúbico. Quizás el mapa cúbico fue tomado nuevamente de los mapas de reflexión cúbicos, o tal vez creado de nuevo. En ambos modos, la rarefacción de la niebla se toma de la textura global de rarefacción, en la que las curvas de rarefacción se empaquetan para varios efectos. En tal esquema, es notable que proporcione una niebla iluminada de muy bajo costo, es decir dispersando los cambios internos en el espacio, creando la ilusión de la interacción de la niebla con la iluminación distante. Este enfoque también se puede utilizar para la dispersión atmosférica hacia el interior cerca del cielo.

imagen

Niebla a

imagen

Niebla después

Iluminación volumétrica


En las primeras etapas del marco, se realizan varias operaciones para prepararse para la iluminación volumétrica. Se copian dos memorias intermedias de la CPU a la GPU: índices de fuente de luz y datos de fuente de luz. Ambos son leídos por un sombreador computacional que genera una textura 3D de 40x23x16 de la vista de la cámara que contiene el número de fuentes de luz que cruzan esta área. La textura es 40 × 23 porque cada mosaico ocupa 32 × 32 píxeles (1280/32 = 40, 720/32 = 22.5), y 16 es el número de píxeles en profundidad. La textura no incluye todas las fuentes de luz, sino solo aquellas que están marcadas como voluminosas (hay tres en nuestra escena). Como veremos a continuación, hay otros efectos volumétricos falsos creados por texturas planas. La textura mostrada tiene una resolución más alta: 160x90x64. Después de determinar el número de fuentes de luz por mosaico y su índice, tres sombreadores computacionales se ejecutan secuencialmente, realizando las siguientes operaciones:

  1. El primer paso determina la cantidad de luz que ingresa a la celda dentro del volumen en forma de una pirámide de visibilidad. Cada célula acumula la influencia de todas las fuentes de luz, como si tuvieran partículas suspendidas que reaccionan a la luz y devuelven parte de ella a la cámara.
  2. La segunda pasada difumina la iluminación con un radio pequeño. Esto probablemente sea necesario para evitar parpadeos al mover la cámara, porque la resolución es muy baja.
  3. El tercer paso omite la textura del volumen de adelante hacia atrás, agregando gradualmente la influencia de cada fuente y dando la textura final. De hecho, simula la cantidad total de iluminación entrante a lo largo del haz a una distancia dada.Dado que cada celda contiene una parte de la luz reflejada por las partículas hacia la cámara, en cada una de ellas recibiremos una contribución conjunta de todas las celdas previamente pasadas. Este pasaje también se difumina.

Cuando todo esto se completa, obtenemos una textura 3D que informa cuánta luz recibe una posición en particular en relación con la cámara. Todo lo que queda por hacer en el pasaje de pantalla completa es determinar esta posición, encontrar el vóxel correspondiente de la textura y agregarlo al búfer HDR. El sombreador de iluminación en sí es muy simple y contiene solo unas 16 instrucciones.

imagen

Iluminación volumétrica

imagen

Iluminación volumétrica después

Renderizado de cabello


Si la función PureHair no está habilitada, las capas estándar de cabello se representan una encima de la otra. Esta solución todavía se ve genial, pero me gustaría centrarme en la última tecnología. Si la función está habilitada, el marco comienza con una simulación del cabello de Lara con una secuencia de sombreadores computacionales. La primera parte de Tomb Raider utilizó una tecnología llamada TressFX, y en la secuela Crystal Dynamics implementó una tecnología mejorada. Después de los cálculos iniciales, obtenemos hasta 7 buffers. Todos ellos se usan para controlar el cabello de Lara. El proceso es el siguiente:

  1. Inicie un sombreador computacional para calcular valores de movimiento basados ​​en posiciones anteriores y actuales (para desenfoque de movimiento)
  2. 1×1 ()
  3. 122 (Triangle Strip) ( — ). , . 7 , . , , . « ».
  4. / quad , , . , , .
  5. 4, ( « »)

Si está interesado en aprender más sobre esto, entonces AMD tiene muchos recursos y presentaciones , porque es una biblioteca pública creada por la compañía . Estaba confundido por la etapa anterior a la etapa 1, en la que se realiza la misma llamada de sorteo que en la etapa 3, se dice que representa solo valores de profundidad, pero de hecho el contenido no se representa, y esto es interesante; quizás Renderdoc no me está diciendo nada. Sospecho que puede haber intentado ejecutar una solicitud de representación condicional, pero no veo ninguna llamada de predicción.

imagen

Cabello recogido

imagen

Píxeles visibles del cabello

imagen

Cabello sombreado

Representación en mosaico de datos alfa y partículas


Los objetos transparentes vuelven a utilizar la clasificación de las fuentes de luz en mosaico calculadas para el paso de iluminación preliminar de las fichas. Cada objeto transparente calcula su propia iluminación en una pasada, es decir, el número de instrucciones y ciclos se vuelve bastante aterrador (por eso se utilizó la pasada preliminar de iluminación para objetos opacos). ¡Los objetos transparentes incluso pueden realizar reflejos en el espacio de la pantalla si están encendidos! Cada objeto se procesa en orden de atrás hacia adelante directamente en el búfer HDR, incluyendo vidrio, llama, agua de rutina, etc. El pasaje alfa también muestra los bordes resaltados cuando Lara se enfoca en algún objeto (por ejemplo, una botella con una mezcla combustible en una caja a la izquierda).


Sin embargo, las partículas se convierten en un búfer de media resolución para suavizar la enorme carga en el ancho de banda creado por su repintado, especialmente cuando se utilizan muchas partículas grandes que cubren la pantalla para crear niebla, niebla, llamas, etc. Por lo tanto, el tampón HDR y el tampón de profundidad disminuyen a la mitad en cada lado, después de lo cual comienza el procesamiento de partículas. Las partículas crean una gran cantidad de redibujado, algunos píxeles se sombrean unas 40 veces. El mapa de calor muestra lo que quiero decir. Dado que las partículas se renderizaron en la mitad de la resolución, se usa el mismo truco de zoom inteligente que en la iluminación ambiental (los espacios se marcan en la plantilla, el primer paso se convierte en píxeles internos, el segundo recrea los bordes). Puede notar que las partículas se convierten en otros efectos alfa, como la llama,brillo, etc. Esto es necesario para que el alfa se pueda clasificar correctamente en relación con, por ejemplo, el humo. También puede notar que aquí aparecen rayos de luz "volumétricos", provenientes de focos de seguridad. Se agregan aquí y no se crean en la etapa de iluminación volumétrica. Esta es una forma económica pero realista de crearlos a largas distancias.

imagen



imagen

-

imagen

1

imagen

2

imagen

3

imagen


imagen



imagen

-


ROTR realiza la velocidad de obturación y la corrección de tono en una sola pasada. Sin embargo, aunque generalmente creemos que la corrección gamma ocurre con la corrección de tono, este no es el caso aquí. Hay muchas formas de implementar la exposición, como hemos visto con otros juegos . El cálculo de la luminancia en ROTR es muy interesante y casi no requiere datos intermedios o pases, por lo que explicaremos este proceso con más detalle. Toda la pantalla se divide en 64 × 64 mosaicos, después de lo cual el cálculo de grupos (20, 12, 1) de 256 secuencias en cada uno comienza a llenar toda la pantalla. Cada hilo esencialmente realiza la siguiente tarea (el pseudocódigo se presenta a continuación):

 for(int i = 0; i < 16; ++i) { uint2 iCoord = CalculateCoord(threadID, i, j); // Obtain coordinate float3 hdrValue = Load(hdrTexture, iCoord.xyz); // Read HDR float maxHDRValue = max3(hdrValue); // Find max component float minHDRValue = min3(hdrValue); // Find min component float clampedAverage = max(0.0, (maxHDRValue + minHDRValue) / 2.0); float logAverage = log(clampedAverage); // Natural logarithm sumLogAverage += logAverage; } 

Cada grupo calcula la suma logarítmica de los 64 píxeles (256 hilos, cada uno de los cuales procesa 16 valores). En lugar de almacenar el valor promedio, guarda la suma y el número de píxeles realmente procesados ​​(no todos los grupos procesan exactamente 64 × 64 píxeles, porque, por ejemplo, pueden ir más allá de los bordes de la pantalla). Shader usa sabiamente el almacenamiento local de hilos para dividir la suma; cada flujo primero funciona con 16 valores horizontales, y luego los flujos separados resumen todos estos valores verticalmente, y finalmente el flujo de control de este grupo (flujo 0) agrega el resultado y los guarda en el búfer. Este búfer contiene 240 elementos, esencialmente dándonos el brillo promedio de muchas áreas de la pantalla. El siguiente comando inicia 64 hilos que recorren todos estos valores y los agregan,para obtener el brillo final de la pantalla. También regresa del logaritmo a unidades lineales.

No tengo mucha experiencia con técnicas de exposición, pero al leer esta publicación de Krzysztof Narkovic se aclararon algunas cosas. Es necesario guardar en una matriz de 64 elementos para calcular el promedio móvil, en el que puede ver los valores calculados anteriores y suavizar la curva para evitar cambios muy bruscos en el brillo, creando cambios bruscos en la velocidad de obturación. Este es un sombreador muy complejo y todavía no he descubierto todos sus detalles, pero el resultado final es el valor de la velocidad de obturación correspondiente al cuadro actual.

Después de encontrar las velocidades de obturación adecuadas, una pasada realiza la velocidad de obturación final más la corrección tonal. ROTR parece usar mapeo fotográfico de tonos, que explica el uso de medios logarítmicos en lugar de los medios habituales. La fórmula de corrección tonal en el sombreador (después de la exposición) se puede ampliar de la siguiente manera:



Una breve explicación se puede encontrar aquí . No pude entender por qué es necesaria una división adicional por Lm, porque cancela la influencia de la multiplicación. En cualquier caso, whitePoint es 1.0, por lo que el proceso no hace mucho en este marco, la imagen solo cambia la velocidad de obturación. ¡Ni siquiera hay un límite para los valores del intervalo LDR! Ocurre durante la gradación de color, cuando el cubo de color limita indirectamente valores superiores a 1.0.

imagen

Exposición a

imagen

Exposición después

Destello de lente


Los destellos de las lentes se representan de manera interesante. Un pequeño pase preliminar calcula una textura 1xN (donde N es el número total de elementos de deslumbramiento que se renderizarán como destellos de lente, en nuestro caso hay 28). Esta textura contiene el valor alfa de la partícula y alguna otra información no utilizada, pero en lugar de calcularlo a partir de una solicitud de visibilidad o algo similar, el motor lo calcula analizando el búfer de profundidad alrededor de la partícula en el círculo. Para hacer esto, la información sobre los vértices se almacena en un búfer disponible para el sombreador de píxeles.


Luego, cada elemento se representa como simples planos alineados con el plano emitidos por las fuentes de luz. Si el valor alfa es menor que 0.01, el valor NaN se asigna a la posición para que esta partícula no se rasterice. Son un poco como el efecto de floración y agregan brillo, pero este efecto en sí mismo se crea más tarde.

imagen

Lente estalla

imagen

Elementos de destello de lente

imagen

Destellos de lente después

Bloom


Bloom utiliza un enfoque estándar: reduciendo la resolución del búfer HDR, se aíslan los píxeles brillantes y luego su escala se incrementa secuencialmente con desenfoque para expandir su área de influencia. El resultado se amplía a la resolución de la pantalla y la composición se superpone encima. Hay un par de puntos interesantes que vale la pena explorar. Todo el proceso se realiza utilizando 7 sombreadores computacionales: 2 para disminución de muestras, 1 para desenfoque simple, 4 para acercamiento.

  1. target (mip 1). . , mip- , 0.02.
  2. mip mip 2, 3, 4 5.
  3. mip 5. , . , .
  4. — . 3 , mip N mip N + 1, , . bloom , .
  5. mip 1 HDR-, bloom.

imagen

Bloom


MIP 1 Bloom


MIP 2 Bloom


MIP 3 Bloom


MIP 4 Bloom

imagen

MIP 5 Bloom


MIP 5 Bloom


MIP 4 Bloom


MIP 3 Bloom

imagen

MIP 2 Bloom


MIP 1 Bloom


Bloom after El

aspecto curioso es que las texturas de escala reducida cambian la relación de aspecto. En aras de la visualización, los corregí, y solo puedo adivinar las razones de esto; quizás esto se hace para que los tamaños de textura sean múltiplos de 16. Otro punto interesante: dado que estos sombreadores generalmente tienen un ancho de banda muy limitado, los valores almacenados en la memoria compartida del grupo se convierten de float32 a float16. Esto permite que el sombreador intercambie operaciones matemáticas para duplicar la memoria libre y el ancho de banda. Para que esto se convierta en un problema, el rango de valores debería ser bastante grande.

Fxaa


ROTR admite una amplia gama de diferentes técnicas de suavizado, como FXAA (Fast Approximate AA) y SSAA (Super Sampling AA). Es de destacar que la opción de habilitar AA temporal está ausente, porque para la mayoría de los juegos AAA modernos se está convirtiendo en estándar. Sea como fuere, FXAA hace frente a su tarea notablemente, SSAA también funciona bien, esta es una opción bastante "pesada" si el juego carece de rendimiento.

Desenfoque de movimiento


Parece que Motion Blur utiliza un enfoque muy similar a la solución en Shadows of Mordor. Después de renderizar la iluminación volumétrica, un pase de representación separado genera los vectores de movimiento desde los objetos animados hasta el buffer de movimiento. Luego, este búfer se combina con el movimiento causado por la cámara, y el búfer de movimiento final se convierte en entrada al paso de desenfoque, que realiza el desenfoque en la dirección indicada por los vectores de movimiento del espacio de la pantalla. Para estimar el radio de desenfoque en unas pocas pasadas, se calcula la textura de los vectores de movimiento en una escala reducida para que cada píxel tenga una idea aproximada de qué tipo de movimiento se encuentra cerca. El desenfoque se realiza en varias pasadas a media resolución y, como vimos, más tarde su escala con la ayuda de la plantilla aumenta en dos pasadas. Se realizan varios pases por dos razones: primero,para reducir la cantidad de lecturas de textura necesarias para crear desenfoque con un radio potencialmente muy grande y, en segundo lugar, porque se realizan diferentes tipos de desenfoque. Depende de si el personaje animado estaba en los píxeles actuales.

imagen

Desenfoque de movimiento para


Velocidad de desenfoque de movimiento


Pase de desenfoque de movimiento 1


Motion Blur Pass 2


Motion Blur Pass 3


Motion Blur Pass 4


Motion Blur Pass 5


Motion Blur Pass 6


Desenfoque de movimiento, acercamiento y alejamiento


Desenfoque de movimiento, bordes de zoom

Características y detalles adicionales


Hay algunas cosas más que vale la pena mencionar sin muchos detalles.

  1. Congelación de la cámara: en climas fríos agrega copos de nieve y escarcha a la cámara
  2. Cámara sucia: agrega suciedad a la cámara.
  3. Corrección de color: al final del cuadro, se realiza una pequeña corrección de color, utilizando un cubo de color bastante estándar para realizar la corrección de color, como se describió anteriormente, y también agrega ruido para hacer que algunas escenas sean más severas

UI


La interfaz de usuario se implementa de forma un poco inusual: representa todos los elementos en el espacio lineal. Por lo general, en el momento de la representación, la interfaz de usuario ya había realizado la corrección de tono y la corrección gamma. Sin embargo, ROTR usa espacio lineal hasta el final del cuadro. Esto tiene sentido, porque el juego utiliza una reminiscencia de la interfaz de usuario 3D; sin embargo, antes de grabar imágenes sRGB en el búfer HDR, deben convertirse al espacio lineal para que la operación más reciente (corrección gamma) no distorsione los colores.

Para resumir


Espero que hayas disfrutado leyendo este análisis de la misma manera que lo hice. Personalmente, definitivamente aprendí mucho de eso. Felicitaciones a los talentosos desarrolladores de Crystal Dynamics por el fantástico trabajo realizado para crear este motor. También quiero agradecer a Baldur Karlsson por su fantástico trabajo en Renderdoc. Su trabajo hizo que la depuración de gráficos en una PC fuera un proceso mucho más conveniente. Creo que lo único que fue un poco complicado en este análisis fue el seguimiento de los lanzamientos de sombreadores, porque al momento de escribir esta función no está disponible para DX12. Espero que con el tiempo aparezca y todos estemos muy contentos.

Source: https://habr.com/ru/post/es436500/


All Articles