Realistische ätzende Reflexionen


Die meisten technischen Künstler versuchen irgendwann in ihrer Karriere, plausible Reflexionen der Kaustik zu erzeugen. Wenn Sie ein Spieleentwickler sind, ist einer der Hauptgründe für das Lesen von Twitter der endlose Strom an Inspiration, den Sie daraus ziehen können. Vor einigen Tagen hat Florian Gelzenlichter ( kolyaTQ auf Twitter) ein GIF des in Unity mithilfe von Shadern erzeugten Ätzeffekts veröffentlicht. Der Beitrag (unten dargestellt) gewann schnell 1,5 Tausend Likes, was ein aufrichtiges Interesse an dieser Art von Inhalten zeigt.


Obwohl ich mich normalerweise mehr für längere und technisch komplexe Artikelserien interessiere (zum Beispiel über volumetrische atmosphärische Lichtstreuung [ Übersetzung auf Habré] und inverse Kinematik [ erster und zweiter Teil der Übersetzung auf Habré]), konnte ich der Versuchung nicht widerstehen, ein kurzes und niedliches Tutorial zu schreiben über die Auswirkungen von Florian .

Am Ende dieses Artikels befindet sich ein Link zum Herunterladen des Unity-Pakets und aller erforderlichen Assets.

Was ist ätzend


Möglicherweise kennen Sie das Konzept der Kaustik nicht , obwohl Sie diesen Effekt täglich feststellen. Ätzmittel sind Lichtreflexionen, die durch gekrümmte Oberflächen verursacht werden. Im allgemeinen Fall kann sich jede gekrümmte Oberfläche wie eine Linse verhalten, die Licht an einigen Punkten fokussiert und an anderen streut. Die gebräuchlichsten Medien, die einen solchen Effekt erzielen, sind Glas und Wasser, die sogenannte Ätzwellen erzeugen (siehe unten).


Ätzmittel können andere Formen annehmen. Ein Regenbogen ist beispielsweise ein optisches Phänomen, das auftritt, wenn Licht in Regentropfen gebrochen wird. Genau genommen ist es daher ätzend.

Anatomie der Wirkung


Ein erkennbares Merkmal von Ätzwellen ist die Art und Weise, wie sie sich bewegen. höchstwahrscheinlich hast du ihn gesehen, wenn du jemals auf den Boden des Pools geschaut hast. Die Wiederherstellung eines echten Ätzmittels ist sehr kostspielig, da viele Lichtstrahlen simuliert werden müssen.

Florian gelang es, einen plausiblen Effekt zu erzielen, beginnend mit einer einzigen ätzenden Textur. Um mein Tutorial zu erstellen, habe ich die unten gezeigte Textur aus OpenGameArt verwendet .


Eine wichtige Eigenschaft, mit der dieser Effekt realisiert werden kann, ist, dass das oben gezeigte Ätzmuster nahtlos ist . Dies bedeutet, dass Sie zwei Bilder nebeneinander platzieren können und es keine erkennbare Naht zwischen ihnen gibt. Da wir diesen Effekt auf großen Flächen nutzen möchten, ist es wichtig, dass wir die Möglichkeit haben, diese Textur ohne Risse zu dehnen, die die Illusion zerstören können.

Nachdem Florian die Textur erhalten hat, schlägt er drei Schritte vor:

  • Wenden Sie zweimal ein Ätzmuster auf die Oberfläche des Modells an, jedes Mal mit unterschiedlichen Größen und Geschwindigkeiten
  • Mischen Sie zwei Muster mit dem min Operator
  • Separate RGB-Kanäle beim Sampling.

Mal sehen, wie Sie die einzelnen Schritte in Unity implementieren können.

Shader-Erstellung


Der erste Schritt besteht darin, einen neuen Shader zu erstellen. Da dieser Effekt wahrscheinlich in einem 3D-Spiel verwendet wird, das auch über eine echte Beleuchtung verfügt, ist es am besten, mit einem Oberflächen-Shader zu beginnen . Oberflächen-Shader sind eine von vielen Arten von Shadern, die von Unity unterstützt werden (z. B. Vertex- und Fragment-Shader für unbeleuchtete Materialien, Screen-Shader für Nachbearbeitungseffekte und Computational-Shader für Simulationen außerhalb des Bildschirms).

