Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Game Development
  2. Shaders

Toon-Wasser für das Web erstellen: Teil 3

by
Difficulty:AdvancedLength:LongLanguages:
This post is part of a series called Creating Toon Water for the Web.
Creating Toon Water for the Web: Part 2

German (Deutsch) translation by Alex Grigorovich (you can also view the original English article)

Willkommen zurück zu dieser dreiteiligen Serie zum Erstellen von stilisiertem Toon-Wasser in PlayCanvas mithilfe von Vertex-Shadern. In Teil 2 haben wir Auftriebs- und Schaumlinien behandelt. In diesem letzten Teil werden wir die Unterwasserverzerrung als Nachbearbeitungseffekt anwenden.

Brechungs- und Nachbearbeitungseffekte

Unser Ziel ist es, die Lichtbrechung durch Wasser visuell zu kommunizieren. Wie Sie diese Art von Verzerrung in einem Fragment-Shader in einem früheren Tutorial für eine 2D-Szene erstellen, haben wir bereits behandelt. Der einzige Unterschied besteht darin, dass wir herausfinden müssen, welcher Bereich des Bildschirms sich unter Wasser befindet, und nur dort die Verzerrung anwenden müssen.

Nachbearbeitung

Im Allgemeinen ist ein Nachbearbeitungseffekt alles, was nach dem Rendern auf die gesamte Szene angewendet wird, z. B. ein Farbton oder ein alter CRT-Bildschirmeffekt. Anstatt Ihre Szene direkt auf dem Bildschirm zu rendern, rendern Sie sie zuerst in einen Puffer oder eine Textur und rendern diese dann über einen benutzerdefinierten Shader auf den Bildschirm.

In PlayCanvas können Sie einen Nachbearbeitungseffekt einrichten, indem Sie ein neues Skript erstellen. Nennen Sie es Refraction.js und kopieren Sie diese Vorlage zunächst:

Dies ist wie bei einem normalen Skript, wir definieren jedoch eine RefractionPostEffect-Klasse, die auf die Kamera angewendet werden kann. Dies erfordert einen Scheitelpunkt und einen Fragment-Shader zum Rendern. Die Attribute sind bereits eingerichtet. Erstellen wir also Refraction.frag mit folgendem Inhalt:

Und Refraction.vert mit einem einfachen Vertex-Shader:

Hängen Sie nun das Skript Refraction.js an die Kamera an und weisen Sie die Shader den entsprechenden Attributen zu. Wenn Sie das Spiel starten, sollten Sie die Szene genau so sehen, wie sie vorher war. Das ist ein leerer Post-Effekt, der die Szene einfach neu rendert. Um zu überprüfen, ob dies funktioniert, geben Sie der Szene einen roten Farbton.

Versuchen Sie in Refraction.frag, anstatt einfach die Farbe zurückzugeben, die rote Komponente auf 1,0 zu setzen, was wie im Bild unten aussehen sollte.

Scene rendered with a red tint

Verzerrung Shader

Wir müssen eine Zeituniform für die animierte Verzerrung hinzufügen. Erstellen Sie also eine in Refraction.js in diesem Konstruktor für den Post-Effekt:

Innerhalb dieser Renderfunktion übergeben wir sie nun an unseren Shader und erhöhen sie:

Jetzt können wir denselben Shader-Code aus dem Tutorial zur Wasserverzerrung verwenden, sodass unser vollständiger Fragment-Shader folgendermaßen aussieht:

Wenn alles geklappt hat, sollte jetzt alles so aussehen, als wäre es unter Wasser, wie unten.

Underwater distortion applied to the whole scene
Herausforderung Nr. 1: Stellen Sie sicher, dass die Verzerrung nur auf die untere Hälfte des Bildschirms angewendet wird.

Kameramasken

Wir sind fast da. Jetzt müssen wir diesen Verzerrungseffekt nur noch auf den Unterwasserteil des Bildschirms anwenden. Der einfachste Weg, dies zu tun, besteht darin, die Szene neu zu rendern, wobei die Wasseroberfläche wie unten gezeigt als festes Weiß dargestellt wird.

Water surface rendered as a solid white to act as a mask

