Unity VFX Graph: creando una galaxia

En este post quiero enseñarte como crear una galaxia basada en un sistema de partículas utilizando el Unity VFX Graph y el HDRP template.

Personalmente, me gusta demostrar que Unreal y Unity (y otros motores) son en esencia idénticos. Lo que se puede hacer en un motor, se puede replicar sin pegas en otro. Me he basado en este tutorial del canal CGHOW (Unreal Engine) para recrear el siguiente sistema de partículas con Unity:

Primero, vamos a crear un nuevo proyecto con Unity HDRP desde Unity Hub. Para este tutorial estoy utilizando la versión 2021.3.0f1.

Unity Hub create project

Configurando el espacio de trabajo en HDRP

Cuando estemos dentro de nuestro proyecto, vamos a crear una carpeta para almacenar nuestros assets. Esto es una buena práctica para organizar nuestro espacio de trabajo.

A continuación vamos a realizar algunas configuraciones para nuestro entorno visual en HDRP. No será necesario modificar en gran escala el pipeline, así que vamos a utilizar los settings por defecto.

Para este proyecto vamos a realizar ajustes menores. Comenzaremos por crear una nueva escena dentro de nuestra carpeta _GalaxyPS con el mismo nombre. A continuación vamos a crear nuestro Volume Profile (right click > create > Volume Profile).

Create Volume Profile

El volume profile es un asset que nos permite modificar y sobreescribir elementos de post procesado. En este caso vamos a utilizarlo para modificar el color de nuestra escena.

Con el VP_GalaxyPS seleccionado, agregaremos desde el inspector el override “Visual Environment”. En sus opciones marcaremos el checkbox “Sky Type” y seleccionaremos “Gradient Sky”.

Ahora vamos a agregar otro override. En esta ocasión seleccionaremos “Sky > Gradient Sky”. Marcaremos las opciones “Top, Middle y Bottom” y cambiaremos el color de cada uno a R = 8, G = 8, B = 8.

Para que estos cambios hagan efecto sobre nuestra escena, debemos asignar nuestro VP a nuestro HDRP. Abrimos Edit > Project Settings > Graphics > HDRP Global Settings. Asignaremos nuestro VP en el apartado “Default Volume Profile Asset”.

Los cambios deberían verse reflejados en nuestra escena. Por ahora esta configuración debería ser suficiente, pero volveremos un poco más adelante.

VFX Graph: creando el sistema de partículas.

Para comenzar con nuestro sistema de partículas con VFX Graph, vamos a crear un nuevo Visual Effect llendo a Right Click > Create > Visual Effects > Visual Effect Graph. Nombraremos a nuestro sistema como P_GalaxyPS (opcional) y lo agregaremos a nuestra escena.

Ahora vamos a editar nuestras partículas. Al hacer doble clic sobre nuestro asset se abrirá nuestro VFX Graph. Debería lucir algo así:

Unity VFX Graph
Unity VFX Graph

Inicializando el sistema de partículas

Para comenzar con nuestras partículas vamos a modificar nuestro Spawn Rate. El Spawn Rate es la cantidad de partículas que el sistema puede instanciar a la vez. Modificaremos su valor a 2500. También modificaremos los Bounds y la capacidad de nuestro sistema a 50000 partículas. Utilizaremos los siguientes valores:

Eliminamos el bloque Set Velocity Random de nuestro bloque padre Initialize Particle. Cambiamos nuestro Set Lifetime Random a un valor entre 1 y 20. Esto permite que nuestras partículas vivan durante un tiempo aleatorio entre 1 a 20 segundos.

Dentro del mismo bloque padre, vamos a agregar un nuevo bloque Position (Mesh). Este bloque será nuestro emisor de partículas, a partir de un objeto mesh Capsule. Cambiaremos su propiedad “Placement Mode” a Surface. Esto servirá para emitir las partículas a partir de la superficie de la mesh.

Los ajustes deberían verse así:

Initialize Particle Block

Agregando movimiento a las partículas

A continuación vamos a generar el movimiento de nuestras partículas. En el bloque padre Update Particle, vamos a agregar un bloque Turbulence. Este bloque permite agregar movimiento “errático” con fuerzas y velocidades vectoriales basadas en capas de noise.

Con turbulence agregado, cambiaremos sus valores de la siguiente forma:

  • Noise Type: Perlin
  • Field Transform > Scale: X=5, Y=0.3, Z=5
  • Intensity: 50
  • Drag: 1
  • Frequency: 0.1
  • Octaves: 3
  • Roughness: 0.5
  • Lacunarity: 2

Ahora vamos a hacer que nuestras partículas roten sobre su posición de origen. Esto nos permitirá crear el efecto espiral que caracteriza a muchas de las galaxias en el universo. Crearemos un nuevo bloque Set Velocity. Los ajustes deben lucir así:

Update Particle Block

El bloque Set Velocity no tendrá efecto por sí mismo, así que vamos a implementar un poco de matemática para recrear ese movimiento orbital.

Usando un nodo Get Attribute: position conectado a un nodo Cross Product con valor -0.5 en la componente Y lograremos este movimiento. Nuestro cross product genera un vector perpendicular entre la posición de nuestra partícula y nuestro vector UP (Y). El vector resultante será la nueva velocidad para nuestras partículas.

Fix: el bloque Set Velocity debe ejecutarse antes del bloque Turbulence, de otra forma no se producirá el movimiento deseado. Basta con arrastrar el bloque Set Velocity sobre Turbulence en el Stack.

Creando un shader para nuestras partículas

Lo interesante de este sistema de partículas no es el propio sistema, sino el hecho de poder cambiar el color de las mismas de forma individual basado en una textura. En este caso, utilizaremos texturas de nebulosas para representar de manera fiel los colores de nuestra galaxia.

Para comenzar, vamos a importar los assets necesarios: una textura de nebulosa y una textura para nuestras estrellas.

Ahora vamos a crear el shader para nuestras partículas con Right Click > Create > Shader Graph > VFX Shader Graph. Lo nombraremos PS_GalaxyStars (opcional). Hacemos doble clic sobre él para editarlo.

Nuestro shader será muy sencillo. Primero vamos a agregar un nodo Emission a nuestro stack Fragment. Esto nos permitirá crear el efecto de glow de nuestras partículas.

Ya que vamos a necesitar que nuestro sistema de partículas envíe datos (parámetros) a nuestro shader, vamos a utilizar dos properties dentro de este.

En nuestro blackboard vamos a crear una property de tipo Vector2 a la que llamaremos UV y otra de tipo Color que nombraremos de igual forma.

Por último vamos a explicar la estructura de nuestro shader:

  1. Conectaremos nuestra property UV a nuestra textura de nebulosa; a continuación vamos a multiplicar esta textura con nuestra textura de estrella. El resultado lo conectaremos con nuestro pin Base Color en el Fragment Stack.
  2. Usaremos el canal R de nuestra textura de estrella como mascara alpha de nuestro shader.
  3. Multiplicaremos nuestra multiplicación previa con nuestra property Color y el resultado lo conectaremos al pin Emission.

El resultado debería verse así:

PS_GalaxyStars

Color particle per point: visualizar partículas coloridas

Llegó el momento de utilizar nuestras nuevas partículas de estrella. Dentro de nuestro bloque padre Output Particle Quad vamos a eliminar el bloque Set Color Over Lifetime. En la propiedad Shader Graph vamos a seleccionar nuestro shader PS_GalaxyStars.

Una vez hecho esto, veremos como nuestras properties (parámetros) asignados en el shader aparecen dentro de nuestro VFX Graph. Estos parámetros están listos para enviar datos desde el graph a nuestro shader.

También cambiaremos nuestro bloque Set Size Over Lifetime, modificando la curva con los siguientes valores:

KeyTimeValue
100
20.250.5
30.750.5
410

Ahora viene la parte interesante. Con ayuda de un nodo Mesh (con una mesh Capsule asignada) vamos a obtener un segundo nodo de tipo Sample Mesh. Este segundo nodo nos ayudará a obtener los datos que enviaremos como parámetro UV a nuestro shader.