Der neue Surface Shader verfügt nur über wenige Funktionen. Um diesen Effekt zu erzielen, müssen wir Informationen an den Shader übertragen. Das erste ist die ätzende Textur. Zweitens ist dies der Parameter, der zum Skalieren und Versetzen verwendet wird.

Erstellen wir zwei Shader-Eigenschaften :

 Properties { ... [Header(Caustics)] _CausticsTex("Caustics (RGB)", 2D) = "white" {} // Tiling X, Tiling Y, Offset X, Offset Y _Caustics_ST("Caustics ST", Vector) = (1,1,0,0) } 

und die entsprechenden Cg-Variablen :

 sampler2D _CausticsTex; float4 _Caustics_ST; 

Die Shader-Eigenschaften entsprechen den Feldern, die im Unity Material Inspector angezeigt werden. Die entsprechenden Cg-Variablen sind die Werte selbst, die im Shader-Code verwendet werden können.

Wie Sie dem obigen Code _Caustics_ST ist float4 , float4 es enthält vier Werte. Wir werden sie verwenden, um die Probenahme der ätzenden Textur zu steuern. Nämlich:

  • _Caustics_ST.x : Skala der ätzenden Textur entlang der X-Achse;
  • _Caustics_ST.y : Skala der ätzenden Textur entlang der Y-Achse;
  • _Caustics_ST.z : Verschiebung der ätzenden Textur entlang der X-Achse;
  • _Caustics_ST.w : Verschiebung der ätzenden Textur entlang der Y-Achse;

Warum heißt die Variable _Caustics_ST?
Wenn Sie bereits ein wenig Erfahrung mit Shadern haben, haben Sie bereits andere Eigenschaften gesehen, die mit dem Suffix _ST . In Unity kann _ST verwendet werden, um zusätzliche Informationen darüber hinzuzufügen, wie die Textur abgetastet wird.

Wenn Sie beispielsweise die Cg-Variable _MainTex_ST , können Sie damit die Größe und den Versatz _MainTex_ST , wenn Sie eine Textur auf das Modell anwenden.

Normalerweise _ST Variablen _ST Eigenschaften, da sie automatisch im Inspektor angezeigt werden. In diesem speziellen Fall können wir uns jedoch nicht darauf verlassen, da wir die Textur zweimal abtasten müssen, jedes Mal mit einem anderen Maßstab und Versatz. In Zukunft müssen wir diese Variable in zwei verschiedene Variablen duplizieren.

Abtasttextur


Jeder Oberflächen-Shader enthält eine Funktion, die üblicherweise als surf bezeichnet wird und zur Bestimmung der Farbe jedes gerenderten Pixels verwendet wird. Die "Standard" surf sieht folgendermaßen aus:

 void surf (Input IN, inout SurfaceOutputStandard o) { // Albedo comes from a texture tinted by color fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; // Metallic and smoothness come from slider variables o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = ca; } 

Die endgültige Farbe wird durch die Anzahl der Felder bestimmt, die der Shader initialisieren und in einer Struktur namens SurfaceOutputStandard . Wir müssen die Albedo ändern, die ungefähr der Farbe des von weißem Licht beleuchteten Objekts entspricht.

Im neu erstellten Surface Shader wird Albedo aus einer Textur namens _MainTex . Da der ätzende Effekt der vorhandenen Textur überlagert wird, müssen wir in _CausticsTex eine zusätzliche Abtastung der Textur _CausticsTex .

Mit einer als UV-Overlay bezeichneten Technik können Sie nachvollziehen, welcher Teil der Textur abgetastet werden muss, je nachdem, welcher Teil der Geometrie gerendert werden muss. Dies erfolgt mit uv_MainTex - der Variablen float2 , die an jedem Scheitelpunkt des 3D-Modells gespeichert ist und die Koordinate der Textur angibt.

Unsere Idee ist es, _Caustics_ST zu verwenden, um _Caustics_ST zu skalieren und zu uv_MainTex , um die ätzende Textur über das Modell zu strecken und zu bewegen.

 void surf (Input IN, inout SurfaceOutputStandard o) { // Albedo comes from a texture tinted by color fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; // Caustics sampling fixed2 uv = IN.uv_MainTex * _Caustics_ST.xy + _Caustics_ST.zw; fixed3 caustics = tex2D(_CausticsTex, uv).rgb; // Add o.Albedo.rgb += caustics; // Metallic and smoothness come from slider variables o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = ca; } 

Was passiert, wenn Albedo 1 überschreitet?
Im obigen Code fügen wir zwei Texturen hinzu. Farbe ist normalerweise zwischen 0vorher 1Es gibt jedoch keine Garantie dafür, dass einige Werte dieses Intervall nicht überschreiten.

Bei älteren Shadern kann dies zu Problemen führen. Hier ist es eigentlich eine Funktion . Wenn der Pixelfarbwert die Einheit überschreitet, bedeutet dies, dass sich sein Einfluss über seine Grenzen hinaus „ausbreiten“ und benachbarte Pixel beeinflussen sollte.

Dies ist genau das, was passiert, wenn sehr helle Spiegelreflexionen erhalten werden. Dieser Effekt sollte jedoch nicht nur von einem Surface Shader erzeugt werden. Damit der Effekt funktioniert, muss HDR auf der Kamera eingeschaltet sein. Diese Eigenschaft steht für High Dynamic Range ; Dadurch können Farbwerte überschritten werden 1. Um eine übermäßige Menge an Farben auf benachbarten Pixeln zu verwischen, ist ein Nachbearbeitungseffekt erforderlich.

Unity verfügt über einen eigenen Nachbearbeitungsstapel mit einem Bloom-Filter, der genau das tut. Weitere Informationen hierzu finden Sie im Unity-Blog: PostFX v2 - Erstaunliche Grafik, aktualisiert .

Vorläufige Ergebnisse sind unten gezeigt:


Animierte Ätzmittel


Eines der wichtigsten Merkmale der Kaustik ist ihre Bewegung. Im Moment werden sie einfach statisch als zweite Textur auf die Oberfläche des Modells projiziert.

Die Animation von Materialien in Shadern kann mithilfe der Unity-Eigenschaft _Time implementiert werden. Es kann verwendet werden, um auf die aktuelle Spielzeit zuzugreifen, dh um den Gleichungen Zeit hinzuzufügen.

Am einfachsten ist es, die Textur einfach basierend auf der aktuellen Zeit zu versetzen.

 // Caustics UV fixed2 uv = IN.uv_MainTex * _Caustics_ST.xy + _Caustics_ST.zw; uv += _CausticsSpeed * _Time.y; // Sampling fixed3 caustics = tex2D(_CausticsTex, uv).rgb; // Add o.Albedo.rgb += caustics; 

Das Feld _Time.y enthält die aktuelle Spielzeit in Sekunden . Wenn sich die Reflexion zu schnell bewegt, können Sie sie mit einem Faktor multiplizieren. Hierzu wird im oben dargestellten Code die Variable _CausticsSpeed vom Typ float2 verwendet.

Möglicherweise müssen Sie die ätzende Textur in einer Sinuskurve für Ihre Zwecke vibrieren lassen. Es ist wichtig zu verstehen, dass es keinen Standardweg gibt, um den Effekt zu realisieren. Abhängig von Ihren Anforderungen können Sie ätzende Reflexionen ganz anders bewegen.

Die unten gezeigten Ergebnisse sind immer noch ziemlich mittelmäßig. Das ist normal: Wir haben noch viel zu tun, damit die Reflexionen schön aussehen.


Mehrfachabtastung


Der Effekt wird lebendig, wenn Sie die ätzende Textur nicht nur einmal, sondern zweimal abtasten. Wenn Sie sie übereinander legen und mit unterschiedlichen Geschwindigkeiten bewegen, ist das Ergebnis völlig unterschiedlich.

Zunächst duplizieren wir die Eigenschaften _Caustics_ST und _CausticsSpeed sodass die Samples der beiden Texturen unterschiedliche Maßstäbe, Verschiebungen und Geschwindigkeiten aufweisen:

 [Header(Caustics)] _CausticsTex("Caustics (RGB)", 2D) = "white" {} // Tiling X, Tiling Y, Offset X, Offset Y _Caustics1_ST("Caustics 1 ST", Vector) = (1,1,0,0) _Caustics2_ST("Caustics 1 ST", Vector) = (1,1,0,0) // Speed X, Speed Y _Caustics1_Speed("Caustics 1 Speed", Vector) = (1, 1, 0 ,0) _Caustics2_Speed("Caustics 2 Speed", Vector) = (1, 1, 0 ,0) 

Nachdem wir nun zwei ätzende Proben haben, können diese mit dem min Operator gemischt werden. Wenn Sie nur den Durchschnittswert nehmen, ist das Ergebnis nicht sehr gut.

 // Caustics samplings fixed3 caustics1 = ... fixed3 caustics2 = ... // Blend o.Albedo.rgb += min(caustics1, caustics2); 

Eine so kleine Änderung macht einen großen Unterschied:


Um den Code schön zu halten, können Sie den ätzenden Abtastcode auch in Ihre eigene Funktion einbinden:

 // Caustics fixed3 c1 = causticsSample(_CausticsTex, IN.uv_MainTex, _Caustics1_ST, _Caustics1_Speed); fixed3 c2 = causticsSample(_CausticsTex, IN.uv_MainTex, _Caustics2_ST, _Caustics2_Speed); o.Albedo.rgb += min(c1, c2); 

RGB-Trennung


Damit die ätzenden Reflexionen gut aussehen, müssen Sie den letzten Trick ausführen. Beim Durchlaufen einer Schicht wird Licht unterschiedlicher Wellenlänge unterschiedlich gebrochen. Dies bedeutet, dass sich das Licht bei der Bewegung durch Wasser in verschiedene Farben "aufteilen" kann.

Um diesen Effekt zu simulieren, können wir jede ätzende Probe in drei Teile unterteilen, einen für jeden Farbkanal. Durch Abtasten der roten, grünen und blauen Kanäle mit einer leichten Abweichung erhalten wir eine Farbfehlanpassung.

Beginnen wir mit dem Hinzufügen der Eigenschaft _SplitRGB , die die Stärke des _SplitRGB Effekts angibt:

 // Caustics UV fixed2 uv = IN.uv_MainTex * _Caustics_ST.xy + _Caustics_ST.zw; uv += _CausticsSpeed * _Time.y; // RGB split fixed s = _SplitRGB; fixed r = tex2D(tex, uv + fixed2(+s, +s)).r; fixed g = tex2D(tex, uv + fixed2(+s, -s)).g; fixed b = tex2D(tex, uv + fixed2(-s, -s)).b; fixed3 caustics = fixed3(r, g, b); 

Die Höhe des Versatzes der RGB-Kanäle kann beliebig gewählt werden, aber selbst mit diesem einfachen Versatz wird ein sehr überzeugendes Bild erhalten:


Fazit und Downloads


Wenn Sie lernen möchten, wie Sie nahtlose ätzende Texturen erstellen, sollten Sie den interessanten Artikel Periodische ätzende Texturen lesen.

In der Zwischenzeit arbeitet Florian weiter an seinem ätzenden Shader und hat einige interessante Verbesserungen vorgenommen, die sichtbar werden.


Ein vollständiges Paket für dieses Tutorial ist auf Patreon verfügbar. Es enthält alle erforderlichen Elemente, um diese Technik neu zu erstellen. Das Paket wurde aus Unity 2019.2 exportiert und erfordert Postprocessing Stack v2.

Source: https://habr.com/ru/post/de467887/


All Articles