Middle Earth: Shadow of Mordor se lanzó en 2014. El juego en sí fue una gran sorpresa, y el hecho de que fuera un spin-off de la historia del universo del Señor del Anillo fue bastante inesperado. El juego obtuvo un gran éxito y, al momento de escribir,
Monolith ya lanzó una secuela: Shadow of War. Los gráficos del juego son muy hermosos, especialmente teniendo en cuenta que fue lanzado para diferentes generaciones de consolas, incluidas la Xbox 360 y la PS3. La versión para PC está bastante bien pulida, contiene opciones de gráficos adicionales y paquetes de texturas de alta resolución que revelan completamente el potencial del juego.
El juego utiliza el renderizador diferido DX11 relativamente nuevo. Utilicé
Renderdoc para aprender profundamente las técnicas de renderizado del juego. Al trabajar, se utilizaron los parámetros gráficos máximos posibles (ultra) y se incluyeron todas las "lociones" posibles, como la transparencia independiente del orden, la teselación, la oclusión en el espacio de la pantalla y varios desenfoques de movimiento.
Marco
Aquí hay un marco que analizaremos. El jugador está ubicado en una plataforma de madera en la región de Udun. Shadow of Mordor usa una mecánica similar a la mecánica de juegos como Assassin's Creed, donde puedes escalar edificios y torres, y luego disfrutar del paisaje digital circundante desde los tejados.
Pasaje de profundidad
Las aproximadamente 140 primeras llamadas de renderizado hacen un pase preliminar rápido para renderizar los elementos y edificios de elevación más grandes al búfer de profundidad. La mayoría de los objetos no entran en este pase preliminar, pero ayuda cuando el juego tiene una gran cantidad de llamadas de empate y puedes mirar a lo lejos. Curiosamente, un personaje que siempre está en el centro de la pantalla y ocupa una fracción decente del espacio de la pantalla no entra en este pasaje. Como en muchos otros juegos de mundo abierto, el motor utiliza los valores z inversos. Esta técnica une el plano cercano a 1.0 y el plano lejano a 0.0 para aumentar la precisión a grandes distancias y evitar conflictos z. Lea más sobre la precisión del z-buffer
aquí .
G-buffer
Inmediatamente después de esto, comienza el pase G-buffer, que se realiza en aproximadamente 2700 llamadas de extracción. Si leyó mi
análisis anterior de Castlevania: Lords of Shadow 2 o estudió otros artículos similares, entonces este pasaje debería serle familiar. La propiedad de las superficies se registra en un conjunto de memorias intermedias, que luego se leen en los pasos del cálculo de la iluminación para calcular las reacciones de las superficies a la luz. Shadow of Mordor utiliza un clásico renderizador diferido, pero se utiliza un número relativamente pequeño de objetivos de renderizado del G-buffer para lograr este objetivo (3). A modo de comparación: Unreal Engine en este pasaje usa 5-6 buffers. G-buffer tiene el siguiente esquema:
Tampón normal
R | G | B | Un |
Normal.x | Normal.y | Normal.z | ID |
El buffer de normales almacena las normales en el espacio mundial en el formato de "8 bits por canal". Esto es apenas suficiente, y a veces nada, para describir superficies planas que varían suavemente. Si te fijas bien, esto se puede ver en algunos charcos en el juego. El canal alfa se usa como una ID que marca varios tipos de objetos. Por ejemplo, descubrí que 255 se refiere al personaje, 128 a la parte animada de la bandera, y el cielo está marcado con ID 1, porque más tarde estos identificadores se usan para filtrarlo en la etapa de adición (el cielo obtiene su propia floración radial).
En el artículo original, estas y muchas otras imágenes están animadas para mayor claridad, por lo que recomiendo mirar allí.Buffer Albedo
R | G | B | Un |
Albedo.r | Albedo.g | Albedo.b | Oclusión de la cavidad |
El búfer de albedo almacena los tres componentes de albedo y una oclusión a pequeña escala (a veces llamada oclusión de cavidad), que se utiliza para sombrear pequeños detalles que no pueden ser alcanzados por un mapa de sombras o un procesamiento posterior en el espacio de la pantalla. Se utiliza principalmente para fines decorativos, por ejemplo, agujeros y arrugas en la ropa, pequeñas grietas en un árbol, pequeños patrones en la ropa de Talion, etc.


