Die Geschichte einer Animation

Einmal rief der Designer das Frontend an und bat darum, ein "Spinnennetz" hinter dem beschlagenen Glas zu machen. Aber dann stellte sich heraus, dass dies kein „Spinnennetz“ war, sondern ein sechseckiges Gitter und nicht hinter dem Glas, sondern es ging in die Ferne, und der Frontendendender war mit WebGL nicht vertraut, und ich musste die gesamte Animation beim Zeichnen lernen. Das Frontend war Yuri Artyukh ( akella ).



Yuri beschäftigt sich seit langer Zeit mit dem Satz und zeichnet sonntags Streams mit Analysen realer Projekte auf. Er ist kein Profi in WebGL, erstellt keine Karten darauf, schreibt nicht in Web Assembler, lernt aber gerne etwas Neues. Auf der FrontendConf RIT ++ erklärte Yuri, wie eine Animation vom Layout bis zur Auslieferung an den Client ausgeführt wird, damit alle zufrieden sind und dabei WebGL lernen. Die Geschichte stammt aus der Perspektive der ersten Person und beinhaltet: Three.js, GLSL, Canvas 2D, Grafiken und ein bisschen Mathematik.


Spinnennetz hinter Nebelglas


Irgendwie saß ich da und arbeitete an einem wichtigen Projekt. Dann ruft der Designer aus dem Studio, in dem Spezialeffekte sehr beliebt sind, an und fragt: „Kannst du ein Spinnennetz machen, als ob hinter beschlagenem Glas?“

Dies beschreibt natürlich sofort das gesamte Problem. Wie sich später herausstellte, war dies das "Spinnennetz" hinter dem beschlagenen Glas.



Dies ist ein sechseckiges Gitter, aber für den Designer aus irgendeinem Grund ein "Spinnennetz". Nebelglas - dieses Gitter geht in die Ferne. Kommunikationsschwierigkeiten. Stellen Sie sich vor, wie schwer es ist, introvertiert zu sein und Animationen zu machen? Aber ich bin einfach so und das ist was ich tue.

Dieses "Web" sieht nicht wie eine Animation aus. Danach können Sie einen Bericht über einen erfolgreichen Fall schreiben, ein Startup eröffnen, eine Milliarde Investitionen tätigen, eine Menge Fans haben und eine Rakete ins All starten. Worum geht es hier? Eine braune Linie auf einem weißgrauen Hintergrund, als würde sie von einer Maus gezeichnet. Später stellte sich heraus, dass sie an den Rändern entlang gehen sollte, aber dazu später mehr. Im Allgemeinen lautet der Codename "das Netz hinter dem beschlagenen Glas".

Auf der Site mit dieser Animation gab es mehrere weitere Optionen für das „Spinnennetz“: auf einem grauen Hintergrund oben, auf einem weißen unten. Es musste interaktiv gemacht werden, damit es auf die Bewegung der Maus des Benutzers reagierte.

Das erste, was mich Designer fragten, war, wie schwierig es ist und wie viel es kosten wird. Ein paar Gedanken gingen mir durch den Kopf: wie man eine Linie und ein solches Gitter zeichnet, wie man es nicht langsamer macht, wie es überhaupt funktionieren sollte. Ich habe das noch nie erlebt. Aber als Person, die sich mit Entwicklung beschäftigt, antwortete er: " Das ist einfach, wir werden es tun ..."

Ich mag es, mich auf seltsame Abenteuer einzulassen, denn wenn ich das tue, leide ich normalerweise.

Durch Leiden kommt Wachstum. Es ist unweigerlich mit Leiden verbunden - man kann nicht mit allem glücklich sein, glücklich leben und sich gleichzeitig professionell entwickeln.

Three.js


Ich begann sofort zu überlegen, wie ich das Problem lösen könnte. Da alles in 3D war, erinnerte ich mich an Three.js . Dies ist die beliebteste Bibliothek, über die auf allen Konferenzen gesprochen wurde. Diese Bibliothek macht WebGL einfacher, bequemer und unterhaltsamer als nur natives WebGL.

Three.js hat viele fertige Objekte. PlaneGeometry ist das erste Objekt, das mir perfekt erschien. Dies ist eine primitive Ebene. Die Bibliothek hat alle Arten von Sechsecken, Dodekaedern, Ikosaedern, Zylindern, aber es gibt eine einfache Ebene aus vielen Dreiecken.


