Un flowmap es un mapa que codifica un vector de dos dimensiones en cada pixel. Usamos este vector para distorsionar las coordenadas UV. Con esta técnica se pueden conseguir efectos muy logrados a bajo coste para agua, nube, arena, nieve, etc.,
Vamos a ver como usar el flowmap a mano con objeto de entender bien el proceso.
A diferencia de un mapa de normales que codifica en cada pixel un vector de tres dimensiones, un flowmap codifica un vector de dos dimensiones.
Aquí un ejemplo de flowmap:
Fuente: http://graphicsrunner.blogspot.com/2010/08/water-using-flow-maps.html
Dónde el canal rojo (R) corresponde al eje X y el canal verde (G) corresponde al eje Y. En este sentido, un pixel de color rojo codifica el vector (1,0), mientras que el color amarillo codifica (1,1).
¿Quiere decir esto que no podemos codificar vectores cuyas componentes sean negativas (-1, -0.5) por ejemplo? No. Una vez obtenido el color del pixel podemos restarle uno y multiplicarlo por dos. De este modo podemos convertir remapear el rango [0, 1] a [-1, -1].
Dónde el nodo ContantBiasScale hace precisamente eso, restar uno y multiplicar por dos:
Tal y como hicimos cuando hablamos de distorsion en las UV, podemos aplicar el mismo principio aquí usando el flowmap como textura de distorsión:
Obtenemos la siguiente distorsión:
Justo lo que pretendemos, distorsionar siguiente "un flujo" definido en el flowmap como vectores indicando hacia dónde "fluye".
Añadimos el factor tiempo:
Y nos quedaría el siguiente efecto:
Dado que usamos una fracción al tomar la salida FracTime del nodo TimeWithSpeedVariable tenemos un salto en la animación.
¿Por qué no usamos la salida Time que es continua sin saltos? Porque Time no está acotado y la distorsión podría ser absurdamente alta. ¡Prueba y lo verás!.
¿Cómo resolvemos la discontinuidad de FracTime? Puedes usar una función como coseno o seno:
Con esto evitamos el salto. También podríamos usar una función triangular tal y como propone Valve aquí:
Sustituyendo al seno quedaría:
Aunque hemos eliminado el problema de la continuidad, ahora tenemos el problema de la "pulsación", es decir, se nota demasiado que la animación se reinicia:
Antes de arreglar la pulsación, vamos a refactorizar el material.
Material Function
Vamos a crear un MaterialFunction para calcular la distorsión y así poder reutilizarla:
La función quedaría:
Y el material principal haciendo uso de la función:
Eliminar la pulsación
La idea, también de la conferencia de Valve, es llamar dos veces al MaterialFunction anterior pero cambiando la "fase", de este modo podemos interpolar ambas distorsiones para eliminar la pulsación:
Incluso usar una textura de ruido para combinarla con lo anterior como alpha del lerp.
Nodo Flowmap2D
Entendida la idea base, UE4 nos hace la vida mucho más fácil y ya tiene implementada la lógica para usar un flowmap. ¡El nodo Flowmap!
Cuyo resultado:
Cambiando las texturas de entrada:
Gamma y compresión
Por defecto cuando importas una textura en UE4 se entiende que está gamma-codificada.
Esto está muy bien para texturas de color en general, pero cuando la información en la textura no es un color, si no que están codificando un vector, es un problema.
Para importar correctamente la textura de flowmap, y en general cualquier textura que no representa un color real, debes marcar las siguientes opciones:
También dejamos la textura sin comprimir para que no aparezcan artefactos.