Al procesar enemigos en el sombreador, albedo tiene en cuenta la textura de la sangre (curiosamente, Talion nunca recibe heridas visibles). La textura de la sangre es la entrada para la fase de renderizado de la ropa y el cuerpo de los enemigos, pero no especifica el color de la sangre, que es la entrada en el búfer constante, pero determina los factores / niveles de sangre para controlar la cantidad de sangre mostrada. Además, la orientación normal se utiliza para escalar el efecto, lo que le permite controlar la dirección de las salpicaduras de sangre. Luego, el albedo esencialmente se sombrea con el brillo de las heridas recibidas por el enemigo, tomadas del lugar apropiado en el mapa de sangre, y también modifica otras propiedades, como la especular, para obtener un efecto sanguíneo convincente. No pude encontrar la parte del marco en la que se representa el mapa, pero supongo que se registran justo al comienzo del marco cuando se expone la espada, y luego se usan aquí.




Tampón especular
R | G | B | Un |
Aspereza | Intensidad especular | Fresnel | Factor de dispersión subsuperficial |
El búfer especular contiene otras propiedades de la superficie que se pueden ver en los juegos, como la rugosidad (esto no es del todo rugosidad, sino un grado especular escalado, pero se puede interpretar de esta manera), la intensidad especular (brillo especular) que escala albedo para obtener el color correcto especular, factor de reflectividad (comúnmente conocido en la literatura como F0, porque es la entrada a la respuesta del espejo de Fresnel) y el componente de dispersión subsuperficial (dispersión subsuperficial). El último componente se utiliza para iluminar materiales translúcidos como tejidos finos, plantas y piel. Si luego nos sumergimos en el estudio del sombreador de iluminación, encontramos que aquí usamos la variación del
modelo especular normalizado según Blinn-Fong .
Calcomanías diferidas
Como vimos anteriormente, Shadow of Mordor muestra rastros de sangre bastante detallados en los personajes heridos. Cuando Talion agita su espada, el medio ambiente también recibe su parte de sangre oscura de orco. Sin embargo, otra técnica se utiliza para entornos:
calcomanías diferidas . Esta técnica consiste en proyectar un conjunto de texturas planas sobre la superficie de lo que se renderizó anteriormente. De esta forma, el contenido del G-buffer se reemplaza con este nuevo contenido antes de realizar el pase de iluminación. En el caso de la sangre, solo es suficiente un rocío sangriento, y cuando se generan muchas calcomanías, se crea rápidamente un paisaje bastante sombrío.
Lo último que se representa en el pasaje G-buffer es el cielo, la textura del cielo es de muy alta resolución (8192 × 2048) en formato HDR BC6H. Tuve que hacer una pequeña corrección de tono, porque en HDR todos los colores son demasiado oscuros.
Teselación
Una "característica" muy interesante del juego (si está activada) es la teselación. Se utiliza para muchas cosas diferentes, desde el terreno hasta la representación de personajes (así como accesorios y objetos de personajes). Aquí, la teselación no subdivide la malla de polietileno baja, sino que crea polígonos a partir de una nube de puntos, utilizando el grado de subdivisión necesario, según los criterios para el nivel de detalle, por ejemplo, la distancia a la cámara. Un ejemplo interesante es la capa de Talion, que se transmite a la GPU como una nube de puntos (después de la simulación física), y el sombreador de teselación recrea los polígonos.
Transparencia independiente del orden

Una de las primeras cosas que me sorprendió con su extrañeza es el pase de tratamiento capilar, ya que realiza un sombreador especial muy complejo. En las opciones de gráficos, se menciona la opción OIT (Transparencia independiente del orden) para el cabello, es decir, debería serlo. Primero, realiza la salida a un búfer separado y calcula el número total de píxeles transparentes superpuestos entre sí, al tiempo que guarda las propiedades en una estructura "profunda" similar a un búfer G. Más tarde, otro sombreador clasifica fragmentos individuales individuales de acuerdo con su profundidad. Parece que las flechas también se muestran de esta manera (probablemente, su plumaje requiere una clasificación adecuada). Este es un efecto muy sutil que no agrega ninguna diferencia gráfica especial, pero sigue siendo un detalle interesante. Aquí hay un ejemplo simple: la imagen de arriba muestra el número de fragmentos superpuestos (cuanto más rojos, más fragmentos). La transparencia regular sigue ordenada por la CPU y se representa como alfa tradicional. Solo las entidades individuales caen en el pase OIT.
Sombras de Mordor
SoM tiene muchas fuentes de sombra. Además de los mapas de sombras tradicionales de fuentes de luz dinámicas, SoM utiliza una oclusión ambiental de dos canales en el espacio de la pantalla, una oclusión a microescala creada para casi todos los objetos en el juego y una textura de oclusión similar a un mapa de altura con una vista superior.
Oclusión en el espacio de la pantalla.
El primer paso se procesa utilizando el buffer G ambiental y la oclusión especular en el espacio de la pantalla. El sombreador en sí mismo es un gran ciclo desplegado que muestrea tanto el mapa de profundidad de tamaño completo como el mapa de profundidad promedio previamente reducido, buscando muestras vecinas en un patrón dado. Utiliza una textura cuadrada de 4x4 para seleccionar vectores pseudoaleatorios en busca de fuentes de oclusión. Representa un búfer de oclusión ruidoso, que luego se suaviza con un simple desenfoque en dos pasadas. La característica más interesante aquí es que hay dos canales de oclusión diferentes: uno de ellos se utiliza como oclusión especular y el otro como oclusión difusa. En implementaciones SSAO estándar, se calcula un canal que se aplica a toda la iluminación horneada. Aquí, la tarjeta SSAO también se lee para su transmisión al paso de iluminación direccional, donde se utiliza.
Cartas de sombra
El siguiente evento es la representación del mapa de sombras. Dado que la acción del juego tiene lugar principalmente en espacios abiertos, la mayor parte de la luz y las sombras se toman de la luz direccional principal. Aquí utilizamos la técnica de
mapas de sombras en cascada (una variación de los cuales es
mapas de sombras divididos en paralelo ), una técnica bastante estándar para aplicar sombras a largas distancias, que consiste en representar la misma escena desde un punto de vista de la fuente de luz para diferentes áreas del espacio. Por lo general, los mapas de sombras alejados del área de cobertura de la cámara están a grandes distancias o tienen una resolución más baja que las anteriores, lo que esencialmente reduce la resolución en áreas en las que aún no se requieren detalles debido al hecho de que la geometría está demasiado lejos. En esta escena, el juego presenta tres cascadas de sombras 4096 × 4096 (de hecho, el juego tiene un lugar para cuatro). La cascada superior está muy cerca de Talion, y la inferior incluye montañas y objetos muy lejos de la cámara. Cuando se trabaja con sombras, el juego usa el mismo truco con la coordenada z inversa que en el mapa de profundidad.
Buffer de sombra
El siguiente paso es crear un búfer de sombra. Esta es una textura de un solo canal, basada en información de oclusión de mapas de sombras anteriores, codifica un factor de sombra en el intervalo [0, 1]. Para crear suavidad alrededor de los bordes, el mapa de sombras se muestrea 4 veces usando el estado de un muestreador bilineal especial, que recibe 4 muestras y las compara con un valor dado (esto se llama
filtrado porcentual de cierre ). Obtener múltiples muestras y promediar sus resultados a menudo se llama
Porcentaje de sombras suaves más cercanas . Además de leer el mapa de sombras, también se muestrea el último componente del búfer especular (es decir, el coeficiente de dispersión subsuperficial), que se multiplica por el "factor de purga de luz". Parece que esto es necesario para eliminar el sombreado de estos objetos, para que pase un poco más de luz a través de ellos.
Textura de proyección direccional
Otra fuente de luz y sombra es una textura de vista superior muestreada por una fuente de luz direccional. Este es el color agregado al color de la fuente de luz direccional, más el efecto de sombreado global que se aplica a la iluminación direccional. Parece que algunos de ellos fueron creados manualmente sobre un mapa de iluminación de nivel generado automáticamente con una vista superior. Parece que los bordes de las sombras para la geometría estática se ajustan manualmente (quizás para evitar conflictos con el mapa de sombras real), y algunas partes también parecen estar ligeramente teñidas a mano. Probablemente la tarea de esta textura es la adición de bajo costo de la oclusión ambiental a gran escala y las simulaciones de iluminación global liviana, además de la iluminación direccional. Las imágenes a continuación muestran el tono, la oclusión y el producto de ambos factores, lo que nos da una idea de cómo se ve la máscara de color final.



