Unter der Haube von Graveyard Keeper: Wie Grafikeffekte implementiert werden

    Hallo allerseits! Ich habe die ganzen 4 Jahre nicht in Habr geschrieben. In meiner letzten Reihe von Beiträgen ging es um verschiedene Tools und Techniken, die wir in unserem letzten Spiel verwendet haben (Entwicklung in Unity). Seitdem haben wir das Spiel erfolgreich veröffentlicht und auch ein neues veröffentlicht. Jetzt können Sie ein wenig ausatmen und einige neue Artikel schreiben, die für jemanden nützlich sein können.


    Heute möchte ich über die grafischen Tricks und Tricks sprechen, die wir verwendet haben, um das Bild zu erstellen, das Sie in der obigen GIF sehen.

    Wir reagieren sehr empfindlich auf die Grafik unserer Spiele und haben daher viel Zeit und Mühe in verschiedene Effekte und andere Brötchen investiert, die unsere Pixelkunst so attraktiv wie möglich machen würden. Vielleicht findet jemand etwas Nützliches für sich.

    Zunächst werde ich kurz auflisten, wie das Bild in unserem Spiel aussehen wird:

    1. Variables Umgebungslicht - eine banale Änderung der Beleuchtung in Abhängigkeit von der Tageszeit.
    2. LUT-Farbkorrektur - ist für die Änderung des Bildtons abhängig von der Tageszeit (oder der Art der Zone) verantwortlich.
    3. Dynamische Lichtquellen - Fackeln, Öfen, Lampen.
    4. Normale Karten - sind für die Lautstärkeregelung von Objekten verantwortlich, insbesondere beim Bewegen von Lichtquellen.
    5. 3D-Lichtverteilungsmathematik - ist dafür verantwortlich, dass die Lichtquelle in der Mitte des Bildschirms ein höheres Objekt korrekt beleuchtet, ein niedrigeres Objekt jedoch nicht (d. H. Auf die Kamera mit der nicht beleuchteten Seite gerichtet).
    6. Schatten - von Sprites erzeugt, drehen sich und reagieren auf die Position der Lichtquellen.
    7. Simulation der Höhe von Objekten - für die korrekte Darstellung von Nebel.
    8. Andere Dekorationen: Regen, Wind, Animationen (einschließlich Shader-Animation von Laub und Gras) usw.

    Jetzt genauer.

    Variables Umgebungslicht


    Hier im Prinzip nichts Besonderes. Nachts dunkler, tagsüber heller. Die Farbe des Lichts wird durch den Zeitverlauf bestimmt. Bei Nacht wird die Lichtquelle nicht nur dunkler, sondern erhält einen blauen Farbton.

    Es sieht so aus:


    LUT-Farbkorrektur


    LUT (Nachschlagetabelle) - Farbersetzungstabellen. Grob gesagt handelt es sich um ein dreidimensionales RGB-Array, bei dem in jedem Knoten ein Farbwert vorhanden ist, der durch den entsprechenden ersetzt werden sollte. Das heißt, wenn sich an den Koordinaten (1, 1, 1) ein roter Punkt befindet, bedeutet dies, dass die gesamte weiße Farbe im Bild durch rot ersetzt wird. Wenn die Koordinaten (1, 1, 1) weiß sind (R = 1, G = 1, B = 1), gibt es keine Änderung. Dementsprechend hat die LUT ohne Änderungen eine Farbe für jede Koordinate, die diesen gleichen Koordinaten entspricht. Das heißt am Punkt (0,4, 0,5, 0,8) ist die Farbe (R = 0,4, G = 0,5, B = 0,8).

    Nun, es ist erwähnenswert, dass sie der Einfachheit halber eine 3D-Textur als zweidimensional präsentieren. Zum Beispiel sieht die „Standard“ -LUT so aus (ändert in keiner Weise die Farbwiedergabe):



    Sie ist einfach implementiert und funktioniert schnell und bequem.

    Es ist auch sehr einfach einzurichten - Sie geben dem Künstler ein Bild aus dem Spiel und sagen "Farbton, so dass es sich wie Abend anfühlt." Wenden Sie danach alle Farbkorrekturebenen auf die Standard-LUT an und erhalten Sie die Abend-LUT.

    In unserem Fall blieb der Künstler ein wenig hängen und erstellte bis zu 10 verschiedene LUTs für verschiedene Tageszeiten (Nacht, Dämmerung, Abend usw.). So sieht ihr Setup aus:


    Infolgedessen sieht derselbe Ort je nach Tageszeit unterschiedlich aus:



    Hier ändert sich auch die Transparenz von Lichtsprites aus Fenstern in Abhängigkeit von der Tageszeit.

    Dynamische Lichtquellen und normale Karten


    Lichtquellen werden absolut gewöhnlich von Unity verwendet. Außerdem werden für jedes Sprite normale Karten gezeichnet, sodass Sie ein Gefühl für die Lautstärke erhalten.


    Solche Normalen werden ganz einfach gezeichnet. Ein Künstler malt grob ein Licht von 4 Seiten:


    Und dann geht dieses Skript zur normalen Map:


    Wenn Sie nach einem Shader (und einer Software) suchen, der dies ermöglicht, können Sie in Richtung Sprite Lamp schauen.

    3D-Lichtsimulation


    Das ist etwas komplizierter. Sie können Sprites nicht nur aufnehmen und markieren. Wir müssen überlegen, ob sich das Sprite „hinter“ der Lichtquelle oder „vor“ befindet.

    Achten Sie auf dieses Bild:


    Beide Bäume befinden sich im gleichen Abstand von der Lichtquelle, der entfernte Baum wird jedoch beleuchtet und der nächste Baum nicht (da sein nicht beleuchteter Teil zur Kamera gerichtet ist).

    Ich habe dieses Problem ganz einfach gelöst. Der Shader berechnet den Abstand entlang der vertikalen y-Achse zwischen der Lichtquelle und dem Sprite. Und wenn es positiv ist (die Lichtquelle vor dem Sprite), dann beleuchten wir das Sprite wie gewohnt, aber wenn es negativ ist (das Sprite überlappt die Lichtquelle), nimmt die Lichtintensität aus einer Entfernung mit einem sehr großen Koeffizienten sehr stark ab. Der Koeffizient wird festgelegt und nicht nur „nicht beleuchtet“, sodass das Sprite nicht sofort, sondern allmählich schwarz wird, wenn sich die Lichtquelle bewegt und plötzlich hinter dem Sprite erscheint. Aber immer noch ziemlich schnell.


    Schatten


    Schatten entstehen durch Sprites, die einen Punkt umkreisen. Ich habe versucht, mehr Komprimierung (Skew) hinzuzufügen, aber es stellte sich als unnötig heraus.

    Insgesamt kann jedes Objekt maximal 4 Schatten haben. Eine stammt von der Sonne und drei von dynamischen Lichtquellen. Das Prinzip unten ist im Bild unten dargestellt: Die



    Aufgabe „Finden der nächsten 3 Lichtquellen und Berechnen des Abstands / Winkels der Schatten zu ihnen“ wird durch ein Skript gelöst, das sich in Update dreht. Ja, es funktioniert nicht sehr schnell, weil du musst viel rechnen. Wenn ich jetzt schreiben würde, würde ich die neuen Systeme paralleler Jobs in Unity verwenden. Aber das war noch nicht der Fall, also habe ich normale Skripte so weit wie möglich optimiert.

    Das einzige, was zählt, ist, dass ich die Sprite-Rotation nicht transformiert habe, sondern im Vertex-Shader. Das heißt Drehung bewegt sich nicht. Es ist nur so, dass im Sprite ein Parameter festgelegt wird (ich habe die Farbe dafür verwendet, da alle Schatten gleich sind) und der Shader für die Drehung des Sprites verantwortlich ist. Das geht schneller weil In Unity muss Geometrie nicht gezerrt werden.

    Ein weiteres Minus dieses Ansatzes ist, dass die Schatten für jedes Objekt einzeln erstellt (und manchmal gemalt) werden müssen. Wahrscheinlich kosten wir Dutzende verschiedener mehr oder weniger universeller Sprites (dünn, dick, oval usw.).

    Der zweite Nachteil ist, dass es manchmal schwierig ist, einen Schatten für ein Objekt zu erzeugen, dessen Kontaktpunkt mit der Erde sehr langgestreckt ist. Schauen Sie sich zum Beispiel den Schatten vom Zaun an:


    Nicht perfekt So sieht es aus, wenn Sie das Sprite des Zauns selbst durchscheinend machen:


    Hierbei ist jedoch zu beachten, dass das Sprite vertikal immer noch stark deformiert ist (das ursprüngliche Schatten-Sprite sieht fast wie ein Kreis aus). Das ist der Grund, warum sein Spielzug nicht als Spielzug, sondern als Verzerrung erscheint.

    Nebel- und Höhensimulationen


    Es ist Nebel im Spiel. Es sieht so aus (oben ist die normale Version, unten ist ein extremer Nebel von 100%, um den Effekt zu demonstrieren).



    Wie Sie sehen können, ragen die Oberseiten der Häuser und Bäume aus dem Nebel heraus. Tatsächlich war es ziemlich einfach, diesen Effekt zu erzielen. Der Nebel besteht aus vielen horizontalen Wolken, die über die Bühnentiefe verteilt sind. Als Ergebnis stellt sich heraus, dass der obere Teil aller Sprites von weniger Nebel-Sprites blockiert wird:



    Der wind


    Pixel Art Wind ist eine andere Geschichte. Es gibt nicht viele Möglichkeiten. Entweder mit den Händen animieren (was mit unserer Menge an Kunst fast unmöglich ist) oder einen deformierenden Shader schreiben, aber dann muss man manchmal hässliche Verzerrungen ertragen. Sie können natürlich überhaupt nicht animieren, aber dann sieht das Bild leblos aus.

    Wir haben die Verzerrungsoption mit dem Shader ausgewählt. Es sieht so aus:


    Wenn Sie diesen Shader auf eine karierte Textur anwenden, wird klar, was passiert:


    Es ist auch erwähnenswert, dass wir nicht die gesamte Krone animieren, sondern nur einzelne Blätter:


    Wir schwingen auch Weizen im Wind, aber alles ist einfach - der Vertex-Shader verformt die x-Koordinaten unter Berücksichtigung der y-Komponente. Je höher der Punkt, desto stärker die Staffelung. Dies geschieht so, dass nur der obere Teil taumelt, der untere jedoch nicht. Plus - Die Wobbelphase ändert sich von den x / y-Koordinaten, sodass verschiedene Sprites auf dem Bildschirm zufällig schwingen.


    Derselbe Shader wird auch verwendet, um den Effekt des Schwingens von Weizen und Gras zu erzeugen, wenn ein Spieler durch sie hindurchgeht.


    Das ist wahrscheinlich alles für jetzt. Ich habe mich bewusst nicht mit der Konstruktion der Szene und ihrer Geometrie befasst, weil Dies ist Material für einen separaten Artikel. Im Übrigen sprach er über die Hauptlösungen, die in der Entwicklung verwendet wurden.

    PS: Wenn jemand an technischen Aspekten interessiert ist, schreibe in die Kommentare. Vielleicht werde ich in einem separaten Artikel erzählen. Es sei denn natürlich notwendig.

    PPS: Ich nutze diese Gelegenheit, um zu sagen, dass wir jetzt mehrere kompetente Leute im Team finden wollen (Programmierer, PM, KM, Künstler). Details finden Sie auf der Studio-Website. Ich hoffe, dieser Satz hat nicht gegen die Regeln verstoßen.

    Jetzt auch beliebt: