Iluminación en Unity

Todos los que trabajamos con motores 3D tenemos claro que un aspecto fundamental para tener simulaciones de calidad es conseguir una buena iluminación de nuestra escena. Este no es un concepto trivial sino que requiere de varias técnicas para conseguir acabados de calidad y suelen estar más relacionadas con el diseño y el arte que con la programación. No hay normas mágicas en la iluminación, sólo conocer algunas técnicas y experimentar hasta conseguir el acabado deseado.

Para poder aprovechar completamente este artículo es necesario contar con unos conocimientos básicos de Unity. Será fundamental saber de qué tipos de luces disponemos o conocer conceptos sobre materiales, texturas, etc.

Finalmente lo que aprenderemos en este artículo serán algunas técnicas para pasar de esto:

a esto otro:

Como norma general nuestro objetivo será conseguir la mejor iluminación posible con el menor gasto de recursos. Para ello vamos a ver como bakear la mayor parte de la iluminación de la escena de forma que la luz no sea calculada en cada fotograma sino renderizada previamente. Esto se consigue automáticamente en Unity marcando la casilla Static del inspector de propiedades de cada objeto. Eso si, esto sólo lo debemos aplicar si el objeto es realmente estático, si vamos a mover el objeto en la escena tendremos que dejarla desmarcada.

Antes de empezar es bueno que tengamos los objetos de nuestra escena en una jerarquía bien organizada. No hay normas generales pero una buena aproximación sería la siguiente:

Esto nos permite por un lado, identificar rápidamente los elementos que forman nuestra escena, y por otro nos facilita marcar todos los objetos como estáticos: simplemente marcamos el nodo padre de la escena (Escena) y automáticamente Unity nos preguntará si queremos hacer lo mismo con sus nodos hijos.

Una vez tengamos estos pasos previos listos podemos comenzar a trabajar la iluminación.

El primer paso será ir a la cámara y cambiar la propiedad Rendering Path a Deferred. Esta propiedad no nos va a dar más calidad directamente pero si va a ampliar algunas opciones que nos permitirán mejorar la iluminación. También marcamos la casilla Allow HDR y desmarcamos Allow MSAA.

Cambiamos, dentro de Edit / Project Settings / Player, en el apartado Other Settings, la propiedad Color Space a Linear.

Lógicamente no debemos de olvidarnos de incluir las sombras a alguna de nuestras luces sino el resultado será bastante plano.

Por defecto, al marcar algún objeto como Static, Unity comienza a generar los mapas de iluminación automáticamente. Este proceso puede llevar bastante tiempo así que lo mejor es desmarcar la casilla Auto Generate de la ventana Lighting para evitar evitar que nos renderice la iluminación cada vez que hagamos un cambio. Seremos nosotros los que decidamos cuándo se genera la iluminación pulsando el botón Generate Lighting.

Importamos nuestro modelo y marcamos el GameObject principal de nuestra escena como Static. Le respondemos que SI a la pregunta de marcar también sus hijos.

Puede que nuestra escena tenga objetos metálicos o muy pequeños y estos no necesitan mapa de iluminación. A estos les desmarcamos el check de Lightmap Static. Con esto conseguimos que no se genere mapa de iluminación para esos objetos.

También podemos desmarcar el check Static de objetos muy grandes y que no es necesario que tengan mapa como por ejemplo el entorno.

El siguiente paso será seleccionar en la ventana Hierarchy todos los MeshRenderer. Para ello en la parte superior escribimos meshrenderer (¡OJO! Hay que escribirlo todo sino no aparece nada) y nos quedan sólo aquellos objetos con MeshRenderer atachado.

Seleccionamos todos en la lista y cambiamos su propiedad Cast Shadows a Two Sided.

En cualquier momento podemos ir viendo cómo va quedando nuestra escena pulsando el botón Generate Lighting de la ventana Lighting.

Este proceso es costoso computacionalmente y es probable que tarde hasta horas. Para reducir los tiempos de espera podemos cambiar las propiedades Lightmap Resolution y Lightmap Size a 2 y 512 respectivamente. Reducimos la calidad pero el tiempo de renderizado es menor. Para obtener el resultado final es mejor volverlos a poner en 20 y 1024.

Una vez que acabe el proceso de bakeado podemos reducir aun más el coste de renderizado de la escena si analizamos los mapas de luz de cada objeto. Para ello en la ventana Scene elegimos el modo de visualización Baked Lightmap.

Ahora estamos viendo los mapas de iluminación de cada objeto. Esos cuadros nos indican la calidad del mapa: cuanto más grandes los cuadros menor es su resolución y por lo tanto su calidad. También podemos comprobar si el objeto tiene el mapa mal colocado (cuadros estirados o deformados) y cuando esto suceda tendremos que crear un mapa de iluminación en el programa de modelado o marcar la casilla Generate Lightmap en las opciones de importación del modelo.

Como decía antes desde aquí podemos controlar la resolución del mapa de iluminación (tamaño de los cuadros). Esto se hace con el valor Scale In Lightmap de las propiedades del MeshRenderer del objeto. Si el objeto es muy pequeño no necesitará un mapa muy detallado (reducimos su escala) y si es más grande (suelos) podemos aumentar la resolución de su mapa para incrementar la calidad.

Ahora mismo la escena cuenta con una sola luz direccional que entra por el ventanal del fondo. Como vemos esa luz es insuficiente para iluminar correctamente todo el local.

