La plupart des artistes techniques à un moment donné de leur carrière essaient de créer des reflets plausibles des caustiques. Si vous êtes un développeur de jeux, l'une des principales raisons de lire Twitter est le flot incessant d'inspiration que vous pouvez en tirer. Il y a quelques jours, Florian Gelzenlichter (
kolyaTQ sur Twitter) a publié un GIF de l'effet caustique créé dans Unity à l'aide de shaders. La publication (présentée ci-dessous) a rapidement gagné 1,5 mille likes, ce qui montre un intérêt sincère pour ce type de contenu.
Bien que je sois généralement plus attiré par des séries d'articles plus longues et techniquement complexes (par exemple, sur
la diffusion volumétrique de la lumière atmosphérique [
traduction sur Habré] et
la cinématique inverse [
première et
deuxième parties de la traduction sur Habré), je n'ai pas pu résister à la tentation d'écrire un tutoriel court et mignon sur les effets de
Florian .
À la fin de cet article, il y a un lien pour télécharger le package Unity et tous les actifs nécessaires.
Qu'est-ce que caustique
Vous ne
connaissez peut-être
pas le concept des
caustiques , bien que vous rencontriez cet effet quotidiennement. Les caustiques sont des reflets de lumière causés par des surfaces courbes. Dans le cas général, toute surface incurvée peut se comporter comme une lentille, focalisant la lumière à certains points et la diffusant à d'autres. Les médias les plus courants fournissant un tel effet sont le verre et l'eau, qui génèrent les
ondes dites
caustiques (voir ci-dessous).
Les caustiques peuvent prendre d'autres formes. Un arc-en-ciel, par exemple, est un phénomène optique qui se produit lorsque la lumière est réfractée dans les gouttes de pluie. Par conséquent, à proprement parler, c'est caustique.
Anatomie de l'effet
Une caractéristique reconnaissable des ondes caustiques est la façon dont elles se déplacent; vous l'avez probablement vu si vous avez déjà regardé le fond de la piscine. Recréer un véritable caustique est très coûteux car il nécessite la simulation de nombreux rayons lumineux.
Florian a réussi à créer un effet plausible, à commencer par une seule texture caustique. Pour créer mon tutoriel, j'ai utilisé la texture illustrée ci-dessous, tirée d'
OpenGameArt .
Une propriété importante qui permet de réaliser cet effet est que le motif caustique illustré ci-dessus est
sans couture . Cela signifie que vous pouvez placer deux images l'une à côté de l'autre et qu'il n'y aura pas de couture visible entre elles. Puisque nous voulons utiliser cet effet sur de grandes surfaces, il est important que nous ayons la possibilité d'étirer cette texture sans larmes qui peuvent détruire l'illusion.
Ayant reçu la texture,
Florian suggère de suivre trois étapes:
- Appliquer un motif caustique deux fois sur la surface du modèle, à chaque fois en utilisant différentes tailles et vitesses
- Mélanger deux motifs avec l'opérateur
min
- Séparez les canaux RVB lors de l'échantillonnage.
Voyons comment vous pouvez implémenter chacune des étapes dans Unity.
Création de shader
La première étape consiste à créer un nouveau shader. Étant donné que cet effet est susceptible d'être utilisé dans un jeu 3D qui dispose également d'un véritable éclairage, il est préférable de commencer avec un
shader de surface . Les shaders de surface sont l'un des nombreux types de shaders pris en charge par Unity (tels que les
shaders de vertex et de fragments pour les matériaux non éclairés,
les shaders d'écran pour les effets de post-traitement et
les shaders de calcul pour les simulations hors écran).
Le nouveau shader de surface n'a que quelques fonctionnalités. Pour créer cet effet, nous devons transférer des informations vers le shader. Le premier est la texture caustique. Deuxièmement, il s'agit du paramètre utilisé pour le mettre à l'échelle et le compenser.
Créons deux
propriétés de shader :
Properties { ... [Header(Caustics)] _CausticsTex("Caustics (RGB)", 2D) = "white" {}
et les
variables Cg correspondantes:
sampler2D _CausticsTex; float4 _Caustics_ST;
Les propriétés du shader correspondent aux champs affichés dans l'inspecteur de matériaux Unity. Les
variables Cg correspondantes sont les valeurs elles-mêmes, qui peuvent être utilisées dans le code du shader.
Comme vous pouvez le voir dans le code ci-dessus,
_Caustics_ST
est
float4
, c'est-à-dire qu'il contient quatre valeurs. Nous les utiliserons pour contrôler l'échantillonnage de la texture caustique. À savoir:
_Caustics_ST.x
: échelle de la texture caustique le long de l'axe X;_Caustics_ST.y
: échelle de la texture caustique le long de l'axe Y;_Caustics_ST.z
: déplacement de la texture caustique le long de l'axe X;_Caustics_ST.w
: déplacement de la texture caustique le long de l'axe Y _Caustics_ST.w
Pourquoi la variable s'appelle _Caustics_ST?Si vous avez déjà un peu d'expérience avec les shaders, vous avez déjà vu d'autres propriétés se terminant par le suffixe _ST
. Dans Unity, _ST
peut être utilisé pour ajouter des informations supplémentaires sur la façon dont la texture est échantillonnée.
Par exemple, si vous créez la variable Cg _MainTex_ST
, vous pouvez l'utiliser pour définir la taille et le décalage lors de l'application de la texture au modèle.
Les variables _ST
pas besoin de propriétés car elles sont automatiquement affichées dans l'inspecteur. Cependant, dans ce cas particulier, nous ne pouvons pas nous y fier car nous devons échantillonner la texture deux fois, à chaque fois avec une échelle et un décalage différents. À l'avenir, nous devons dupliquer cette variable en deux variables différentes.
Texture d'échantillonnage
Chaque
shader de surface contient une fonction, communément appelée
surf
, qui est utilisée pour déterminer la couleur de chaque pixel rendu. La fonction de
surf
«standard» ressemble à ceci:
void surf (Input IN, inout SurfaceOutputStandard o) {
La couleur finale est déterminée par le nombre de champs que le shader doit initialiser et renvoyer dans une structure appelée
SurfaceOutputStandard
. Nous devons changer l'
Albedo
, qui correspond à peu près à la couleur de l'objet éclairé par la lumière blanche.
Dans le shader de surface nouvellement créé, l'albédo est extrait d'une texture appelée
_MainTex
. Étant donné que l'effet caustique est superposé à la texture existante, nous devrons effectuer un échantillonnage supplémentaire de la texture dans
_CausticsTex
.
Une technique appelée
superposition UV vous permet de comprendre quelle partie de la texture doit être échantillonnée en fonction de la partie de la géométrie qui doit être rendue. Cela se fait en utilisant
uv_MainTex
- la variable
float2
, stockée à chaque sommet du modèle 3D et indiquant la coordonnée de la texture.
Notre idée est d'utiliser
_Caustics_ST
pour
_Caustics_ST
à l'échelle et compenser
uv_MainTex
pour étirer et déplacer la texture caustique à travers le modèle.
void surf (Input IN, inout SurfaceOutputStandard o) {
Que se passe-t-il si Albedo dépasse 1?Dans le code ci-dessus, nous ajoutons deux textures. La couleur est généralement comprise entre
avant
Cependant, rien ne garantit qu'en conséquence, certaines valeurs ne dépasseront pas cet intervalle.
Dans les shaders plus anciens, cela pourrait provoquer un problème. Ici, c'est en fait une
fonctionnalité . Si la valeur de couleur des pixels dépasse l'unité, cela signifie que son influence devrait «s'étendre» au-delà de ses frontières et affecter les pixels voisins.
C'est exactement ce qui se produit lorsque des réflexions spéculaires très lumineuses sont obtenues. Cependant, cet effet ne doit pas être créé uniquement par un ombrage de surface. Pour que l'effet fonctionne, la caméra doit avoir le
HDR activé. Cette propriété signifie
High Dynamic Range ; il permet aux valeurs de couleur de dépasser
. De plus, pour brouiller une quantité excessive de couleurs sur les pixels voisins, un effet de post-traitement est requis.
Unity a sa propre pile de post-traitement, qui a un filtre de floraison qui fait exactement cela. Vous pouvez en savoir plus à ce sujet sur le blog Unity:
PostFX v2 - Incroyable visuels, mis à niveau .
Les résultats préliminaires sont présentés ci-dessous:
Caustiques animés
L'une des caractéristiques les plus importantes des caustiques est leur mouvement. Pour le moment, ils sont simplement projetés statiquement sur la surface du modèle en tant que seconde texture.
L'animation des matériaux dans les shaders peut être implémentée à l'aide de la propriété Unity
_Time
. Il peut être utilisé pour accéder au temps de jeu actuel, c'est-à-dire ajouter du temps aux équations.
Le moyen le plus simple consiste à simplement décaler la texture en fonction de l'heure actuelle.
Le champ
_Time.y
contient le temps de lecture actuel en
secondes . Si la réflexion se déplace trop rapidement, vous pouvez la multiplier par un facteur. Pour cela, la variable
_CausticsSpeed
de type
float2
utilisée dans le code présenté ci-dessus.
Vous devrez peut-être faire vibrer la texture caustique dans une sinusoïde à vos fins. Il est important de comprendre ici qu'il n'y a pas de moyen standard de réaliser l'effet. Selon vos besoins, vous pouvez faire bouger les réflexions caustiques de manière complètement différente.
Les résultats ci-dessous sont encore assez médiocres. C'est normal: nous avons encore beaucoup à faire pour rendre les reflets magnifiques.
Échantillonnage multiple
L'effet devient vivant si vous échantillonnez la texture caustique non pas une, mais deux fois. Si vous les posez les uns sur les autres et les déplacez à des vitesses différentes, le résultat sera complètement différent.
Tout d'abord, nous
_CausticsSpeed
propriétés
_Caustics_ST
et
_CausticsSpeed
afin que les échantillons des deux textures aient des échelles, des déplacements et des vitesses différents:
[Header(Caustics)] _CausticsTex("Caustics (RGB)", 2D) = "white" {}
Maintenant que nous avons deux échantillons caustiques, ils peuvent être mélangés à l'aide de l'opérateur
min
. Si vous prenez simplement la valeur moyenne, le résultat ne sera pas très bon.
Un si petit changement fait une énorme différence:
Pour garder le code beau, vous pouvez également envelopper le code d'échantillonnage caustique dans votre propre fonction:
Séparation RVB
Pour que les reflets caustiques semblent bons, vous devez faire le dernier tour. En passant à travers une tranche, la lumière de différentes longueurs d'onde est réfractée différemment. Cela signifie que lorsque vous vous déplacez dans l'eau, la lumière peut "se diviser" en différentes couleurs.
Pour simuler cet effet, nous pouvons diviser chaque échantillon caustique en trois, un pour chaque canal de couleur. En échantillonnant les canaux rouge, vert et bleu avec un léger biais, nous obtenons un décalage de couleur.
Commençons par ajouter la propriété
_SplitRGB
, qui indique la force de l'effet de
_SplitRGB
-
_SplitRGB
:
La quantité de décalage des canaux RVB peut être sélectionnée arbitrairement, mais même avec ce décalage simple, une image très convaincante est obtenue:
Conclusion et téléchargements
Si vous êtes intéressé à apprendre à créer des textures caustiques sans couture, alors vous devriez lire l'article intéressant
Textures caustiques périodiques .
Pendant ce temps,
Florian continue de travailler sur son shader caustique et a apporté des améliorations assez intéressantes qui peuvent être vues.
Un package complet pour ce tutoriel est disponible sur Patreon, il comprend tous les atouts nécessaires pour recréer cette technique. Le package a été exporté depuis Unity 2019.2 et nécessite la pile de post-traitement v2.