PBR (Physically Based Rendering), ó también llamado PBS (physically based shading) en función del contexto, es una nueva forma de hacer render en los motores de render real-time (por ejemplo en videojuegos) que mejoran muchísimo la calidad final de las escenas renderizadas gracias a que son físicamente correctos.
Hasta ahora crear materiales físicamente correctos era tarea imposible para los motores de render en real-time porque requería cálculos complejos, ésto cambió con un algoritmo de Disney-Pixar para la función BRDF (más abajo se detalla que es BRDF) que dió origen a PBR para render en real-time.
PBR modela de manera muy realista el comportamiento de la luz al incidir en un material (que es, al fin y al cabo, lo que intenta hacer un shader). Las principales ventajas de PBR son:
- Es más fácil crear assets realistas porque elimina todo el trabajo a mano de integrar las luces en la textura, los shaders PBR usan fórmulas 100tifikas para simular el comportamiento complejo de la luz.
- Los assets se ven consistentes en todas las condiciones de luces.
- Proporciona un workflow para crear artwork consistente, incluso entre distintos artistas.
Esto significa que, en última instancia, cualquier material PBR de cualquier sitio (juego, app o lo que fuere) te sirve para tu proyecto aunque tu setup de luces sea diferente.
Para crear un material PBR en UE4 basta con pulsar el botón Add New > Material. Si haces doble-clic en el material reciente creado abres el editor de materiales:
¿Qué es eso que hay que enchufar al material que dice BaseColor, Metallic, Roughness y demás entradas?
Para entender esas entradas, primero hay que entender los conceptos claves detrás de PBR.
- Conservación de la energía.
- La reflexión, su Fresnel y F0
- BRDF y las microrugosidades.
Vamos por el primero:
Conservación de la energía
Cuando la luz incide sobre un material se divide fundamentalmente en dos componentes: una componente difusa y una componente especular (ó reflexiva).
Existen otros fenómenos como la refracción, la luz absorbida en forma de calor y otros. Pero para lo que nos ocupa, la componente difusa y la especular son las realmente importantes.
La componente difusa es aquella que se introduce en la superficie y termina rebotando fuera del material. Esta componente de la luz es la responsable de dar el "color" principal de los materiales. El rojo de las manzanas o el amarillo de los plátanos.
La imagen de más arriba ilustra este efecto: una parte de la luz se introduce un poco en la superficie del material, se "contamina" del color del mismo (rojo para las manzanas, amarillo para plátanos) y termina rebotando en cualquier dirección. En la imagen de ejemplo el color del material es blanco mate como se puede apreciar.
La componente especular (o reflectiva) es aquella que rebota completamente sobre la superficie sin llegar a introducirse en el material. Al no introducirse en la superficie no se "contamina" del color del material. Esta componente especular es la causante de los reflejos.
Estas dos componentes se dan simultaneamente y son las principales responsables de que los materiales se vean como se ven.
¿Qué dice el principio de conservación de la energía? La luz que incide debe ser mayor o igual que la suma de sus componentes difusa y especular.
$$ L_i \ge L_d + L_s $$
Dónde $L_i$ es la luz que incide, $L_d$ la componente difusa y $L_s$ la componente especular.
Nota al margen: Quizás te preguntes ¿por que no es estrictamente igual? Si la luz se divide en sus componentes difusa y especular, ¿acaso la suma no debería ser igual que la luz incidente? En principio no, recuerda que además de las componentes difusa y especular también está la luz que es absorbida y se transforma en calor y otros efectos que no estamos teniendo en cuenta.
El principio de conservación de la energia nos revela un hecho muy importante: si el material tiene mucha componente difusa será a costa de la componente especular y viceversa:
En la imagen cuanta más luz refleja (componente especular) menos se ve el color base del material (componente difusa).
La reflexión, su Fresnel y F0.
Vale, ya sabemos que la luz tiene una componente difusa y una especular y que se dan ambas y que si hay mucho de una es porque hay poco de otra (conversación de la energía).
Sin embargo, la componente especular (o reflexión) no es igual a lo largo de todo el material.
La reflexión de la luz depende del ángulo de visión: En los bordes (el ángulo de visión es de 90º) se refleja el 100% de la luz (por tanto, hay 0% de componente difusa) mientras que de frente (ángulo de visión de 0º) se refleja un porcentaje base llamado $F_0$.
A éste efecto se le llama Fresnel.
En la imagen, puedes ver como dónde el ángulo de visión es 0º (el centro de la esfera) se refleja un 5%. Es decir, $F_0 = 0.05$. A medida que nos vamos acercando a los bordes crece la componente especular, hasta llegar al 100% cuando el ángulo de visión es 90º.
Todos los materiales reflejan el 100% cuando el ángulo de visión es 90º. Lo que sí es diferente de material a material es su $F_0$, esto es, la cantidad que refleja de base.
Metales vs Dieléctricos
Llegados a este punto necesitamos dividir los materiales en dos grupos: los dieléctricos (también llamado no conductores o no metálicos) y los conductores (también llamados metales).
Ejemplos de dieléctricos: madera, plástico, celulosa, cemento, orgánicos (pieles, etc.,), tela, etc.,
Ejemplos de metales: cobre, oro, aluminio, hierro, etc.,
La curva que describe como cambia el porcentaje de reflexión desde $F_0$ hasta los bordes (100%) está en función de:
- Para los dieléctricos: El índice refracción del material (IOR)
- Para los metales: El índice de refracción (IOR) y una constante adicional K, llamada constante de extinción.
Todos los motores de render real-time actuales (incluidos los motores no-realtime) no incluyen la constante K para sus cálculos por lo que no simulan correctamente los materiales metálicos, aunque los aproximan bastante bien. Sí son muy precisos con los dieléctricos (o no-metálicos).
La reflexión base ($F_0$) está en función de la longitud de onda y depende del tipo de material: dielectrico o conductor. Por ejemplo, el oro es un material conductor que refleja un 80% el rojo ($F_0=0.8$ para el rojo) , un 75% el verde ($F_0 = 0.75$ para el verde) y un 10% el azul ($F_0 = 0.1$ para el azul).
¿Y qué color es un 80% rojo, 75% verde y 10% azul? Pues precisamente el color amarillento del oro. Es por ello que cuando incide una luz blanca sobre el oro se ve, bueno, pues de color oro.
A continuación la curva fresnel para el oro para longitudes de onda roja (650nm):
Fíjate como para el ángulo 0º tenemos una reflectividad de aprox. el 80%. Es decir, $F_0 = 0.8$ para longitudes de onda de color rojo.
Debes fijarte en la línea verde que indica la luz sin polarizar.
Por supuesto cuando nos aproximamos a ángulos cercanos a 90% llegamos al 100% de componente especular debido al efecto fresnel. Tal y como ilustra la gráfica.
Veamos ahora para longitudes de onda verde (510nm) y azul (475nm).
Si te fijas en la línea verde de ambas gráficas ves que:
- $F_0 = 0.75$ para longitud verde
- $F_0 = 0.10$ para longitud azul
- Por tanto $F_0$ es distinto en cada gráfica, es decir, $F_0$ depende de la longitude de onda
- También es interesante notar como para 90º siempre refleja el 100% debido al efecto fresnel.
Esto ha sido para el oro, un metal.
Sin embargo, en los materiales dielectricos $F_0$ es prácticamente el mismo para todas las longitudes de onda, por ejemplo para la celulosa:
Dado que es la misma curva para todas las longitudes de onda, se puede decir que para los dieléctricos basta con especificar un único valor de F0. Para los materiales conductores necesitaríamos tres valores, para el rojo, F0 para el verde y F0 para el azul.
Por tanto, en los materiales dieléctricos se refleja la luz tal y como es (si es blanca, se refleja luz blanca, si es amarilla, se refleja luz amarilla).
En otras palabras, para los dieléctricos podremos decir que F0 no está en función de la longitud de onda de la luz incidente, para cualquier longitud de onda se aplica un F0 del 4% (o del que fuere) y por tanto lo único que se hace es "atenuar" el color de la luz incidente.
Los valores F0 de un dieléctrico son del rango 2% al 5% (apenas hay diferencias entre esos valores, por eso la mayoría de motores usan un valor hardcodeado para los dielectricos de F0=4%).
Sin embargo, para un conductor F0 está en función de la longitud de onda por ello la luz que refleja tiene un color distinto al de la luz incidente. En concreto para el oro, si te fijas en los F0 para cada longitud de onda verás que da ese color amarillo característico del oro.
Los valores F0 para según que materiales:
Fíjate como los diélectricos tienen todos practicamente el mismo valor y van de 2% al 5% mientras que los conductores el rango es amplio y va del 70% al 100%.
También en los diélectricos el F0 es igual para cada componente RGB (por ejemplo el agua 43,43,43) mientras que los metales tienen un F0 diferente para cada componente RGB (por ejemplo el copper 250,208,192).
Es importante volver a subrayar el hecho de que para los dieléctricos basta con especificar un único valor de F0. Además su rango es 2%-5% no aporta apenas diferencias, podríamos decir que todos los dieléctricos tienen un F0 = 4%.
Por otra parte los materiales metálicos necesitan F0 para cada componente rojo, verde y azul. Y su rango es demasiado amplio como para aproximarlo a un único valor. Y por tanto para un metal F0 es un color (tres valores).
BRDF y las microrugosidades
Ya sabemos que la componente difusa da el color, que los reflejos son responsabilidad de la componente especular y que dicha componente especular está en función de F0 y el efecto fresnel. Además, para los dieléctricos podemos decir que F0 = 4% y para los metálicos necesitamos que F0 sea un color porque depende de la longitud de onda de la luz incidente.
Veamos ahora el siguiente ejemplo:
Tanto el agua como el barro tienen la misma componente reflectiva, es decir, aunque no lo parezca, ¡reflejan la misma cantidad de luz!. ¿Cómo es posible? La clave es: la microrugosidad ó roughness.
El roughness intuitivamente indica cómo de esparcida es esa luz reflejada.
Un espejo tendrá 0% de roughness (ó 100% de smoothness) porque la luz reflejada es perfectamente nítida mientras que un material matte tendrá 80-90% de roughness porque la luz es reflejada en todas las direcciones (esparcida). La cantidad de luz reflejada es la misma, la diferencia es la nitidez con la que se ve que está en función de su roughness.
Por tanto, volviendo al ejemplo del agua y el barro, la diferencia está en su rugosidad, en el caso del agua es del 0% (no rugosa) y en el caso del barro del 84% de roughness (muy rugosa).
Fíjate como en la imagen no nos indican la propiedad roughness. En vez de eso nos dan el Gloss. Se trata del mismo parámetro en realidad, salvo que gloss indica lo pulido (en vez de lo rugoso). Pero es lo mismo decir 16% pulido (gloss) que 84% rugoso (roughness).
Recuerda que, debido al efecto fresnel, hay un 100% de reflexión en los bordes. Esta reflexión se verá blurry si el material no está pulido (mucho roughness) mientras que si el material está pulido (poco roughness) la reflexión se verá nítida:
Todo esto que hemos visto es calculado por una única función, la función BRDF.
BRDF es una función que dada la luz incidente, la direccion de la cámara (para el cálculo del fresnel) y las características del material (F0 y roughness) calcula la reflexión del material para cada punto del mismo.
BRDF es responsable del blur de los reflejos, de los detalles que se reflejan, del efecto fresnel, etc., como todo ello es provocado mayoritariamente por la microfacet (microrugosidades) del objeto, se puede afirmar que la BRDF es quién, en ultima instancia, calcula la microfacet del material en función de las características del mismo.
Hay varias implementaciones de BRDF como Phong, Blinn, etc., pero la mejor con diferencia (en calidad y rendimiento) es GGX. (GGX está disponible en Unity desde la versión 5.4 y en Unreal Engine 4 desde su primera versión).
BaseColor, Metallic y Roughness
Una vez vistos los conceptos claves, ¿que significa estos mapas de base color, roughness, metallic, etc y para que sirven?
En primer lugar hay que saber que, aunque todos los shaders PBR comparten la misma filosofía, cada implementación concreta puede ser diferente de otra.
Por ejemplo, en Unity no existe el mapa roughness, en lugar de roughness tenemos smoothness (que es igual que el roughness pero invertido).
Empecemos por el mapa metálico.
Metallic
El mapa metálico es una máscara (negro: no metal o blanco: metal) que indica que partes son metálicas o dieléctricas.
Para los dielectricos se usa un F0 (reflexión base) de 4% hardcodeado en el shader y para los metales se usa el valor del mapa Albedo. Recuerda que para los metales necesitamos un color para F0. El fresnel está fijado en 1 en los extremos (90º).
Negro: no metal; Blanco: metal
Los valores intermedios (grises) no están definidos, quizás un motor los interprete como metal (valores distintos de 1), otro como no metal (valores distintos de 0), o según el motor a veces es útil añadir grises en la transición metal-no metal para evitar artefactos pero dependerá de la implementación concreta de cada motor. En UE4 no está definido.
Albedo
También llamado BaseColor.
El mapa albedo contiene el color difuso para los dielectricos y el color reflectivo (F0) para los metales.
La diferencia con el mapa difuso clásico es que el mapa albedo no contiene información de luz salvo, y de manera muy excepcional, micro-oclusiones.
Recuerda que en los dielectricos se refleja la luz tal y como es (si es blanca, se refleja luz blanca, si es amarilla, se refleja luz amarilla). Mientras que en los metales se refleja otro color de luz distinta, por ello F0 está en albedo porque hay un F0 distinto para cada longitud de onda de la luz.
Por ejemplo, para los dielectricos se refleja un 4% el rojo, un 4% el verde y 4% el azul, basta con decir F0=4%, sin embargo en los materiales, por ejemplo el oro, se refleja un 80% el rojo (F0 = 0.8 para el rojo), un 75% el verde (F0 = 0.75 para el verde) y un 10% el azul (F0 = 0.1 para el azul), dando ese color amarillo característico del oro.
Roughness
Es un mapa de grises, donde negro significa que el material está 100% pulido y blanco 100% rugoso.
Resultado final
Combinando todos los mapas el shader puede obtener materiales físicamente correctos: