La mayoría de los artistas técnicos en algún momento de sus carreras intentan crear reflejos plausibles de cáusticos. Si eres un desarrollador de juegos, entonces una de las principales razones para leer Twitter es el flujo interminable de inspiración que puedes obtener de él. Hace unos días, Florian Gelzenlichter (
kolyaTQ en Twitter) publicó un GIF del efecto cáustico creado en Unity usando sombreadores. La publicación (presentada a continuación) ganó rápidamente 1,5 mil me gusta, lo que muestra un sincero interés en este tipo de contenido.
Aunque generalmente me atraen más las series de artículos más largas y técnicamente complejas (por ejemplo, sobre
la dispersión volumétrica de la luz atmosférica [
traducción en Habré] y la
cinemática inversa [
primera y
segunda parte de la traducción en Habré], no pude resistir la tentación de escribir un tutorial corto y lindo sobre los efectos de
Florian .
Al final de este artículo hay un enlace para descargar el paquete de Unity y todos los recursos necesarios.
¿Qué es cáustico?
Es posible que no
conozca el concepto de
cáusticos , aunque encuentre este efecto a diario. Los cáusticos son reflejos de luz causados por superficies curvas. En el caso general, cualquier superficie curva puede comportarse como una lente, enfocando la luz en algunos puntos y dispersándola en otros. Los medios más comunes que proporcionan tal efecto son el vidrio y el agua, que genera las llamadas
ondas cáusticas (ver más abajo).
Los cáusticos pueden tomar otras formas. Un arco iris, por ejemplo, es un fenómeno óptico que ocurre cuando la luz se refracta en las gotas de lluvia. Por lo tanto, estrictamente hablando, es cáustico.
Anatomía del efecto
Una característica reconocible de las ondas cáusticas es la forma en que se mueve; lo más probable es que lo hayas visto si alguna vez miraste el fondo de la piscina. La recreación de un cáustico real es muy costosa porque requiere la simulación de muchos rayos de luz.
Florian logró crear un efecto plausible, comenzando con una textura cáustica única. Para crear mi tutorial, utilicé la textura que se muestra a continuación, tomada de
OpenGameArt .
Una propiedad importante que permite realizar este efecto es que el patrón cáustico que se muestra arriba es
perfecto . Esto significa que puede colocar dos imágenes una al lado de la otra y no habrá una costura notable entre ellas. Como queremos usar este efecto en grandes superficies, es importante que tengamos la oportunidad de estirar esta textura sin rasgaduras que puedan destruir la ilusión.
Habiendo recibido la textura,
Florian sugiere seguir tres pasos:
- Aplique un patrón cáustico dos veces a la superficie del modelo, cada vez con diferentes tamaños y velocidades.
- Mezcle dos patrones con el operador
min
- Canales RGB separados al muestrear.
Veamos cómo puede implementar cada uno de los pasos en Unity.
Creación de sombreadores
El primer paso es crear un nuevo sombreador. Dado que es probable que este efecto se use en un juego 3D que también tiene iluminación real, es mejor comenzar con un
sombreador de superficie . Los sombreadores de superficie son uno de los muchos tipos de sombreadores compatibles con Unity (como
sombreadores de vértices y fragmentos para materiales no iluminados,
sombreadores de pantalla para efectos de procesamiento posterior y
sombreadores computacionales para simulaciones fuera de pantalla).
El nuevo sombreador de superficie tiene solo algunas características. Para crear este efecto, necesitamos transferir información al sombreador. El primero es la textura cáustica. En segundo lugar, este es el parámetro utilizado para escalarlo y compensarlo.
Creemos dos
propiedades de sombreador :
Properties { ... [Header(Caustics)] _CausticsTex("Caustics (RGB)", 2D) = "white" {}
y las correspondientes
variables de Cg :
sampler2D _CausticsTex; float4 _Caustics_ST;
Las propiedades del sombreador corresponden a los campos que se muestran en el Inspector de materiales de Unity. Las
variables Cg correspondientes son los valores mismos, que se pueden usar en el código del sombreador.
Como puede ver en el código anterior,
_Caustics_ST
es
float4
, es decir, contiene cuatro valores. Los utilizaremos para controlar el muestreo de la textura cáustica. A saber:
_Caustics_ST.x
: escala de la textura cáustica a lo largo del eje X;_Caustics_ST.y
: escala de la textura cáustica a lo largo del eje Y;_Caustics_ST.z
: desplazamiento de la textura cáustica a lo largo del eje X;_Caustics_ST.w
: desplazamiento de la textura cáustica a lo largo del eje Y;
¿Por qué la variable se llama _Caustics_ST?Si ya tiene un poco de experiencia trabajando con sombreadores, ya ha visto otras propiedades que terminan con el sufijo _ST
. En Unity, _ST
puede usarse para agregar información adicional sobre cómo se muestrea la textura.
Por ejemplo, si crea la variable Cg _MainTex_ST
, puede usarla para establecer el tamaño y el desplazamiento al aplicar textura al modelo.
Por _ST
general, _ST
variables _ST
no necesitan propiedades porque se muestran automáticamente en el inspector. Sin embargo, en este caso particular, no podemos confiar en esto porque necesitamos muestrear la textura dos veces, cada vez con una escala y un desplazamiento diferentes. En el futuro, necesitamos duplicar esta variable en dos variables diferentes.
Textura de muestreo
Cada
sombreador de superficie contiene una función, comúnmente llamada
surf
, que se utiliza para determinar el color de cada píxel renderizado. La función de
surf
"estándar" se ve así:
void surf (Input IN, inout SurfaceOutputStandard o) {
El color final está determinado por la cantidad de campos que el sombreador debe inicializar y devolver en una estructura llamada
SurfaceOutputStandard
. Necesitamos cambiar
Albedo
, que coincide aproximadamente con el color del objeto iluminado por la luz blanca.
En el sombreador de superficie recién creado, el albedo se toma de una textura llamada
_MainTex
. Dado que el efecto cáustico se superpone sobre la textura existente, tendremos que realizar un muestreo adicional de la textura en
_CausticsTex
.
Una técnica llamada
superposición UV le permite comprender qué parte de la textura necesita ser muestreada dependiendo de qué parte de la geometría necesita ser renderizada. Esto se hace usando
uv_MainTex
, la variable
float2
, almacenada en cada vértice del modelo 3D e indicando la coordenada de la textura.
Nuestra idea es usar
_Caustics_ST
para escalar y compensar
uv_MainTex
para estirar y mover la textura cáustica a través del modelo.
void surf (Input IN, inout SurfaceOutputStandard o) {
¿Qué pasa si Albedo excede 1?En el código anterior, agregamos dos texturas. El color suele estar entre
antes
Sin embargo, no hay garantía de que, como resultado, algunos valores no excedan este intervalo.
En sombreadores más antiguos, esto podría causar un problema. Aquí es en realidad una
característica . Si el valor del color del píxel excede la unidad, esto significa que su influencia debería "extenderse" más allá de sus bordes y afectar a los píxeles vecinos.
Esto es exactamente lo que sucede cuando se obtienen reflejos especulares muy brillantes. Sin embargo, este efecto no debe ser creado solo por un sombreador de superficie. Para que el efecto funcione, la cámara debe tener
HDR activado. Esta propiedad significa
alto rango dinámico ; permite que los valores de color excedan
. Además, para desenfocar una cantidad excesiva de colores en píxeles vecinos, se requiere un efecto de procesamiento posterior.
Unity tiene su propia pila de procesamiento posterior, que tiene un filtro de floración que hace exactamente eso. Puede leer más sobre esto en el blog de Unity:
PostFX v2 - Imágenes increíbles, actualizadas .
Los resultados preliminares se muestran a continuación:
Cáusticos animados
Una de las características más importantes de los cáusticos es cómo se mueve. Por el momento, simplemente se proyectan estáticamente en la superficie del modelo como una segunda textura.
La animación de materiales en sombreadores se puede implementar utilizando la propiedad Unity
_Time
. Se puede usar para acceder al tiempo de juego actual, es decir, agregar tiempo a las ecuaciones.
La forma más fácil es simplemente compensar la textura en función de la hora actual.
El campo
_Time.y
contiene el tiempo de reproducción actual en
segundos . Si el reflejo se mueve demasiado rápido, puede multiplicarlo por un factor. Para esto, la variable
_CausticsSpeed
de tipo
float2
usa en el código presentado anteriormente.
Es posible que necesite hacer vibrar la textura cáustica en una sinusoide para sus propósitos. Es importante entender aquí que no hay una forma estándar de realizar el efecto. Dependiendo de sus necesidades, puede hacer que los reflejos cáusticos se muevan de manera completamente diferente.
Los resultados que se muestran a continuación siguen siendo bastante mediocres. Esto es normal: todavía tenemos mucho por hacer para que los reflejos se vean hermosos.
Muestreo múltiple
El efecto cobra vida si muestras la textura cáustica no una, sino dos veces. Si los coloca uno encima del otro y los mueve a diferentes velocidades, el resultado será completamente diferente.
Primero,
_CausticsSpeed
propiedades
_Caustics_ST
y
_CausticsSpeed
para que las muestras de las dos texturas tengan diferentes escalas, desplazamientos y velocidades:
[Header(Caustics)] _CausticsTex("Caustics (RGB)", 2D) = "white" {}
Ahora que tenemos dos muestras cáusticas, se pueden mezclar usando el operador
min
. Si solo toma el valor promedio, el resultado no será muy bueno.
Un cambio tan pequeño hace una gran diferencia:
Para mantener el código hermoso, también puede ajustar el código de muestreo cáustico en su propia función:
Separación RGB
Para que los reflejos cáusticos se vean bien, debes hacer el último truco. Al pasar a través de un corte, la luz de diferentes longitudes de onda se refracta de manera diferente. Esto significa que cuando se mueve a través del agua, la luz puede "dividirse" en diferentes colores.
Para simular este efecto, podemos dividir cada muestra cáustica en tres, una para cada canal de color. Al muestrear los canales rojo, verde y azul con un ligero sesgo, obtenemos una falta de coincidencia de color.
Comencemos agregando la propiedad
_SplitRGB
, que indica la fuerza del efecto de
_SplitRGB
-
_SplitRGB
:
La cantidad de desplazamiento de los canales RGB se puede seleccionar arbitrariamente, pero incluso con este desplazamiento simple, se obtiene una imagen muy convincente:
Conclusión y descargas
Si está interesado en aprender cómo crear texturas cáusticas sin costura, entonces debería leer el interesante artículo
Texturas cáusticas periódicas .
Mientras tanto,
Florian continúa trabajando en su sombreador cáustico y ha realizado algunas mejoras bastante interesantes que se pueden ver.
Un paquete completo para este tutorial está disponible en Patreon, incluye todos los recursos necesarios para recrear esta técnica. El paquete se exportó desde Unity 2019.2 y requiere la pila de posprocesamiento v2.