Es gibt viele Dreiecke, weil ich detaillierte Wellen brauche - die Oberfläche muss sich Sorgen machen.

Wenn Sie in Three.js schauen, ist diese Ebene tatsächlich ein einfaches JS-Objekt mit einer Liste aller Koordinaten der Punkte.



In meinem Fall habe ich eine 50 × 50-Quadratebene, also brauchte ich 2601 Eckpunkte. Warum 50 × 50 = 2601? Das ist Schulmathematik. Die Koordinate z = 0, weil die Ebene y = 1 ist, weil dies die erste Reihe von Eckpunkten mit 50 Teilen ist und x sich ändert.

Aber warum brauche ich ein Flugzeug, muss ich es irgendwie biegen? Das erste, was Sie mit einem Array tun können, ist, mathematische Operationen daran auszuführen. Gehen Sie beispielsweise for each Schleife durch und weisen Sie die Z-Koordinate dem Sinuswert der x-Koordinate zu.



Es stellte sich heraus, dass es einer Sinuswelle ähnlich ist, da der Höhenwert jedes Scheitelpunkts gleich dem Sinus dieses Werts entlang der x-Achse ist. Um dies irgendwie zu komplizieren (jetzt wird es ein schwieriger mathematischer Moment, machen Sie sich bereit) - Ich werde dem Sinus Zeit hinzufügen, und diese Leinwand wird sich bewegen, einfach weil die Mathematik so funktioniert. Wenn Sie der x-Koordinate Zeit hinzufügen, wird das Diagramm horizontal verschoben. Wenn y koordiniert werden soll - wird vertikal verschoben. Ich war an horizontaler Bewegung interessiert - ich wollte, dass mein Ozean sich Sorgen macht.

Der Designer hatte nicht erwartet, dass ich eine Sinuskurve habe, die von links nach rechts kriecht. Er wollte, dass es schön war, wie ein Spinnennetz, ein Ozean oder was auch immer in seinem Kopf war. Daher passte die Option mit einer Sinuskurve nicht. Es brauchte eine Art Zufälligkeit. Das Flugzeug sollte nicht als Sinuswelle vorhersehbar sein. Wenn Sie jedoch für jeden dieser Peaks einen Zufall nennen, erhalten wir die Unvorhersehbarkeit.

Das Wesen der Zufälligkeit ist, dass sie zufällig und unabhängig ist. Jeder zufällige Scheitelpunkt hängt in keiner Weise von seinen Nachbarn ab. Zufällig werden keine Parameter übergeben, Nachbarn sind ihm egal.



Das Ergebnis ist eine gebrochene Kurve, die nur aus der Ferne dem Ozean oder dem Netz ähnelt. Eher als Illustration für einen Film über "Hacker" und Cyber-Einbrüche geeignet.

Wenn Sie aus der Sicht jedes Punktes auf dem Bildschirm zufällig schauen, sieht es aus wie weißes Rauschen - viele kleine weiße und schwarze Punkte. Jeder Punkt ist schwarz und weiß - 0 oder 1.

Aber was ich brauche, um einen Ozean von Wellen zu erschaffen, sollte so aussehen.


Es ähnelt Nebel und Wolken und Bergen.

Es gibt zufällige Funktionen, die solche Bilder zurückgeben. Sie werden als Geräusche oder Geräusche bezeichnet : Simplex-Geräusche, Perlin-Geräusche. Perlin im Namen des Rauschens ist der Name des Erstellers des Gradientenrauschalgorithmus, der einen schönen Zufall zurückgibt. Er schuf es, indem er an den Spezialeffekten des ersten Teils des Films "Tron" arbeitete. Dieser mathematische Algorithmus gab es früher, aber jetzt wird er aktiv in Filmen und Spielen verwendet.

Wenn zufällige Karten in "Heroes of Might and Magic III" (für Personen über 30) oder in Strategien erstellt werden, sehen Sie normalerweise etwas Ähnliches. Es ist immer dieselbe Funktion, die diese Geräusche zurückgibt.

Es gibt eine ganze Bewegung "Generative Kunst". Die Teilnehmer erzeugen mithilfe der Rauschfunktion Kunstwerke und Landschaften. Zum Beispiel im Bild unten eine pseudo-natürliche Landschaft von einem der Künstler. Es ist nicht sofort klar, ob es sich um Mathematik oder die Topographie eines Berges handelt. Die Aufgabe der generativen Kunst besteht genau darin, mathematisch eine Landschaft zu erzeugen, die von der Gegenwart nicht zu unterscheiden ist.