Dies würde zu einer Textur gerendert, die als Maske fungieren würde. Wir würden diese Textur dann an unseren Refraktions-Shader übergeben, der ein Pixel im endgültigen Bild nur dann verzerrt, wenn das entsprechende Pixel in der Maske weiß ist.

Fügen wir der Wasseroberfläche ein boolesches Attribut hinzu, um festzustellen, ob es als Maske verwendet wird. Fügen Sie dies zu Water.js hinzu:

Wir können es dann mit material.setParameter('isMask',this.isMask) an den Shader übergeben. wie gewöhnlich. Deklarieren Sie es dann in Water.frag und setzen Sie die Farbe auf Weiß, wenn es wahr ist.

Bestätigen Sie, dass dies funktioniert, indem Sie die Option "Ist Maske?" Eigenschaft im Editor und Neustart des Spiels. Es sollte weiß aussehen, wie im vorherigen Bild.

Um die Szene neu zu rendern, benötigen wir eine zweite Kamera. Erstellen Sie im Editor eine neue Kamera und nennen Sie sie CameraMask. Duplizieren Sie die Entität Water auch im Editor und nennen Sie sie WaterMask. Stellen Sie sicher, dass "Ist Maske?" ist falsch für die Entität Water, aber wahr für die WaterMask.

Um die neue Kamera anzuweisen, anstelle des Bildschirms eine Textur zu rendern, erstellen Sie ein neues Skript mit dem Namen CameraMask.js und hängen Sie es an die neue Kamera an. Wir erstellen ein RenderTarget, um die Ausgabe dieser Kamera wie folgt zu erfassen:

Wenn Sie jetzt starten, sehen Sie, dass diese Kamera nicht mehr auf dem Bildschirm gerendert wird. Wir können die Ausgabe des Renderziels in Refraction.js wie folgt abrufen:

Beachten Sie, dass ich diese Maskentextur als Argument an den Post-Effekt-Konstruktor übergebe. Wir müssen in unserem Konstruktor einen Verweis darauf erstellen, damit es so aussieht:

Übergeben Sie schließlich in der Renderfunktion den Puffer an unseren Shader mit:

Um zu überprüfen, ob dies alles funktioniert, lasse ich das als Herausforderung.

Herausforderung Nr. 2: Rendern Sie den uMaskBuffer auf dem Bildschirm, um zu betätigen, dass es sich um die Ausgabe der zweiten Kamera handelt.

Beachten Sie, dass das Renderziel bei der Initialisierung von CameraMask.js eingerichtet wird und zum Zeitpunkt des Aufrufs von Refraction.js bereit sein muss. Wenn die Skripte umgekehrt ausgeführt werden, wird eine Fehlermeldung angezeigt. Um sicherzustellen, dass sie in der richtigen Reihenfolge ausgeführt werden, ziehen Sie die CameraMask wie unten gezeigt an den Anfang der Entitätsliste im Editor.

PlayCanvas editor with CameraMask at top of entity list

Die zweite Kamera sollte immer dieselbe Ansicht wie die ursprüngliche haben. Lassen Sie sie daher beim Update von CameraMask.js immer ihrer Position und Drehung folgen:

Und definieren Sie CameraToFollow in der Initialisierung:

Keulungsmasken

Beide Kameras rendern derzeit dasselbe. Wir möchten, dass die Maskenkamera alles außer dem echten Wasser rendert, und wir möchten, dass die echte Kamera alles außer dem Maskenwasser rendert.

Dazu können wir die Culling-Bit-Maske der Kamera verwenden. Dies funktioniert ähnlich wie bei Kollisionsmasken, wenn Sie diese jemals verwendet haben. Ein Objekt wird ausgesondert (nicht gerendert), wenn das Ergebnis eines bitweisen AND zwischen seiner Maske und der Kameramaske 1 ist.

Für das Wasser wird Bit 2 und für die Wassermaske Bit 3 gesetzt. Dann müssen für die reale Kamera alle Bits außer 3 gesetzt sein, und für die Maskenkamera müssen alle Bits außer 2 gesetzt sein. Eine einfache Möglichkeit, dies zu sagen "all bits except N" ist zu tun:

Weitere Informationen zu bitweisen Operatoren finden Sie hier.

Um die Culling-Masken für die Kamera einzurichten, können Sie diese unten in die Initialisierung von CameraMask.js einfügen:

Setzen Sie nun in Water.js die Maske des Wassernetzes auf Bit 2 und die Maskenversion auf Bit 3:

Jetzt hat eine Ansicht das normale Wasser und die andere das feste weiße Wasser. Die linke Bildhälfte unten zeigt die Ansicht der Originalkamera und die rechte Hälfte die Maskenkamera.

Split view of mask camera and original camera

Anwenden der Maske

Ein letzter Schritt jetzt! Wir wissen, dass die Bereiche unter Wasser mit weißen Pixeln markiert sind. Wir müssen nur überprüfen, ob wir kein weißes Pixel haben, und wenn ja, die Verzerrung in Refraction.frag deaktivieren:

Und das sollte es tun!

Beachten Sie, dass die Textur für die Maske beim Start initialisiert wird. Wenn Sie die Größe des Fensters zur Laufzeit ändern, entspricht sie nicht mehr der Größe des Bildschirms.

Kantenglättung

Als optionalen Bereinigungsschritt haben Sie möglicherweise bemerkt, dass die Kanten in der Szene jetzt etwas scharf aussehen. Dies liegt daran, dass wir beim Anwenden unseres Post-Effekts das Anti-Aliasing verloren haben.

Wir können zusätzlich zu unserem Effekt einen zusätzlichen Anti-Alias als weiteren Post-Effekt anwenden. Glücklicherweise gibt es eine im PlayCanvas-Store, die wir einfach verwenden können. Gehen Sie zur Seite mit den Skript-Assets, klicken Sie auf die große grüne Schaltfläche zum Herunterladen und wählen Sie Ihr Projekt aus der angezeigten Liste aus. Das Skript wird im Stammverzeichnis Ihres Asset-Fensters als posteffect-fxaa.js angezeigt. Fügen Sie dies einfach der Kamera-Entität hinzu, und Ihre Szene sollte ein wenig schöner aussehen!

Abschließende Gedanken

Wenn Sie es bis hierher geschafft haben, klopfen Sie sich auf den Rücken! Wir haben in dieser Serie viele Techniken behandelt. Sie sollten jetzt mit Vertex-Shadern vertraut sein, Texturen rendern, Nachbearbeitungseffekte anwenden, Objekte selektiv aussortieren, den Tiefenpuffer verwenden und mit Überblendung und Transparenz arbeiten. Obwohl wir dies in PlayCanvas implementiert haben, sind dies alles allgemeine Grafikkonzepte, die Sie in irgendeiner Form auf jeder Plattform finden, auf der Sie landen.

Alle diese Techniken sind auch auf eine Vielzahl anderer Effekte anwendbar. Eine besonders interessante Anwendung, die ich für Vertex-Shader gefunden habe, ist in diesem Vortrag über die Kunst von Abzu, in dem erklärt wird, wie sie Vertex-Shader verwendet haben, um Zehntausende von Fischen auf dem Bildschirm effizient zu animieren.

Sie sollten jetzt auch einen schönen Wassereffekt haben, den Sie auf Ihre Spiele anwenden können! Sie können es jetzt ganz einfach anpassen, da Sie jedes Detail selbst zusammengestellt haben. Mit Wasser kann man noch viel mehr anfangen (ich habe überhaupt keine Reflexion erwähnt). Im Folgenden finden Sie einige Ideen.

Rauschbasierte Wellen

Anstatt die Wellen einfach mit einer Kombination aus Sinus und Cosinus zu animieren, können Sie eine Rauschstruktur abtasten, um die Wellen etwas natürlicher und unvorhersehbarer erscheinen zu lassen.

Dynamische Schaumwege

Anstelle von vollständig statischen Wasserlinien auf der Oberfläche können Sie auf diese Textur zeichnen, wenn sich Objekte bewegen, um eine dynamische Schaumspur zu erstellen. Es gibt viele Möglichkeiten, dies zu tun, daher könnte dies ein eigenes Projekt sein.

Quellcode

Das fertige gehostete PlayCanvas-Projekt finden Sie hier. In diesem Repository ist auch ein Three.js-Port verfügbar.

Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.