Para iluminar habitaciones de forma realista es necesario utilizar luces de área. Colocamos la luz de área como si fuera nuestro techo sobresaliendo un poco por los laterales de nuestra habitación.

Una vez realizado el bakeado con la luz de área nuestra escena ya parece un poquito mejor.

En los objetos como focos, se puede poner su material como Emissive. Esto va a generar una iluminación interior. Se marca el material como Emission, se elige un color y una intensidad. Dentro de ese objeto, en el MeshRenderer se puede marcar la casilla Prioritize Illumination para que Unity incluya ese objeto en los cálculos de luz (se usa en objetos fuertemente emisivos).

Para mejorar los acabados tenemos que incluir reflejos para los materiales que los necesite. Para ello colocamos un Reflection Probe en el centro de la habitación cuyos materiales van a reflejar.

Además de poder colocar el Reflection Probe en la escena, vamos a poder delimitar su alcance con el botón que vemos en la imagen superior. Cuando lo tengamos situado pulsamos el botón Bake. A partir de este momento, aquellos objetos cuyo material esté marcado como Metallic, comenzarán a reflejar el entorno.

ANTES: Albedo a blanco, Metallic y Smoothness a 0.5

DESPUÉS: Albedo a blanco, Metallic a 1 y Smoothnes a 0.8

Un detalle importante de la iluminación de una escena es que en gran parte depende de la calidad de los materiales que utilicemos. Por ejemplo la pared que se ve en el fondo de la imagen utiliza un Normal Map para conseguir el efecto de hundimiento de las juntas de los azulejos.

Vamos a ver cómo conseguir esto con el suelo de nuestra escena. El suelo utiliza esta textura:

Sin el NormalMap aplicado nuestro suelo se ve así:

Para generar el Normal Map de una textura tenemos que utilizar alguna herramienta externa. Hay muchísimas alternativas: el editor de materiales más complejo y también uno de los más potente es Substance Painter. Una alternativa más simple y económica es CrazyBump aunque también
también existen otros como Awesome Bump, Njob, etc. Una última opción es utilizar un generador online como NormalMap-Online que es el que usaremos para este ejemplo.

Hacemos click en el cuadro de la izquierda y subimos nuestra textura de suelo. Como vemos se ha generado un mapa de normales en el cuadro del medio y vemos el resultado final en el de la derecha.

Podemos observar que el resultado es demasiado exagerado, corregimos esto reduciendo el valor Strengh a 1.

Descargamos el fichero que nos genera y lo aplicamos como NormalMap en el material del suelo.

Pulsamos el botón Fix Now y ya tenemos el NormalMap aplicado.

Como vemos en la imagen anterior se ha creado un pequeño efecto de hundimiento en las baldosas del suelo. Esto será más o menos acusado dependiendo del NormalMap y de la incidencia de la luz sobre la textura.

El siguiente paso será aplicar efectos de post-procesado que se aplican en la cámara. Para ello necesitaremos un paquete de Unity llamado Post Processing. Lo primero es comprobar si ya tenemos ese paquete instalado. Vamos a Window / Package Manager.

Al abrirlo vemos que no está instalado. Pulsamos en el botón All, lo buscamos y lo instalamos.

Una vez instalado añadimos a la cámara los siguientes componentes: Post-Process Layer y Post-Process Volume.

En el Post-Process Layer cambiamos la propiedad Layer a Everything y el modo de Anti-aliasing a Fast Aproximate Anti-aliasing FXAA.

En el Post-Process Volume activamos la propiedad Is Global y pulsamos el botón New para crear un nuevo fichero que englobará todos los efectos de post procesado. La creación es de este fichero es útil si tenemos varias escenas ya que podremos aplicar todos los efectos a las mismas sin tener que repetir estos pasos en cada una de ellas.

Ahora ya podemos añadir efectos de post-procesado pulsando el botón que aparece Add Effect.

Añadimos el efecto Ambient Occlusion, que nos va a dar sombras en las esquinas que añaden mucho más realismo a la escena.

Activamos Mode y le damos una intensidad con Intensity.

Escena SIN Ambient Occlusion
Escena CON Ambient Occlusion al 0.5
Escena CON Ambient Occlusion al 1

Ahora, mediante el Post Processing Volume de la cámara añadiremos el efecto Bloom.

El Bloom es para que los objetos cuyo color se acerque al blanco brillen. Marcamos Intensity y establecemos un valor de Threshold. Si Threshold es 1, serán sólo los objetos blancos, si lo reducimos empezarán a brillar todos los objetos.

En la imagen anterior vemos cómo la luz que entra por la ventana se vuelve más luminosa. También algunos objetos cuyo color era blanco ahora se ven más intensos.

Si queremos ganar calidad en los reflejos tenemos que añadir al Post Processing Volume el efecto Screen Space Reflections y colocar su Preset en Overkill.

Y por último si queremos darle a nuestra escena un aspecto mas filmico tendremos que añadir el efecto Color Grading y jugar con los valores que nos ofrece.

Todos los efectos, filtros y técnicas utilizadas en este ejemplo tienen infinidad de valores que se pueden modificar. No hay una regla mágica para saber cuales son los valores correctos ya que esto dependerá mucho del aspecto que le queremos dar nuestra escena: hora del día, penumbra, sol, incluso podemos jugar con las tonalidades de la luz.

Por ejemplo, cambiando el SkyBox, bajando el valor de la luz de área y cambiando el color e intensidad de la luz direccional conseguimos una escena más crepuscular.

O incluso una escena nocturna.