Es stellte sich heraus, dass diese Funktion im Gegensatz zur Zufälligkeit Parameter berücksichtigt, da die Punkte von den Nachbarn abhängen müssen, die ihnen am nächsten liegen. Wenn Sie die Rauschfunktion anstelle der üblichen Zufälligkeit verwenden, erhalten Sie so etwas.


Schwarz und Weiß ist einfach Höhe: 0 sind schwarze Täler, 1 sind weiße Gipfel. Es dreht sich eine wellige Oberfläche.

Diese Funktion ist auf allen PLs verfügbar, da es sich nur um einen Algorithmus handelt - Sinus, Cosinus, Multiplikation.

Ich kann die Verzerrung auf die gleiche Weise durchführen, indem ich alle Scheitelpunkte meines PlaneGeometry-Objekts durchlaufe und jeden Wert der Rauschfunktion zuweise:

 geometry.vertices.forEach(v => { vz = noise(vx, vy, time); }); 

Die Funktion nimmt nur 30-40 Zeilen ein, ist jedoch mathematisch komplex.

Es gibt Rauschfunktionen aller Dimensionen: eindimensional, zweidimensional, dreidimensional. In meinem Fall ist dies dreidimensionales Rauschen, da drei Parameter an es übergeben werden. Zusätzlich zu den Raumkoordinaten der x- und y-Ebene übertrage ich die Zeit - die Oberfläche wird sich ständig verdrehen und ihre Position ändern.

Three.js! == GPU


Als ich den Algorithmus startete, begannen sich die Wellen zu bewegen. Wenn ich etwas für das Web mache, schaue ich immer in den Profiler und jetzt habe ich mich auch damit befasst. So sahen die Wellen dort aus.



Auf dem Bildschirm wird ein vom Browser gezeichnetes Bild angezeigt. Rahmen werden durch vertikale graue gestrichelte Linien angezeigt. Innerhalb des Rahmens wird 2/3 der Zeit von der Ausführung der Rauschfunktion belegt. Wenn Sie etwas im Web animieren, verwenden Sie den Anforderungsanimationsrahmen, der bestenfalls alle 16 ms ausgeführt wird. Der Rahmen alle 16 ms zählt die Rauschfunktion für 2600 Eckpunkte. Für jeden Scheitelpunkt werden eine Auf-Ab-Bewegung und eine Höhe berücksichtigt. Bei jedem nachfolgenden Frame werden die Werte neu berechnet, da die Oberfläche rechtzeitig leben muss.

Es stellte sich heraus, dass die Rauschfunktion, die 2600 Mal gedreht wurde, bereits 2/3 des Frames auf meinem Computer einnimmt. Und das ist nicht der ganze Rahmen. Bei der Entwicklung von Animationen ist dies bereits eine rote Fahne.

Keine Animationen sollten mehr als die Hälfte des Frames einnehmen.

Wenn mehr, besteht ein hohes Risiko, dass der Frame während einer Interaktion, einer Schaltfläche oder eines Mouseovers verloren geht.

Daher war es eine harte rote Fahne. Mir wurde klar, dass Three.js nicht unbedingt WebGL ist. Trotz der Tatsache, dass ich Three.js zu verwenden schien, habe ich alles in 3D gezeichnet, es wurde in WebGL gerendert, ich habe keine fantastische Leistung von WebGL erhalten. Ich habe nur 2.600 Eckpunkte - das reicht WebGL nicht. Zum Beispiel gibt es auf jeder Karte Tausende von Objekten, die jeweils aus Dutzenden von Dreiecken bestehen. Schätzen Sie die Skala: Hunderttausende sind normal, aber es gibt nur 2600 Spitzen.

Vertex Shader


Nach einem Problem mit Frames stellte ich fest, dass es Shader gibt. Es gibt nur zwei Arten von ihnen:

  • Vertex Shader;
  • Fragment Shader.

Ich interessierte mich für den Vertex Shader - Vertex Shader. Wenn wir die Animation darauf umschreiben, sieht es so aus:

 position.z = noise( vec3(position.x, position.y, time) ); 

Position.z - Z-Koordinaten der Komponente jedes Punkts mit eigenen Datentypen. vec3 gibt an, dass es drei Parameter geben wird.

