Galaxy Skin Shader Breakdown – Parte 1
Recientemente me puse un poco Fortnitesco (o algo así) y decidí construir mi propia versión de una Galaxy Skin. En este post dedicado a shaders con Unreal Engine, voy a mostrar como construir y replicar el siguiente efecto:
Construyendo el shader
Comenzaremos por construir nuestro shader. Para esta versión en particular, decidí optar por una combinación de distintos efectos.
La configuración utilizada para nuestro material es:
- Material Domain: Surface
- Blend Mode: Opaque
- Shading Model: Unlit
Depth Texture Reflection
Esta sección de nuestro shader hace referencia a la visualización de la textura y al efecto de “profundidad estática” que genera en nuestro modelo 3D.
Utilicé una textura de nebulosa convincente (puedes utilizar cualquier textura que gustes) y la convertí en una textura seamless con la ayuda de GIMP.
Así es como luce la primera parte de nuestro shader:
El nodo Reflection Vector es un nodo de Unreal Engine que nos devuelve el vector de reflexión de nuestro punto de vista/cámara. Para entenderlo mejor, demos un vistazo al siguiente gráfico ilustrativo:
En esta representación podemos observar como es que trabaja un vector de reflexión, donde d es el vector de incidencia (punto de vista/cámara), n es la normal de la superficie, r es el vector de reflexión y t el vector de transmisión.
Para mapear texturas sobre objetos 3D solo necesitamos dos vectores, así que utilizamos un nodo Component Mask para obtener los vectores RG de nuestro Reflection Vector y los utilizamos como mapeo de UV sobre nuestra textura.
Esto debería ser suficiente para crear nuestro efecto, pero hay un detalle particular en este shader. Este método funciona bien cuando la textura utilizada es de tipo Cube Map y están especialmente diseñadas para Skybox.
Seguro habrás notado que hay un nodo Vector3 con valores 0.5 para cada componente como dato de entrada para nuestro Reflection Vector.
Ya que no estoy haciendo uso de un Cube Map, sino de una textura seamless, este nodo ayuda a personalizar la Custom World Normal. Esto me permite modificar la posición de la textura y evitar que se deforme, estrechandoce o estirandose.
Sí previsualizamos nuestro resultado obtendríamos algo como esto:
Fresnel Function
Este efecto (como muchos otros) se ve mejor con una función de Fresnel aplicada. Esto le da un mejor y más vistoso detalle al resultado final.
Utilizando un nodo Fresnel con un parámetro Fresnel Multiplier que nos permita controlar la influencia exponencial de nuestro efecto y un nodo de función Cheap Contrast conectado a un parámetro que regule el contraste, vamos a lograr un efecto similar a este:
Fresnel Color
En esta sección vamos a agregar color a nuestro efecto Fresnel anterior.
Este apartado es realmente sencillo. Utilizamos un parámetro Vector3 para designar un color (a nuestro gusto) y lo multiplicamos por un parámetro Glow Crank para controlar la intensidad del color.
Base Color Shadow
Vamos a agregar un detalle de sombra a nuestro efecto. Esto le dará un feeling de profundidad sobre el color final a todo el conjunto.
Vamos a aplicar una sombra vertical a nuestro modelo utilizando la componente V (G con Component Mask) de nuestro nodo Tex Coord. La invertiremos para que su dirección sea de abajo hacia arriba.
Con un nodo Power conectado a un parámetro Ambient Shadow vamos a controlar la intensidad exponencial de nuestro efecto de sombra. Al final multiplicaremos el resultado por la textura Depth Texture Reflection.
El resultado debería verse similar a esto:
Preview General 1
Vamos a dar un vistazo general a nuestro avance hasta ahora. Con un nodo Lerp vamos a unificar nuestras secciones construidas.
Conectando la multiplicación de la sección Ambient Shadow con Depth Texture Reflection al pin A, la sección Fresnel Color al pin B y la sección Fresnel Function al pin Alpha obtendremos el siguiente resultado:
Main Stars Layer
En esta sección vamos a agregar los detalles interesantes del shader como lo es la capa de estrellas personalizada.
Aunque el grafo parezca complejo a simple vista, realmente es muy sencillo de entender.
La primera parte se encarga de aplicar un efecto tiling sobre nuestra textura de estrellas, multiplicando las componentes UV de nuestro nodo Tex Coord.
La segunda parte aplica un efecto offset sobre nuestra textura, con un nodo Panner controlado por dos parámetros de movimiento.
Por último, con un nodo Subtract controlado por un parámetro llamado Main Stars Amount Offset controlaremos la “cantidad de estrellas” en el layer. Al final el resultado es conectado a un nodo Saturate para anclar los valores entre 0 y 1.
Para entenderlo mejor, Subtract se encargará de restar el valor de Main Stars Amount Offset al valor de la textura de estrellas. Esto hace que los valores más pequeños disminuyan o desaparezcan (se vuelvan 0) en el layer de estrellas, creando el efecto de reducción.
Volumetric Depth Stars
Ahora vamos a agregar una nueva capa de estrellas, con la diferencia de que esta sección añadirá un efecto de profundidad volumétrica.
Vamos a fragmentar esta sección para poder explicarla mejor.
Custom Reflection Vector
Vamos a utilizar un nodo Camera Vector y transformaremos su espacio de World Space a Tangent Space con Transform Vector. En otras palabras, nos aseguraremos de que este efecto se renderice en espacio de tangente.
Conectaremos un nodo Vector3 con valor 0, 0, 1 al pin Normal del nodo Custom Reflection Vector y nuestro transform anterior al pin Camera Vector.
Esta secuencia de nodos nos permite obtener un vector de reflexión personalizado. El nodo Vector3 indica que el efecto se aplicará de forma perpendicular a la geometría.
En términos simples, el efecto de profundidad ocurrirá a lo largo de la normal de la geometría, sin distorsiones verticales u horizontales.
Depth Offset
Esta sección se encarga de controlar el offset o distancia de profundidad de la capa de estrellas.
Con Component Mask obtenemos la componente B de nuestro Custom Reflection Vector y a continuación obtenemos su valor absoluto Abs para evitar valores negativos. Dividimos el resultado por la distancia de profundidad que deseamos, en mi caso utilicé un valor fijo de 512.
Multiplicamos el resultado de la división anterior por los componentes RG de nuestro Custom Reflection Vector.
Depth Resolution Offset
Este conjunto de nodos se encarga de controlar la cantidad de offset de profundidad que ocurre con base en la resolución de la textura que utilizamos.
En este caso el valor de resolución de la textura es de 1024. Dividimos este valor por 1. El resultado lo multiplicamos por nuestro anterior Depth Offset.
Offset Panner
Finalmente, conectamos nuestra operación anterior a un nodo Add. En el segundo pin de entrada agregamos el nodo Panner desde nuestra sección Main Stars Layer.
Por último, al igual que en nuestra sección Main Stars Layer, hacemos un Subtract controlado por un parámetro Depth Stars Amount Offset para controlar la cantidad de estrellas. Conectamos un nodo Saturate para hacer clamp de los valores y listo.
Stars Color
Para la última sección de nuestro shader vamos a aplicar color a nuestras estrellas.
Vamos a comenzar con un Lerp que nos permita colorear nuestras estrellas de tonos bicolores basados en una textura de noise simple. El color de nuestras estrellas se basará en el valor 0 o 1 de nuestra textura noise.
El nodo Lerp anterior lo conectaremos a un segundo Lerp en el pin B, mientras que el pin A tendrá un valor fijo de 0. Su pin Alpha recibirá el nodo Saturate de nuestra sección Main Stars Layer.
Repetiremos la acción anterior con un tercer Lerp, pero en esta ocasión el pin Alpha recibirá el nodo Saturate de la sección Volumetric Depth Stars.
A continuación, vamos a multiplicar el resultado del segundo y tercer Lerp por un parámetro Stars Glow Multiplier que controle la intensidad del brillo de nuestras estrellas.
Finalmente, con un nodo Add uniremos estas dos capas de estrellas desde sus nodos Multiply.
Ajuste Final
Como último paso y finalizando el shader, vamos a conectar nuestro nodo Add de la sección Stars Color con un nuevo nodo Add.
Este nuevo nodo Add debe ser un nodo intermedio al cual conectaremos nuestra textura de la sección Depth Texture Reflection como entrada. Acto seguido conectaremos la salida del nuevo Add al nodo Multiply de la sección Base Color Shadow.
Preview Finalizado
Este es el resultado de nuestro shader:
Al acercarte a la geometría podrás apreciar la capa de estrellas con efecto de profundidad:
Con algunos ajustes de Post Procesado como el efecto Bloom y agregando detalles extra como partículas con Niagara puedes obtener un resultado así:
Actualización y mejoras
Analizando el shader y realizando algunos experimentos, me he dado cuenta de algunas mejoras que se pueden llevar a cabo.
Ambient Shadow per Object Position
El problema
En la primera sección de nuestro shader aplicamos un efecto de sombra en degradado vertical. Este efecto ayuda a crear más profundidad visual:
El problema con esta solución es que el degradado ocurre en los UV Maps. Si utilizamos un modelo con un mapeado diferente al del Mannequin estándar de Unrel Engine, ocurriría algo así:
Solución
Para resolver esto vamos a aplicar el efecto sombra en Object Space.
El nodo Local Position es una Material Function que se encarga de transformar el Absolute World Space a Local Space. Con esto nos aseguramos de que el efecto comienza en el origen del modelo 3D.
Con una operación Subtract controlada por un parámetro Gradient Mask Offset vamos a poder controlar el offset u “origen” de nuestro efecto. Para aplicar el efecto verticalmente a nuestro objeto, necesitamos un Component Mask aislando la componente B.
La siguiente imagen muestra como se vería nuestro modelo con un offset de 100 unidades (cm en Unreal Engine) aplicado. Recordemos que el origen de este modelo se encuentra en la base del mismo, a la altura de los pies.
Vamos a conectar la operación anterior a un nodo Divide controlado por un parámetro Gradient Mask Falloff. Este parámetro nos permite controlar la atenuación de nuestro gradiente, de esta forma podemos configurar la “longitud de su caída” a nuestro gusto:
Con el error corregido lo único que nos queda por hacer es habilitar ambas opciones para los usuarios: sombra por UV Maps o por Object Space. Para ello utilizamos un nodo Static Bool Param conectado a un nodo Static Switch.
Como valor true conectamos nuestro nodo Divide anterior. Como valor false conectamos nuestro nodo One Minus de la versión UV Maps. El resultado lo conectamos al nodo Power que persiste desde la versión original.
Ahora nuestro efecto está corregido y listo para utilizarse en modelos externos.
Reorganización del Stars Layer
El problema
Ya sea con UV Maps o con Local Space, el efecto sombra del shader nos deja con otro problema a resolver. Entre mas amplio sea el valor de Ambient Shadow mas intenso se vuelve el efecto.
Si subimos los valores tanto de Ambient Shadow como de Offset o Falloff, nuestra sombra comienza cubrir las estrellas hasta desaparecerlas:
Solución
Para corregir este detalle basta con reorganizar un nodo y sus conexiones. Concretamente, se trata del nodo Add que se encarga de combinar las capas de estrellas con la base del shader.
Comenzamos reconectando nuestra textura de nebulosa al nodo Multiply encargado del efecto sombra. Este último nodo lo pasamos como entrada al nodo Add y a su vez, la salida al nodo Lerp final.
Esta configuración permite que la textura de profundidad y la sombra se apliquen en conjunto, previo a aplicar el layer de estrellas. Una vez esto ocurre, aplicamos el layer de estrellas sobre esta capa de información. De esta forma, nuestro layer queda por encima del efecto sombra, viéndose así:
Implementar o no la solución queda a cargo de la visión artística del usuario, no es obligatoria.
Siguientes Pasos
Quiero ofrecer agradecimientos especiales a:
Ya que gracias a sus tutoriales, recursos y conocimientos he podido recrear este shader inspirado en Fortnite.
Ahora es tu turno para recrear este shader y hacer improvements sobre él. ¿Sabías que puedes cambiar el modelo de shading para que reaccione a la luz? O quizá prefieras intentar utilizar una textura de Cube Map para la nebulosa.
En el siguiente post: ¿cómo crear el efecto de partículas de este shader con Niagara y Unreal Engine 5?
Si tienes alguna duda o pregunta no olvides dejar tu comentario para que todos podamos aportar y aprender.
Sigue aprendiendo sobre VFX para videojuegos en este post y continúa practicando una y otra vez hasta ser un experto.