El resultado de todos los pases de iluminación se guarda en el objetivo de representación del formato R11G11B10F. Así es como se ve el resultado. Realicé una corrección tonal de los resultados para hacer más visible el efecto de la iluminación direccional en el nivel.
Todas las montañas distantes (que no se muestran en la imagen de arriba) también se iluminan con luz direccional, pero se resaltan como un caso separado para que la iluminación pueda controlarse mejor. Algunos están escalados, pero los que están más lejos en realidad son impostores con mapas normales y albedo inteligentemente creados. Tienen fuentes especiales de iluminación direccional que afectan solo a las montañas.
Iluminación estática
Shadow of Mordor utiliza una implementación de iluminación estática muy intensiva en memoria, que utiliza texturas de gran volumen. La imagen a continuación muestra tres texturas estáticas para la cantidad de iluminación utilizada para iluminar difusamente parte de esta área. Cada uno de ellos es una gran textura comprimida 512x512x128 BC6H, es decir, ocupan 32 MB por textura o 96 MB en general (jugamos con configuraciones de máxima calidad). La textura del color indica la intensidad que ingresa al vóxel. Los otros dos indican la fuerza o magnitud de esta intensidad a lo largo de las seis direcciones xyz y -xyz, y la normal se usa para seleccionar tres componentes (xyz positivo o negativo, aquellos que son más consistentes con lo normal). Una vez construido este vector, tomamos su producto vectorial por el cuadrado de lo normal, y esto se convierte en el factor de escala para la intensidad. La fórmula es la siguiente:


Los volúmenes de luz estática también representan un mapa cúbico para la iluminación especular, que probablemente se captura en el centro del SLV. Curiosamente, las texturas de volumen almacenan valores HDR comprimidos en BC6H, y los mapas cúbicos se almacenan en formato BC3 (DXT5), que no puede almacenar valores de punto flotante. Para compensar esta limitación, el canal alfa retiene el brillo, que luego escala de 1 a 10. Esta es una decisión un poco extraña, y para mí, se parece más a una implementación de Legacy. No olvides que el juego se lanzó para las consolas de la generación anterior, que no admiten los nuevos formatos de textura HDR.
Los cuadros a continuación muestran los resultados "antes y después" teniendo en cuenta el impacto de la imagen promedio. Para la visualización, realicé la corrección tonal.
Niebla atmosférica
Shadow of Mordor tiene un sistema de tiempo y tiempo, gracias al cual el sol brilla o la lluvia cae a través del juego en Mordor. Este sistema está controlado por la suma de los componentes, y uno de los más importantes es la niebla. Shadow of Mordor utiliza un modelo de niebla atmosférica bastante simple pero físicamente sólido, que incluye
la dispersión única de
Rayleigh , así como la dispersión por una partícula esférica (dispersión de Mie).
Comienza calculando la posición de la cámara con respecto al
centro de la tierra . Varias fórmulas trigonométricas permiten determinar dónde se encuentra la cámara en la atmósfera, dónde está el píxel y qué distancia recorre el haz en la atmósfera a una altura máxima dada de la atmósfera. En nuestro caso, la atmósfera se establece a una altura de 65,000 metros sobre la superficie del planeta. Con esta información en mente, los coeficientes de partículas esféricas y de Rayleigh se utilizan para calcular los tipos de densidades de las partículas de niebla y sus colores. Estas densidades oscurecen los píxeles ya sombreados, dispersan la luz que ingresa a la cámara desde la superficie sombreada y contribuyen a la niebla. Al simular dicha dispersión, se tiene en cuenta el brillo y la dirección del sol.
Velocidad de obturación y corrección de tono
Al calcular la velocidad de obturación, se utiliza un enfoque bastante estándar: reducir secuencialmente la resolución del búfer de brillo, calculado a partir del búfer de color HDR principal, en una cadena de texturas, cada una de las cuales es la mitad del tamaño de la textura anterior, comenzando con una textura 1/3 del búfer de cuadro principal. Con esta disminución en la resolución, se toman 4 muestras, promediando los valores de píxeles vecinos, es decir, después de convertir todos los valores promedio en un solo texel, el resultado final se convierte en brillo promedio. Después de que la textura alcanza el tamaño de 16 × 9 texels, se inicia un sombreador de cálculo, que resume todos los texels restantes. Este valor se lee inmediatamente en el pase de corrección de tono para cambiar los valores de brillo.
Para la corrección del tono, se utiliza el operador Reinhardt, cuya fórmula optimizada se puede encontrar
aquí y
aquí . En el código hlsl, esto se verá así:
float3 hdrColor = tex2D(HDRTexture, uv.xy); hdrColor *= exposureValue;
Si trazamos esta curva, veremos que este operador descarta el 10% de los valores de blanco incluso con un valor de entrada de 2.0, al mismo tiempo que deja a la fuerza una pequeña parte del intervalo inferior completamente en negro. Esto crea una imagen oscura desaturada.
Etapa alfa
El paso alfa es un poco inusual porque representa los objetos directamente en el búfer LDR. Otros juegos también los muestran en el búfer HDR para que puedan participar en el pase del obturador. Sea como fuere, la textura de brillo previamente calculada se limita a todos los objetos con iluminación alfa (en algunos casos, por ejemplo, para objetos emisores de luz, la velocidad de obturación se calcula utilizando las constantes de sombreado, no la búsqueda de textura), y por lo tanto, la velocidad de obturación se aplica automáticamente al renderizar, y no realizado en postprocesamiento. Un caso muy específico de usar alfa en un juego es una transición al modo fantasma (en él, el fantasma de Celebrimbor se representa sobre el personaje del jugador, forjado en los anillos soberanos del universo LOTR; por lo tanto, el juego muestra que siempre está cerca, aunque invisible). El juego pasa varios parámetros a las mallas de ambos personajes, que controlan la opacidad y permiten que el juego oscurezca parcialmente a Talion y muestre gradualmente a Celebrimore. Otros objetos en el juego en modo fantasma también muestran versiones fantasmas sobre objetos opacos, como enemigos y torres. Aquí hay otra escena con la transición al mundo fantasmal.




Lluvia
En el marco principal que examinamos, no llueve, pero el clima es una parte tan importante del juego que me gustaría mencionarlo aquí. Se genera y simula en la GPU, y se procesa justo al final de la fase alfa. Se ejecuta un sombreador de cómputo que realiza la simulación y escribe las posiciones en el búfer. Estas posiciones son tomadas por otro sombreador, que con la ayuda de una llamada indirecta instanciada genera tantas instancias de quads como las posiciones se calcularon en el pase anterior. El sombreador de vértices tiene un quad simple que, si es necesario, se deforma y gira hacia la cámara. Para evitar que la lluvia penetre a través de las superficies, el sombreador de vértices también lee mapas de altura desde una vista superior, lo que le permite rechazar todas las gotas debajo de la superficie superpuesta. Este mapa de altura se representa justo al comienzo del marco. El mismo sombreador de vértices le dice al sombreador de píxeles dónde obtener la muestra de la textura de la gota; Si la caída está cerca de la superficie, selecciona un área de textura que contiene una animación de bienvenida. Además, las gotas de lluvia realizan cálculos de niebla en el sombreador de píxeles para integrarse perfectamente con el resto de la escena. Aquí hay una captura de pantalla desde la misma perspectiva en un día lluvioso.


Cuando se activa el efecto de lluvia, el buffer especular se modifica globalmente para crear superficies húmedas, y las ondas de lluvia se convierten en el buffer normal. La animación está cronometrada, por lo que solo se utiliza un cuadro de la animación en bucle. El búfer normal que se muestra a continuación se modifica para mostrar las ondas representadas en el búfer.
Destellos de lente y floración
Una vez que se completa la representación alfa, los destellos de la lente se representan encima. Se representa una serie de quads desplazados a partir del punto de donde proviene la luz direccional (en nuestro caso, el sol). Inmediatamente después de esto, se realiza un pase de floración. Esta es una técnica bastante estándar, que consiste en una serie de texturas borrosas y de tamaño reducido que contienen píxeles cuyo brillo supera un cierto umbral. Se utilizan dos pases de floración, comunes con el desenfoque gaussiano para toda la escena y un desenfoque radial especial que se aplica solo al cielo. El desenfoque radial es una de las operaciones que utiliza una identificación especial del búfer G de los mapas normales, porque solo se tienen en cuenta los píxeles en el cielo. Como beneficio adicional, este desenfoque muestreará el mapa de profundidad y puede crear
rayos crepusculares de bajo costo. Como estamos trabajando con un búfer LDR en esta etapa, el valor del umbral de floración es diferente del valor de la alfombra HDR (los valores por encima del umbral, generalmente 1.0, conducen al cálculo), y esto significa que el valor de la floración obtenida de él es ligeramente limitado. En cualquier caso, esto es bueno para el juego y aquí están los resultados. En las imágenes a continuación, los colores de la textura de mip de floración se ven un poco extraños porque cada píxel está escalado por el brillo contenido en el canal alfa. Este brillo se calculó anteriormente, en la etapa de corrección tonal. En la composición final, bloom se calcula como
bloom.rgb · bloom.a · bloomScale .
Antialiasing + Profundidad de campo
No hay mucho que decir sobre estas dos operaciones; se utilizan enfoques estándar de la industria. Se realiza una pasada simple a través del antialiasing FXAA inmediatamente después de componer la floración con la imagen LDR, y la profundidad de campo se realiza inmediatamente después. Por profundidad de campo, el juego presenta dos versiones borrosas disminuidas del búfer final. Luego, la profundidad de píxel se usa para mezclar imágenes borrosas y normales, lo que da el efecto de desenfoque. En aras de la claridad, en esta captura, exageré el efecto de la profundidad de campo. El juego tiene un modo de captura de pantalla incorporado, que le permite configurar fácilmente estas condiciones.
Desenfoque de movimiento
El desenfoque de movimiento consta de dos pases. Primero, los datos de las orientaciones anteriores y actuales de la cámara se transfieren al búfer de velocidad de pantalla completa. En este caso, dos canales de la textura se llenan de velocidad en el espacio de la pantalla. Ahora, el canal r contiene la magnitud del cambio de píxel en la dirección horizontal de la pantalla, y el canal g contiene la magnitud en la dirección vertical. Así es como se obtienen rayas radiales al mover la cámara. El personaje se representa nuevamente, esta vez llenando el canal azul en función de sus poses actuales y anteriores, como es el caso de la cámara. El canal azul se usa para marcar si se debe o no renderizar. El canal alfa también está lleno de un valor constante (0.0598), pero no he investigado su valor o sus objetivos. La resolución del búfer de velocidad se reduce a una textura muy pequeña promediando un rango relativamente amplio de velocidades en la textura original. En la última pasada, esto le da a cada píxel una idea aproximada de cuál será el radio de desenfoque en una pasada de desenfoque real.
Luego, el pase de desenfoque lee las texturas de velocidad, un mapa de profundidad, el búfer de color original y la textura de ruido. Este último se utiliza para ocultar el efecto de una imagen espejo, que puede ocurrir con este tipo de desenfoque con un radio grande. Luego, el búfer de imagen se muestrea varias veces en la dirección indicada por el búfer de velocidad, se promedian los colores, lo que conduce a que la imagen se vea borrosa en la dirección de los vectores de movimiento. Además, este efecto se escala de acuerdo con la velocidad de fotogramas con la que funciona el juego. Para esta captura, tuve que limitar el juego a 30 fps, porque a 60 fps o más, esto apenas se nota.
Corrección de color
El paso final de la corrección de color se realiza utilizando los "cubos de color". Un cubo de color es una textura 3D cuyos componentes rgb se ajustan a las coordenadas xyz de la textura. Estas coordenadas xyz contienen el color con el que necesitamos reemplazar el color original. En nuestro caso, la tabla de búsqueda (LUT) es neutral (es decir, las coordenadas y el color contienen el mismo valor), por lo que modifiqué la misma escena usando los ajustes preestablecidos que el juego proporciona en el editor de la cámara.
Marco final
Después de la creación del marco principal en un búfer separado, se representa la IU. Esto garantiza que, independientemente del tamaño seleccionado para el búfer trasero, la interfaz de usuario siempre se verá clara y hermosa en el tamaño de la ventana nativa, mientras que el juego puede cambiar la resolución si es necesario para garantizar la velocidad. Al final, ambas texturas se mezclan en función de los datos del canal alfa de la interfaz de usuario, y luego se procesan en el búfer de cuadro final, que está listo para visualizarse en la pantalla.
Espero que hayan disfrutado mi análisis. Quiero agradecer a
Adrian Correge por el increíble trabajo que me inspiró a estudiar los gráficos, así como al personal del estudio
Monolith para este juego realmente inolvidable.