Vamos a modificar las propiedades de Sample Mesh de la siguiente forma:

  • Mode: Wrap
  • Placement Mode: Surface
  • Surface Coordinates: Barycentric

Conectaremos un nodo Random Number al pin Triangle del Sample Mesh. Conectaremos el pin TexCoord0 con nuestro parámetro UV en nuestro Output Quad.

Getting Mesh Data

Esto es lo que sucede: Sample Mesh utiliza los datos de la mesh de la que deriva. Usamos este nodo para obtener las coordenadas UV de la mesh a través de su superficie (triángulos) interpolada. A su vez, utilizamos un triángulo “aleatorio” para obtener su coordenada y así enviarla al shader.

Una vez el shader recibe la coordenada UV del espacio de la mesh, el parámetro se encarga de ubicar ese punto interpolado en el espacio UV de la textura de color (la nebulosa). Así, nuestra partícula obtiene el color de un punto en nuestra textura, sin interferir manualmente.

Agregando un subsistema de partículas

Para finalizar nuestro sistema principal, conectamos nuestro nodo Mesh al pin del bloque Position (Mesh) del bloque padre Initialize Particle. Vamos a cambiar manualmente el color de emisión de nuestra partícula en el bloque padre Output. El valor de color lo colocaremos totalmente blanco y la intensidad HDR en 5.

El resultado final del sistema principal debería verse así:

Nuestro sistema principal está listo, pero debemos agregar más estilo a nuestro efecto.

Primero vamos a duplicar nuestro sistema de partículas principal, pero no el asset, sino el stack dentro del VFX Graph. Una vez duplicado, debemos conectar nuestros nodos exteriores al stack justo como en el sistema principal. El graph debería verse algo como esto:

Vamos a realizar algunos cambios en este segundo sistema de partículas:

  1. Cambiaremos nuestro Spawn Rate a 100
  2. Modificaremos la capacidad del sistema a 1000 partículas (bloque Initialize Particle)
  3. Cambiaremos el parámetro Color del bloque Output Particle Quad a completamente blanco con intensidad HDR de 12

Ahora nuestro sistema está completo. Si visualizamos nuestra escena veremos un resultado genial, pero… aún se siente plano. Vamos a agregar un poco de “punch”.

Puliendo detalles: glow y composición final

Regresando a nuestro VP_GalaxyPS, agregaremos un nuevo override: Post Processing > Bloom. Marcaremos las siguientes casillas con estos valores:

  • Quality: High
  • Threshold: 0.5
  • Intensity: 0.5
  • Scatter: 0.4
  • Tint: FFD0F3

Ahora, al visualizar nuestra escena, el resultado es mucho más interesante.

Como último detalle, vamos a construir una composición más apropiada para una galaxia.

Duplicaremos nuestro objeto P_GalaxyPS y lo rotaremos 180 grados sobre el eje Y. Posicionaremos el sistema duplicado en X=-2, Y=0, Z=-4 y el sistema original en X=2, Y=0, Z=4. Como toque final, agregaremos una esfera en la posición 0, 0, 0.

Este es el resultado final de nuestro sistema:

VFX Graph – Galaxy

Siguientes pasos

Este sistema es interesante debido a que cada partícula obtiene el su color desde una coordenada en una textura, así que si deseas una galaxia de diferente color deberás probar a cambiar las texturas fuente.

Estos son los retos que te propongo:

  1. Convierte los datos necesarios en parámetros dentro del shader y el VFX Graph para que puedas modificar tu sistema de forma dinámica desde el inspector. Pista: el VFX Graph también puede utilizar properties/parámetros.
  2. Crea otro sistema de partículas para simular las nubes de gas dentro de una galaxia. Este efecto le suma mucho estilo visual a la propuesta.

Este tutorial fue posible gracias al contenido del canal CGHOW. Recomiendo que le des un vistazo para aprender más sobre VFX. Si quieres aprender a crear un shader de fog estilizado con Unreal Engine, te recomiendo mi post anterior.

Sigue aprendiendo, inténtalo, rompe las cosas y continua hasta lograr maestría. Hasta el próximo post.