Es gibt keine Schleife im Shader.

Vorher habe ich im Skript for each Schleife eine eingefügt, und für jeden Scheitelpunkt fanden die Berechnungen in einer Schleife statt. Der Unterschied zwischen Shadern und Nicht-Shadern ist das Fehlen eines Zyklus.

Shader - das ist der Zyklus.

Es wird für alle Eckpunkte gleichzeitig parallel ausgeführt. Dies ist seine Hauptbedeutung und Mission und Chip und Mission.

Die GPU auf der Grafikkarte hat im Gegensatz zur Hauptprozessor-CPU einen größeren Kern. Auf dem Prozessor gibt es viel weniger davon, aber er kann universelle Berechnungen schneller durchführen. Auf der Grafikkarte sind sehr einfache Berechnungen verfügbar, es gibt jedoch viele Kerne, sodass Sie viele Berechnungen parallelisieren können. Dies ist, was normalerweise in Shadern passiert. Die Bedeutung des Vertex-Shaders ist, dass das Rauschen für 2600 Vertices im Shader auf der Grafikkarte parallel berechnet wird.

Wenn Sie sich den Profiler ansehen, ändert sich das Erscheinungsbild der Animation nicht, sieht aber so aus.



Auf der CPU wird überhaupt nichts ausgeführt . Natürlich wurde unten ein weiterer Thread auf der GPU hinzugefügt. Es gibt auch Threads auf der GPU, der CPU und den Web-Workern, aber diese Berechnungen werden bereits in einem separaten Thread auf der Grafikkarte ausgeführt.

Das ist natürlich nicht kostenlos. Die Grafikkarte bei der Arbeit wird stärker erwärmt als der Hauptprozessor. Wenn Sie Websites mit ähnlichen Animationen besuchen, arbeiten Fans häufig für Sie. Dies liegt daran, dass die Grafikkarte beim Einschalten im Gegensatz zum Rest der Zeit gekühlt werden muss. Auf Mobilgeräten ist dies wichtiger als auf Desktop-Computern - Mobiltelefone gehen einfach schneller zur Neige. Gleichzeitig erhalten Sie einen ziemlich radikalen Leistungsgewinn.


Das Ergebnis ist eine solche Oberfläche - dies ist das übliche Perlin-Rauschen. Wenn Sie es starten und nur die Zeit ändern, werden kühle Wellen erhalten.

Das ist aber noch nicht alles. Ich musste immer noch "Spinnennetz" - ein sechseckiges Gitter auf der Oberfläche. Mit Erfahrung im Layout ist es am einfachsten und naheliegendsten, ein Fragment auszuwählen, das wiederholt werden kann. Interessanterweise ist ein sechseckiges Gitter nicht quadratisch, sondern rechteckig. Wenn Sie das Muster als Rechteck wiederholen, erhalten Sie ein Raster. Mit der Three.js-Bibliothek können Sie PNG überlagern und vorher nicht das gesamte WebGL lernen. Ich habe PNG ausgeschnitten und auf die Oberfläche gelegt, es stellte sich so etwas heraus.



Auf den ersten Blick, was Sie brauchen! Aber nur zuerst. Dies passte nicht zu mir, da die Animation für die Kryptowährungs-Site erforderlich war - alles sollte „teuer, reich“ sein.

Wenn Sie PNG-Texturen verwenden und diese sich in der Nähe der Kamera befinden, können Sie sehen, dass die Kanten des nächsten Elements verschwommen sind. Es gibt kein Gefühl, dass das Bild klar ist. Das PNG scheint sich im Browser ausgestreckt zu haben. Das Problem ist, dass es in WebGL keine Möglichkeit gibt, Vektortexturen im vollen Sinne des Wortes zu verwenden. Also habe ich geweint und dann im Internet gelesen, dass GLSL dieses Problem löst.

GLSL ist eine C-ähnliche Sprache, in der Shader geschrieben sind. Jeder hat Angst, es zu benutzen, denn das sind Shader, WebGL - nichts ist klar! Aber ich fand heraus, dass es möglich ist, klare Bilder darauf zu machen, und wandte mich dem zweiten Shader-Typ zu.

Fragment Shader


Dieser Shader macht dasselbe wie der Vertex. Wenn der Scheitelpunkt jedoch eine Polylinienoberfläche erstellt und Berechnungen für jeden Scheitelpunkt durchführt, berechnet der Fragment-Shader die Farbe für jedes Pixel der Oberfläche.

Die grundlegendste fragment shader – step(a,b) Funktion fragment shader – step(a,b) . Es werden nur 0 und 1 zurückgegeben:

  • wenn a> b, dann 0;
  • wenn a <b, dann 1.

Ich habe eine Pseudo-Implementierung in JS durchgeführt, um zu verdeutlichen, wie einfach diese Funktion ist.

 function step(a, b) { if (a < b) return 0 else return 1 } 

Wenn Sie in WebGL arbeiten, gibt es normalerweise für jedes Objekt ein Koordinatensystem. Wenn es sich um ein quadratisches Objekt handelt, ist das Koordinatensystem primitiv: Punkte (0,0), (0,1), (1,0), (1,1).

Für jedes Pixel wird ein Fragment Shader ausgeführt. Wenn Vertex Shader 2600 Mal pro Frame ist, wird Fragment Shader so oft ausgeführt, wie Pixel vorhanden sind. Vielleicht eine Million Mal für jeden Frame, wenn die Oberfläche 1000 × 1000 px beträgt. Es klingt beängstigend, aber einfach, weil nur wenige Menschen mit den Ressourcen von Grafikkarten in unserer Zeit vertraut sind.

Wenn Sie die Schrittfunktion (a, b) mit den Koordinaten dieser Pixel verwenden, können Sie die Schrittfunktion mit dem Parameter 0.4 ausführen und die x-Koordinate jedes Pixels an jeden Punkt übergeben.

Es stellt sich heraus, dass alles, was kleiner als 0,4 ist, 0 ist, alles, was größer als 1 ist. In WebGL sind Zahlen und Farben ein und dasselbe. Jede Farbe ist eine Zahl. Weiß - 1, Schwarz - 0. Es gibt drei davon in RGB, aber es ist immer noch 0.0.0 und 1.1.1.



Wenn wir diesen Funktionsschritt komplizierter ausführen, wird links weiß. Diese Funktion wird für jeden Punkt auf dem Bildschirm ausgeführt und berücksichtigt, dass sie entweder 0 oder 1 ist. Dies ist normal. Machen Sie sich darüber keine Sorgen.

Wenn Sie diese beiden Ausdrücke multiplizieren, erhalten Sie einen vertikalen weißen Streifen. Wenn Sie dasselbe auf einer anderen Achse tun, können Sie ein weißes Quadrat zeichnen:


Es sollte der Höhepunkt sein - wir haben ein weißes Quadrat gezeichnet!

Mit Kombinationen von nur einer Funktion können Sie alles zeichnen, was Sie wollen.

Wenn Sie sich erinnern, waren die Elemente des Musters in einem Winkel. Wenn Sie eine geneigte Oberfläche erstellen, die nur aus schwarzen und weißen Farben besteht, wird sie gerippt und nicht geglättet. Rippen fällt auf - es ist hässlich. Damit die Oberfläche für das Auge glatt aussieht, werden nicht nur schwarze und weiße Pixel benötigt, sondern auch graue Halbtöne.

Reibungsloser Schritt


Shader haben eine Smoothstep-Funktion. Es macht dasselbe wie Schritt, interpoliert jedoch zwischen 0 und 1, so dass es einen Gradienten gibt.


Von links nach rechts nach maximaler Komprimierung.

Wenn Sie diese Funktion so weit wie möglich komprimieren, erhalten Sie eine Linie mit minimalem Verlauf. Dies ist genau das, was Sie benötigen, um in jedem Winkel des Fragment-Shaders eine perfekt glatte Linie zu erzeugen.

So konnte ich ein weißes Quadrat mit glatten Kanten machen. Wenn es ein weißes Quadrat gibt, können Sie 3 weiße Quadrate erstellen.


Quadrate können gedreht werden, verwenden Sie die Funktionen von Sinus und Cosinus.

Dann musste ich Papier und ein Blatt verwenden, um mein Muster zu brechen.


Screenshot aus der Produktion.

Dort ist alles mit 23 verschiedenen Multiplizitätsgraden verbunden, so dass es nicht sehr schwierig war, die Koordinaten all dieser Punkte zu berechnen. Und dann können Sie ein solches Muster erhalten.



Ich habe ein Fragment gezeichnet und es viele Male wiederholt. Sie können den Debug-Modus deutlich sehen, in dem sich das Muster wiederholt. Alle Fragmente werden mit dem parametrischen Funktionsschritt und dem smoothstep . Dies bedeutet, dass Sie durch Erstellen eines Musters zum Kippen der Ebene eine unendliche Anzahl dieser Muster erzeugen können. Wenn wir innerhalb des Fragments die Linienstärke oder die Größe der Sechsecke ändern, erhalten wir viele andere Muster.

Ich habe die Parameter verdreht und unendlich viele Muster gefunden. Es ist wie "Generative Kunst" - es ist nicht klar, was getan wurde, aber wunderschön.

SDF


Dann fand ich heraus, dass es auch signierte Entfernungsfelder gibt, die Bilder mit einer Entfernungskarte erzeugen . SDF wird in Karten oder in Computerspielen zum Zeichnen von Texten und Objekten verwendet, da es optimal ist. In WebGL ist es schwierig, Text auf andere Weise zu zeichnen, insbesondere glatt und umrissen.



Dies ist ein mathematisches Format, das außerhalb von WebGL nur schwer zu verwenden ist. Die Idee ist einfach, aber elegant und ergibt einen schönen Effekt.

Wenn wir einen klaren Stern zeichnen wollen, müssen wir dafür das Bild rechts speichern - dies ist das erzeugte Bild des Bildes. Es gibt bereits einen Algorithmus, der jedes klare Bild in ein verschwommenes verwandelt. Danach kann damit eine klare Version erstellt werden, aber gleichzeitig erhalten wir nicht ein Bild, sondern viele. Aus einem klaren Bild derselben Größe können Sie dasselbe, aber größere Bild erstellen. Es wird mit Fehlern sein, aber der Ansatz ist mathematisch interessant.

Wenn Sie beispielsweise ein Bild mit einer Größe von 128 × 128 px aufnehmen, erhalten Sie aus einem Bild in kleiner Größe ein klares Bild, das um ein Vielfaches größer als die Quelle ist. Dies ist einer der Gründe, warum sie SDF verwenden - eine verschwommene Schrift wiegt oft weniger als in einem optimierten Vektorformat.

Natürlich gibt es eine Einschränkung. Es ist unmöglich, die Buchstaben auf 1000 Pixel zu erhöhen, selbst 100 Pixel sehen hässlich aus. Aber wie oft werden Schriftarten dieser Größe benötigt?

Fragment-Shader, Zeichnen von Rechtecken, Ausbreiten - Mit Hilfe dieser Störungen gelang es mir schließlich, die gewünschte Oberfläche zu finden.



Neue Bedingungen


Sie war so, wie es sein sollte: Zappelnd waren alle Elemente klar. Alles war wie ich wollte, aber es stellte sich heraus, dass mehr dahinter steckte:

- Und lass ihn sich mit der Maus bewegen und ein neuer Weg wird gelegt. Und die Waben sind hervorgehoben!

Es wurde angenommen, dass der Benutzer, wenn er die Maus bewegt, seinen dornigen Pfad mithilfe des Dienstes metaphorisch entlang des zerbrochenen "Spinnennetzes" ebnet.



Es ist nicht schwer, die Aufgabe in Worten zu beschreiben, aber wie kann man sie umsetzen? Das erste, was ich dachte - da ich ein sechseckiges Gitter habe, wurde es wahrscheinlich bereits untersucht. Dann stieß ich auf einen interessanten Artikel „ Hexagonal Grid Reference and Implementation Guide “. Darin sammelte der Autor 20 Jahre lang Materialien. Er ist ohne Frage cool und der Artikel ist göttlich für diejenigen, die Algorithmen und Mathematik mögen. Es enthält viele interessante Daten zu hexagonalen Netzen.

Der Artikel ist lang, enthält aber interessante mathematische Ansätze: Wie man ein Koordinatensystem auf einem hexagonalen Gitter konstruiert, wie man diese Sechsecke nummeriert und dann auf sie verweist, wo es verwendet wird. Es stellt sich heraus, dass dies immer vor meinen Augen war, weil in alten Computerspielen überall sechseckige Netze verwendet werden.


Wenn Sie bereits auf einen sechseckigen Bund eingestellt sind, schauen Sie sich die Burg an. Bei anderen Texturen wird ebenfalls ein sechseckiges Gitter erraten.


In "Civilization" ist im Allgemeinen alles offensichtlich.

Es war auch interessant zu wissen, dass, wenn Sie einen Querschnitt entlang der Diagonale eines dreidimensionalen Würfels machen, der aus vielen kleinen Würfeln besteht, dies einerseits Würfel und andererseits regelmäßige Sechsecke sind.



Der Querschnitt eines dreidimensionalen Würfels ergibt ein zweidimensionales hexagonales Gitter. Es hat Spaß gemacht zu lernen, dass dreidimensionale Würfel irgendwie mit zweidimensionalen Sechsecken verbunden sind.

In dem Artikel gab es auch einen Algorithmus zum Finden des Pfades entlang des hexagonalen Gitters. Ich musste nach einem Weg suchen, um mit der Maus Höhe zu erreichen.

Pfadsuchalgorithmen sind komplex und einfach. Am primitivsten ist es, eine Linie zwischen den Punkten zu ziehen und zu sehen, in welche Sechsecke diese Linie fällt. Es stellt sich also heraus, dass früher Einheiten von Punkt A nach Punkt B gingen.


Ich brauchte so etwas.

Aber das brauche ich nicht. Hier ist der Weg entlang der Bereiche der Sechsecke gelegt, und ich brauche entlang der Kanten. Ich musste das Problem anders lösen.

Canvas2d


Vielleicht gibt es bessere Wege, aber meiner ist interessanter. Zuerst habe ich Canvas2D für mein Debug gezeichnet - Schritt 1.



Davor gab es WebGL, Three.js, Shader, und das ist nur Canvas2D! Ich habe alle Punkte des Hex-Gitters darauf gezeichnet. Wenn Sie genau hinschauen, sind dies die gleichen Sechsecke. Dann erinnerte ich mich an die Diagramme, in denen Informationen darüber gespeichert sind, wie Punkte miteinander verbunden sind, und verband jeden Punkt mit drei benachbarten und erhielt ein Diagramm - Schritt Nummer 2. Hierfür verwendete ich Open Source Beautiful Graphs .

Ein Diagramm ist einfach eine Sammlung von Punkten und Informationen darüber, wie sie verbunden sind. Sie sind gut untersucht, es gibt viele gute Algorithmen für jeden Geschmack, um den Weg von Punkt A nach Punkt B innerhalb des Diagramms zu finden. Alle Karten verwenden diese Art von Algorithmen.

Es sieht ungefähr so ​​aus.

 graph = createGraph( ); graph.addNode(..); // 1000 nodes graph.addLink(..); // 3000 links graph.pathFinder(Start, Finish); //0.01s 

Wir erstellen ein Diagramm, fügen 1000 Punkte und alle Verbindungen zwischen ihnen hinzu. Als nächstes übergeben wir die id jedes Punkts - der erste ist mit dem dritten verbunden, der dritte mit dem fünften. Sie müssen nichts erfinden, es gibt einen optimierten Algorithmus mit einer vorgefertigten Funktion, mit der Sie diesen Pfad finden können.

Dieser Algorithmus läuft weniger als ein Frame. Natürlich benötigt es eine Art Ressource, aber Sie müssen sie nicht alle 16 ms ausführen, sondern nur, wenn sich der Pfad ändert.

So konnte ich diese Route in Schritt 3 erstellen. In Canvas2D sah es so aus: der kürzeste Weg von Punkt A nach Punkt B - alles wie im Leben. Auf den ersten Blick scheint dies nicht der kürzeste Weg zu sein, aber es stellt sich heraus, dass es viele kürzeste Wege von Punkt A nach Punkt B entlang des sechseckigen Gitters gibt.

In WebGL sind alle Bilder Zahlen. Dort können Sie Texturen im Shader übertragen, zum Beispiel habe ich versucht, PNG zu übertragen. Es gibt keinen Unterschied für den Browser - es wird png oder Canvas2D übergeben. Für den Canvas2D-Browser entspricht dies der Bitmap für das fertige Bild. Deshalb habe ich dieses Bild zuerst in Form einer Schlange gezeichnet. № 4.

Canvas2D . Canvas2D , — . , . , Canvas2D 3D, , .

, , WebGL — , «, », .

, , Canvas2D, , , . Canvas2D , WebGL, .


: , , .

, --. , , «» .

?


: « ? ?» , : «, !». , . .

, WebGL. , . , WebGL. 2 — , .

, , . , .

, . , , , 3D — .

, . , , .
FrontendConf . , Canvas , RxJS JSX React .

30 . , , .

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


All Articles