Das letzte Mal haben wir über die Grafiken und Trajektorien für die Stop-Motion-Animation gesprochen, und heute geht es um die Matrix. Wir werden herausfinden, wie grundlegende Transformationen in CSS, SVG und WebGL erstellt werden, wie wir mit unseren eigenen Händen eine Anzeige der 3D-Welt auf dem Bildschirm erstellen, eine Parallele mit einem Tool wie Three.js zeichnen und mit Filtern für Fotos experimentieren und herausfinden, was denn solche Magie liegt in ihrem Kern.
Ich möchte Sie daran erinnern, dass wir in dieser Artikelserie verschiedene Dinge aus dem Bereich der Mathematik kennenlernen, die Layoutdesigner erschrecken, aber bei der Lösung von Arbeitsproblemen hilfreich sein können. Wir versuchen, unnötige Theorien zu vermeiden, indem wir Bilder und Erklärungen an den Fingern bevorzugen, wobei der Schwerpunkt auf praktischen Anwendungen im Frontend liegt. In dieser Hinsicht sind die Formulierungen an einigen Stellen aus mathematischer Sicht möglicherweise nicht ganz genau oder nicht ganz vollständig. Der Zweck dieses Artikels ist es, eine allgemeine Vorstellung davon zu geben, was passiert und wo man anfangen soll, wenn etwas passiert.
Skripte zum Generieren von Bildern im Stil dieser Artikelserie befinden sich auf GitHub . Wenn Sie also dasselbe herausfinden möchten, wissen Sie, was zu tun ist.
Nur wenige Definitionen
Eine Matrix in der Mathematik ist eine solche Abstraktion. Wir können sagen, dass es sich in gewissem Sinne um einen Datentyp handelt, der in Form einer rechteckigen Tabelle geschrieben wird. Die Anzahl der Spalten und Zeilen kann beliebig sein, aber im Web handelt es sich fast immer um quadratische Matrizen 2x2, 3x3, 4x4 und 5x5.
Wir brauchen auch eine Definition wie einen Vektor. Ich denke, aus der Schulgeometrie können Sie sich an die Definition erinnern, die mit den Wörtern "Länge" und "Richtung" verbunden ist, aber im Allgemeinen können in der Mathematik viele Dinge als Vektor bezeichnet werden. Insbesondere werden wir über einen Vektor als eine geordnete Menge von Werten sprechen. Zum Beispiel Koordinaten der Form (x, y) oder (x, y, z) oder eine Farbe im Format (r, g, b) oder (h, s, l, a) usw. Abhängig davon, wie viele Elemente in einer solchen Menge enthalten sind, werden wir über einen Vektor der einen oder anderen Dimension sprechen: Wenn zwei Elemente zweidimensional sind, sind drei dreidimensional usw. Im Rahmen der betrachteten Themen kann es manchmal auch zweckmäßig sein, sich einen Vektor als eine Matrix der Größen 1x2, 1x3, 1x4 usw. vorzustellen. Technisch könnten wir uns nur auf den Begriff "Matrix" beschränken, aber wir werden immer noch das Wort "Vektor" verwenden, um diese beiden Konzepte voneinander zu trennen.zumindest im logischen Sinne.
Sowohl für Matrizen als auch für Vektoren werden verschiedene Operationen definiert, die mit ihnen ausgeführt werden können. Insbesondere Multiplikation. Wir vermehren sie ständig untereinander. Der Multiplikationsalgorithmus selbst ist nicht sehr kompliziert, obwohl er etwas verwirrend erscheinen mag:
function multiplyMatrices(a, b) {
const m = new Array(a.length);
for (let row = 0; row < a.length; row++) {
m[row] = new Array(b[0].length);
for (let column = 0; column < b[0].length; column++) {
m[row][column] = 0;
for (let i = 0; i < a[0].length; i++) {
m[row][column] += a[row][i] * b[i][column];
}
}
}
return m;
}
Tatsächlich ist es für uns jedoch nicht so wichtig, sich bei der Lösung alltäglicher Probleme ständig an das Funktionsprinzip zu erinnern. Hier erwähnen wir es eher der Vollständigkeit halber, um Kontext für weitere Beispiele bereitzustellen.
Beim Umgang mit komplexen Entitäten in der Mathematik ist es sehr nützlich, zu abstrahieren. Wie hier - wir werden oft über Multiplikation sprechen, aber wir werden nicht darauf achten, welche Art von arithmetischen Operationen in welcher Reihenfolge dort auftreten. Wir wissen, dass die Multiplikation definiert ist - und das reicht für den Job.
Wir werden quadratische Matrizen nur in einer ganz bestimmten Reihe von Problemen verwenden, daher wird eine Reihe einfacher Regeln ausreichen:
- Sie können nur Matrizen derselben Dimension multiplizieren.
- Wir multiplizieren die Matrix mit der Matrix - wir erhalten die Matrix.
- Sie können eine Matrix mit einem Vektor multiplizieren - wir erhalten einen Vektor.
- Die Reihenfolge der Multiplikation ist wichtig.
Wir werden hauptsächlich die Multiplikation von links nach rechts verwenden, da diese vertrauter und für Erklärungen geeignet ist. In einigen Büchern oder Bibliotheken kann es jedoch vorkommen, dass Sie von rechts nach links notieren und alle Matrizen diagonal gespiegelt werden. Dies hat keinerlei Einfluss auf das Wesentliche der stattfindenden Manipulationen. Wir werden daher nicht näher darauf eingehen. Wenn Sie jedoch etwas kopieren und einfügen, achten Sie darauf.
Auch für die weitere Arbeit benötigen wir ein Konzept wie die Identitätsmatrix. Dies ist eine Matrix mit Einsen auf der Hauptdiagonale und Nullen in allen anderen Zellen. Der Multiplikationsalgorithmus ist so aufgebaut, dass die Identitätsmatrix mit einer anderen Matrix multipliziert wird - wir erhalten dieselbe Matrix. Oder ein Vektor, wenn es sich um einen Vektor handelt. Mit anderen Worten, die Identitätsmatrix spielt bei der üblichen Multiplikation von Zahlen die Rolle einer Eins. Dies ist eine neutrale Sache, die bei Multiplikation "nichts beeinflusst".
Und das Letzte, was wir brauchen, ist das im obigen Bild gezeigte Beispiel. Es ist sozusagen ein Sonderfall, eine Matrix mit einem Vektor zu multiplizieren, wenn die letzte Zeile der Matrix "ein Stück der Identitätsmatrix" ist und das letzte Element des Vektors ebenfalls gleich 1 ist.
In diesem Beispiel verwenden wir die Buchstaben (x, y). Wie Sie vielleicht vermutet haben, konzentriert sich die folgende Diskussion auf Koordinaten in 2D. Aber warum eine dritte Koordinate hinzufügen und als eine belassen? - du fragst. Es geht um Bequemlichkeit oder noch besser um Vielseitigkeit. Wir fügen sehr oft +1 Koordinaten hinzu, um die Berechnungen zu vereinfachen, und arbeiten mit 2D mit 3x3-Matrizen, arbeiten mit 3D - mit 4x4-Matrizen und arbeiten mit 4D, zum Beispiel mit Farben im Format (r, g, b, a) gehen mit Matrizen 5x5. Auf den ersten Blick scheint dies eine verrückte Idee zu sein, aber später werden wir sehen, wie sie alle Operationen vereinheitlicht. Wenn Sie dieses Thema genauer verstehen möchten, können Sie den Ausdruck "einheitliche Koordinaten" googeln.
Aber genug Theorie, lasst uns zur Praxis übergehen.
I. Grundlegende Transformationen in der Computergrafik
Nehmen wir die Ausdrücke aus dem obigen Beispiel und sehen sie so, wie sie sind, außerhalb des Kontextes von Matrizen:
newX = a*x + b*y + c
newY = d*x + e*y + f
Sie können sich dies als die parametrischen Gleichungen vorstellen, die wir beim letzten Mal aufgezeichnet haben. Was passiert, wenn Sie diese oder jene Koeffizienten in ihnen einstellen? Beginnen wir mit der nächsten Option:
newX = 1*x + 0*y + 0 = x
newY = 0*x + 1*y + 0 = y
Hier ändert sich nichts - die neuen Koordinaten (x, y) sind identisch mit den alten. Wenn wir diese Koeffizienten in die Matrix einsetzen und genau hinschauen, werden wir sehen, dass wir die Identitätsmatrix erhalten.
Was passiert, wenn wir andere Koeffizienten nehmen? Zum Beispiel sind dies:
newX = 1*x + 0*y + A = x + A
newY = 0*x + 1*y + 0 = y
Wir werden einen Versatz entlang der X-Achse bekommen. Aber was hätte hier sonst noch passieren können? Wenn Ihnen dies nicht klar ist, kehren Sie besser zum ersten Teil zurück, in dem wir über Diagramme und Koeffizienten gesprochen haben.
Wenn Sie diese 6 Koeffizienten - a, b, c, d, e, f - ändern und die Änderungen in x und y beobachten, werden wir früher oder später zu vier ihrer Kombinationen kommen, die für den praktischen Gebrauch nützlich und bequem erscheinen. Schreiben wir sie gleich in Form von Matrizen und kehren zum ursprünglichen Beispiel zurück:
Die Namen dieser Matrizen sprechen für sich. Beim Multiplizieren dieser Matrizen mit Vektoren mit den Koordinaten einiger Punkte, Objekte in der Szene usw. Wir bekommen neue Koordinaten für sie. Darüber hinaus arbeiten wir mit intuitiven Transformationen - Bewegen, Skalieren, Drehen und Kippen - und die Koeffizienten bestimmen den Schweregrad der einen oder anderen Transformation entlang der entsprechenden Achsen.
Es ist oft bequem, sich Matrizen als Transformationen für etwas wie Koordinaten vorzustellen. Dies ist ein anderes Wort über Abstraktionen.
Transformationen können gestapelt werden. In Bezug auf Matrizen werden wir die Multiplikationsoperation verwenden, die etwas verwirrend sein kann, aber dies ist ein Overhead für gesprochene Sprachen. Wenn wir ein Objekt zur Seite verschieben und vergrößern müssen, können wir eine Matrix für die Verschiebung, eine Matrix für die Skalierung nehmen und sie multiplizieren. Das Ergebnis ist eine Matrix, die gleichzeitig Offset und Skalierung liefert. Es bleibt nur, jeden Punkt unseres Objekts mit seiner Hilfe zu transformieren.
Grundlegende Transformationen in CSS
Aber das sind alles Worte. Mal sehen, wie es in einem echten Frontend aussieht. In CSS haben wir (plötzlich) eine Matrixfunktion. Im Kontext des Codes sieht es ungefähr so aus:
.example {
transform: matrix(1, 0, 0, 1, 0, 0);
}
Viele Neulinge, die es zum ersten Mal sehen, werden von der Frage erfasst - warum gibt es sechs Parameter? Es ist seltsam. Es wäre 4 oder 16 gewesen - es ging immer noch nicht wohin, aber 6? Was machen sie?
In Wirklichkeit ist alles einfach. Diese sechs Parameter sind genau die Koeffizienten, aus denen wir gerade die Matrizen für die Grundtransformationen zusammengestellt haben. Aus irgendeinem Grund wurden sie jedoch in einer anderen Reihenfolge angeordnet:
Auch in CSS gibt es eine Funktionsmatrix3d, um eine 3D-Transformation mithilfe einer Matrix festzulegen. Es gibt bereits 16 Parameter, um eine 4x4-Matrix zu erstellen (vergessen Sie nicht, dass wir die Dimension +1 hinzufügen).
Matrizen für grundlegende 3D-Transformationen werden auf die gleiche Weise wie für 2D erstellt. Es werden nur mehr Koeffizienten benötigt, um sie nicht in zwei, sondern in drei Koordinaten anzuordnen. Aber die Prinzipien sind die gleichen.
Natürlich wäre es jedes Mal seltsam, die Matrix zu umzäunen und die korrekte Platzierung der Koeffizienten zu überwachen, wenn mit einfachen Transformationen in CSS gearbeitet wird. Wir Programmierer versuchen normalerweise, unser Leben leichter zu machen. So haben wir jetzt kurze Funktionen in CSS zum Erstellen individueller Transformationen - translateX, translateY, scaleX usw. Normalerweise verwenden wir sie, aber es ist wichtig zu verstehen, dass sie im Inneren dieselben Matrizen erzeugen, über die wir gesprochen haben, und diesen Prozess einfach hinter einer anderen Abstraktionsebene vor uns verbergen.
Dieselben Translations-, Rotations-, Skalierungs- und Skew-Transformationen sowie die universelle Matrixfunktion zum Definieren von Transformationen sind in SVG vorhanden. Die Syntax ist etwas anders, aber das Wesentliche ist dasselbe. Bei der Arbeit mit 3D-Grafiken, beispielsweise mit WebGL, greifen wir ebenfalls auf dieselben Transformationen zurück. Aber dazu später mehr. Jetzt ist es wichtig zu verstehen, dass sie überall sind und überall nach dem gleichen Prinzip arbeiten.
Zwischensummen
Fassen wir Folgendes zusammen:
- Matrizen können als Transformationen für Vektoren verwendet werden, insbesondere für die Koordinaten einiger Objekte auf der Seite.
- Wir arbeiten fast immer mit quadratischen Matrizen und fügen eine + 1-Dimension hinzu, um Berechnungen zu vereinfachen und zu vereinheitlichen.
- Es gibt 4 grundlegende Transformationen - Verschieben, Drehen, Skalieren und Neigen. Sie werden überall von CSS bis WebGL verwendet und funktionieren überall auf ähnliche Weise.
II. DIY 3D-Szenenkonstruktion
Eine logische Weiterentwicklung des Themas Koordinatentransformation besteht darin, eine 3D-Szene zu erstellen und auf dem Bildschirm anzuzeigen. In der einen oder anderen Form ist diese Aufgabe normalerweise in allen Computergrafikkursen vorhanden, in Front-End-Kursen jedoch normalerweise nicht. Wir werden sehen, vielleicht ein wenig vereinfacht, aber dennoch eine vollwertige Version davon, wie Sie eine Kamera mit unterschiedlichen Betrachtungswinkeln herstellen können, welche Operationen erforderlich sind, um die Koordinaten aller Objekte auf dem Bildschirm zu berechnen und ein Bild zu erstellen, und auch Parallelen zu Three.js zu ziehen - der beliebtesten Tool für die Arbeit mit WebGL.
Hier sollte sich eine vernünftige Frage stellen - warum? Warum lernen, alles mit den Händen zu machen, wenn Sie ein fertiges Werkzeug haben? Die Antwort liegt in Leistungsproblemen. Sie haben wahrscheinlich Websites mit Wettbewerben wie Awwwards, CSS Design Awards, FWA und dergleichen besucht. Erinnern Sie sich, wie leistungsfähige Websites an diesen Wettbewerben teilnehmen? Ja, fast jeder dort wird langsamer, bleibt beim Laden hängen und lässt den Laptop wie ein Flugzeug summen! Ja, natürlich ist der Hauptgrund normalerweise komplexe Shader oder zu viel DOM-Manipulation, aber der zweite Grund ist die unglaubliche Menge an Skripten. Dies hat katastrophale Auswirkungen auf das Laden solcher Websites. Normalerweise passiert alles so: Sie müssen etwas in WebGL tun - sie benötigen eine Art 3D-Engine (+ 500 KB) und einige Plugins dafür (+ 500 KB);Sie müssen ein Objekt fallen lassen oder etwas auseinander fliegen - sie benötigen eine Physik-Engine (+ 1 MB oder noch mehr). Sie müssen einige Daten auf der Seite aktualisieren - fügen Sie ein SPA-Framework mit einem Dutzend Plugins (+ 500 KB) usw. hinzu. Auf diese Weise werden mehrere Megabyte an Skripten eingegeben, die nicht nur vom Client heruntergeladen werden müssen (und dies zusätzlich zu den großen Bildern), sondern auch der Browser wird nach dem Herunterladen etwas mit ihnen tun - sie fliegen nicht nur dorthin. Darüber hinaus wird der Benutzer in 99% der Fälle, bis die Skripte funktionieren, nicht die ganze Schönheit sehen, die er von Anfang an zeigen müsste.Was der Client herunterladen muss (und dies zusätzlich zu den großen Bildern), damit der Browser nach dem Laden etwas mit ihnen macht - sie kommen nicht nur aus einem bestimmten Grund zu ihm. Darüber hinaus wird der Benutzer in 99% der Fälle, bis die Skripte funktionieren, nicht die ganze Schönheit sehen, die er von Anfang an zeigen müsste.Was der Client herunterladen muss (und dies zusätzlich zu den großen Bildern), damit der Browser nach dem Laden etwas mit ihnen macht - sie kommen nicht nur aus einem bestimmten Grund zu ihm. Darüber hinaus wird der Benutzer in 99% der Fälle, bis die Skripte funktionieren, nicht die ganze Schönheit sehen, die er von Anfang an zeigen müsste.
Die weit verbreitete Meinung besagt, dass jede 666 KB lange Skripte in der Produktion die Ladezeit der Seite um genügend Zeit erhöht, damit ein Benutzer einen Website-Entwickler in den nächsten Kreis der Hölle schicken kann. Three.js in der Mindestkonfiguration wiegt 628 KB ...
Darüber hinaus erfordern Aufgaben häufig einfach nicht den Anschluss komplexer Werkzeuge. Um beispielsweise einige Ebenen mit Texturen in WebGL anzuzeigen und einige Shader hinzuzufügen, damit die Bilder in Wellen voneinander abweichen, benötigen Sie nicht die gesamten Three.js. Und um ein Objekt fallen zu lassen, benötigen Sie keine vollwertige Physik-Engine. Ja, es wird wahrscheinlich Ihre Arbeit beschleunigen, insbesondere wenn Sie damit vertraut sind, aber Sie werden es mit der Zeit der Benutzer bezahlen. Hier entscheidet jeder für sich, was für ihn rentabler ist.
Transformationskette koordinieren
Tatsächlich ist die Essenz von Koordinatentransformationen zum Erstellen einer 3D-Szene auf Ihrem Bildschirm recht einfach, aber wir werden sie seitdem immer noch Schritt für Schritt analysieren Höchstwahrscheinlich wird dieser Prozess für viele Layoutdesigner etwas Neues sein.
Also. Angenommen, ein Designer hat ein 3D-Modell gezeichnet. Es sei ein Würfel (in den Beispielen werden wir die einfachsten Konstruktionen verwenden, um die Abbildung nicht aus heiterem Himmel zu komplizieren):
Wie ist dieses Modell? Tatsächlich handelt es sich um eine Reihe von Punkten in einem Koordinatensystem und eine Reihe von Beziehungen zwischen ihnen, sodass Sie bestimmen können, zwischen welchen Punkten sich die Ebenen befinden sollen. Die Frage der Flugzeuge im Kontext von WebGL wird auf den Schultern des Browsers selbst ruhen, und Koordinaten sind für uns wichtig. Sie müssen genau herausfinden, wie Sie sie transformieren können.
Das Modell hat, wie gesagt, ein Koordinatensystem. Aber normalerweise wollen wir viele Models haben, wir wollen eine Szene mit ihnen machen. Die Szene, unsere 3D-Welt, wird ein eigenes globales Koordinatensystem haben. Wenn wir nur die Koordinaten des Modells als globale Koordinaten interpretieren, befindet sich unser Modell wie "im Zentrum der Welt". Mit anderen Worten, nichts wird sich ändern. Aber wir möchten viele Modelle an verschiedenen Orten in unserer Welt hinzufügen, ungefähr so:
Was ist zu tun? Sie müssen die Koordinaten jedes einzelnen Modells in globale Koordinaten konvertieren. Die Position des Modells im Raum wird durch Offsets, Rotationen und Skalierungen festgelegt - wir haben diese grundlegenden Transformationen bereits gesehen. Jetzt müssen wir für jedes Modell eine Transformationsmatrix erstellen, die genau diese Informationen darüber speichert, wo sich das Modell in Bezug auf die Welt befindet und wie es gedreht wird.
Für Würfel gibt es beispielsweise ungefähr die folgenden Matrizen:
// .
// « » .
const modelMatrix1 = [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
];
// , X.
const modelMatrix2 = [
[1, 0, 0, 1.5],
[0, 1, 0, 0 ],
[0, 0, 1, 0 ],
[0, 0, 0, 1 ]
];
// , X .
const modelMatrix3 = [
[1, 0, 0, -1.5],
[0, 1, 0, 0 ],
[0, 0, 1, 0 ],
[0, 0, 0, 1 ]
];
Ferner werden wir ungefähr wie folgt handeln:
{
= [ ] *
}
Dementsprechend benötigt jedes Modell eine eigene Matrix.
Ebenso können Sie eine Kette aus einigen Objekten erstellen. Wenn ein Vogel mit den Flügeln schlagen muss, ist es angebracht, die Koordinaten der Flügelpunkte in die Koordinaten des Vogels und dann in die globalen Weltkoordinaten zu übersetzen. Es wird viel einfacher sein, die Flügelbahn sofort in globalen Koordinaten zu erraten. Aber das ist übrigens so.
Als nächstes müssen Sie entscheiden, auf welcher Seite wir die Welt betrachten. Ich brauche eine Kamera.
Eine Kamera ist eine solche Abstraktion, wie eine Nachahmung einer physischen Kamera. Es hat Koordinaten und einige Neigungswinkel, die seine Position in globalen Koordinaten festlegen. Unsere Aufgabe ist es, eine Reihe von jetzt globalen Koordinaten in ein Kamerakoordinatensystem umzuwandeln. Das Prinzip ist das gleiche wie im vorherigen Beispiel:
{
= [ ] *
}
, , . !
Schauen wir uns die Szene von der Stelle aus an, an der sich unsere bedingte Kamera befindet:
Nachdem wir alle Punkte in das Kamerakoordinatensystem konvertiert haben, können wir einfach die Z-Achse verwerfen und die X- und Y-Achse als "horizontal" und "vertikal" interpretieren. Wenn Sie alle Punkte der Modelle auf dem Bildschirm zeichnen, erhalten Sie wie im Beispiel ein Bild - keine Perspektive und es ist schwer zu verstehen, welcher Teil der Szene tatsächlich in den Rahmen fällt. Die Kamera scheint in alle Richtungen unendlich groß zu sein. Wir können irgendwie alles so einstellen, dass das, was wir brauchen, auf den Bildschirm passt, aber es wäre schön, auf universelle Weise zu bestimmen, welcher Teil der Szene in das Sichtfeld der Kamera fällt und welcher nicht.
Mit physischen Kameras können wir über einen Betrachtungswinkel sprechen. Warum nicht auch hier hinzufügen?
Dafür brauchen wir eine andere Matrix - die Projektionsmatrix. Im Allgemeinen kann es auf verschiedene Arten gebaut werden. Abhängig davon, was als Anfangsparameter verwendet wird, erhalten Sie eine etwas andere Ansicht dieser Matrix, aber die Essenz wird dieselbe sein. Wir nehmen die folgende leicht vereinfachte Version:
// 90
const s = 1 / (Math.tan(90 * Math.PI / 360));
const n = 0.001;
const f = 10;
const projectionMatrix = [
[s, 0, 0, 0],
[0, s, 0, 0],
[0, 0, -(f)/(f-n), -f*n/(f-n)],
[0, 0, -1, 0]
];
Die Projektionsmatrix enthält auf die eine oder andere Weise drei Parameter: Dies ist der Betrachtungswinkel sowie der minimale und maximale Abstand zu den Punkten, mit denen Sie arbeiten. Es kann auf verschiedene Arten ausgedrückt werden, es kann auf verschiedene Arten verwendet werden, aber diese Parameter werden trotzdem in dieser Matrix enthalten sein.
Ich verstehe, dass es nie offensichtlich ist, warum die Matrix genau so aussieht, aber um sie mit Erklärungen abzuleiten, benötigen Sie Formeln für 2-3 Seiten. Dies bringt uns wieder zu der Idee zurück, dass es nützlich ist, zu abstrahieren - wir können mit einem allgemeineren Ergebnis arbeiten, ohne auf kleine Details einzugehen, bei denen es nicht notwendig ist, ein bestimmtes Problem zu lösen.
Nun machen wir die bereits bekannten Transformationen:
{
= [ ] *
}
Wir bekommen in unserem Sichtfeld genau das, was wir erwarten. Vergrößern des Winkels - wir sehen vor allem an den Seiten, Verkleinern des Winkels - sehen wir nur das, was näher an der Richtung liegt, in die die Kamera gerichtet ist. PROFITIEREN!
Aber eigentlich nein. Wir haben die Perspektive vergessen. Ein hoffnungsloses Bild wird an wenigen Stellen benötigt, daher müssen Sie es irgendwie hinzufügen. Und hier brauchen wir plötzlich keine Matrizen mehr. Die Aufgabe sieht sehr schwierig aus, wird jedoch durch die banale Division der X- und Y-Koordinaten durch W für jeden Punkt gelöst:
* Hier haben wir die Kamera zur Seite verschoben und parallele Linien "auf dem Boden" hinzugefügt, um klarer zu machen, wo genau diese Perspektive erscheint.
Durch Auswahl der Koeffizienten nach unserem Geschmack erhalten wir verschiedene Perspektivenoptionen. In gewissem Sinne bestimmen die Koeffizienten hier den Linsentyp, wie stark er den umgebenden Raum „abflacht“.
Jetzt haben wir ein vollständiges Bild. Sie können die X- und Y-Koordinaten für jeden Punkt nehmen und nach Belieben auf dem Bildschirm zeichnen.
Im Allgemeinen reicht dies aus, um eine Szene zu erstellen. In realen Projekten kann es jedoch auch zu einer zusätzlichen Transformation im Zusammenhang mit der Skalierung am Ende kommen. Die Idee ist, dass wir nach der Projektion die Koordinaten (x, y) innerhalb von 1 erhalten, die normalisierten Koordinaten, und sie dann mit der Größe des Bildschirms oder der Leinwand multiplizieren, damit die Koordinaten auf dem Bildschirm angezeigt werden. Dieser zusätzliche Schritt nimmt die Leinwandgröße aus allen Berechnungen heraus und lässt sie nur ganz am Ende. Dies ist manchmal bequem.
Hier haben Sie wahrscheinlich Kopfschmerzen aufgrund der Informationsmenge. Lassen Sie uns also langsamer werden und alle Transformationen an einem Ort wiederholen:
Wenn Sie diese Transformationen zu einer kombinieren, erhalten Sie einen kleinen Motor.
Wie sieht es in Three.js aus?
Nachdem wir nun verstanden haben, woher diese kleine Engine stammt, schauen wir uns ein Beispiel für den Standard-Vertex-Shader in Three.js an, der "nichts tut":
void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
oder vollständiger:
void main() {
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
}
Erinnert er dich an irgendetwas? Ja, das ist dieser spezielle Motor. Und mit "tut nichts" meinen wir, dass es nur die ganze Arbeit der Neuberechnung von Koordinaten erledigt, basierend auf Matrizen, die sorgfältig von Three.js übergeben wurden. Aber niemand stört sich daran, diese Matrizen mit eigenen Händen herzustellen, oder?
Kameratypen in Computergrafik und Three.js
Das Thema Kameratypen steht nicht in direktem Zusammenhang mit Matrizen, aber wir werden dennoch ein paar Minuten darauf verwenden, da wir immer noch über Three.js sprechen und manchmal Leute ein Durcheinander im Kopf haben.
Die Kamera ist eine Abstraktion. Es hilft uns, in der 3D-Welt genauso zu denken wie in der realen Welt. Wie gesagt, eine Kamera hat eine Position im Raum, eine Blickrichtung und einen Blickwinkel. All dies wird unter Verwendung von zwei Matrizen und möglicherweise einer zusätzlichen Koordinatenteilung angegeben, um eine Perspektive zu erstellen.
In der Computergrafik gibt es zwei Arten von Kameras - "mit Perspektive" und "ohne Perspektive". Dies sind zwei grundlegend unterschiedliche Arten von Kameras in technischer Hinsicht, die unterschiedliche Schritte erfordern, um ein Bild zu erhalten. Und alle. Es gibt nichts anderes. Alles andere sind ihre Kombinationen, einige komplexere Abstraktionen. Zum Beispiel hat Three.js eine Stereokamera - dies ist technisch gesehen keine Art "Spezialkamera", sondern nur eine Abstraktion - zwei Kameras, die räumlich leicht voneinander entfernt und in einem Winkel angeordnet sind:
Für jede Hälfte des Bildschirms nehmen wir unsere eigene Kamera und es stellt sich heraus Stereobild. Und CubeCamera ist 6 gewöhnliche Kameras, die sich auf verschiedenen Seiten eines Punktes befinden, mehr nicht.
Was weiter?
Der nächste Schritt, nachdem die Koordinaten der Objekte ermittelt wurden, besteht darin, zu bestimmen, welche Objekte sichtbar und welche hinter anderen verborgen sind. Im Kontext von WebGL erledigt der Browser dies selbst. Nun, es wird immer noch verwandte Aufgaben geben, wie das Anwenden von Texturen auf sie, das Berechnen der Beleuchtung durch Normalen, Schatten, das Nachbearbeiten von Bildern usw. Aber wir haben bereits den wichtigsten und schwierigsten Teil zu verstehen getan. Es ist großartig. Tatsächlich benötigen viele generative Dinge nicht genau diese Texturen und Beleuchtung, so dass es sehr gut sein kann, dass das jetzt gewonnene Wissen ausreicht, um mit ihnen zu arbeiten.
Das Werfen eines Schattens von einem Objekt auf eine Ebene ist übrigens nichts anderes als eine Projektion dieses Objekts auf diese Ebene in einem bestimmten Winkel, gefolgt vom Mischen von Farben. Der Vorgang ist der Kamera von Natur aus sehr ähnlich, fügt jedoch auch einen Winkel zwischen der Projektionsebene und der "Blickrichtung" hinzu.
Über Texturen und Effekte für Bilder in WebGL, auch ohne Bibliotheken, haben wir in früheren Artikeln mehr als einmal gesprochen. Sie können sich auf sie beziehen, wenn Sie an diesem Thema interessiert sind. Wenn wir all dieses Wissen miteinander kombinieren, können wir mit unseren eigenen Händen vollwertige, farbenfrohe 3D-Dinge erstellen.
3D- . – - . , Three.js . , , , - , - . , .
Jetzt ist es an der Zeit, das Obige zusammenzufassen, damit in Ihrem Kopf Platz für den nächsten Anwendungsfall für Matrizen ist.
Damit:
- Sie können eine 3D-Welt erstellen und die Koordinaten von Objekten auf dem Bildschirm mit Ihren eigenen Händen mithilfe eines Zuges aus Matrizen berechnen.
- In der 3D-Welt arbeiten wir mit einer solchen Abstraktion wie einer „Kamera“. Es hat einen Ort, eine Richtung und einen Betrachtungswinkel. All dies wird mit denselben Matrizen eingestellt. Und es gibt zwei grundlegende Kameraansichten - Perspektive und Nichtperspektive.
- Im Kontext von WebGL können durch manuelles Rendern eines Bildes auf dem Bildschirm oder durch physikalische Berechnungen häufig starke Abhängigkeiten beseitigt und das Laden von Seiten beschleunigt werden. Es ist jedoch wichtig, ein Gleichgewicht zwischen Ihren Skripten, vorgefertigten Tools und alternativen Optionen zur Problemlösung zu finden und dabei nicht nur auf Ihre Bequemlichkeit zu achten, sondern auch auf Fragen der Download-Geschwindigkeit und der ultimativen Leistung, auch auf Telefonen.
III. Filter für Bilder
Schließlich werden wir einen solchen Anwendungsbereich von Matrizen als Filter für Bilder betrachten. Wenn wir eine Farbe im RGBA-Format als Vektor betrachten, können wir davon ausgehen, dass wir hier eine Transformation anwenden können, die der mit Koordinaten verwendeten ähnelt:
Und diese nach dem offensichtlichen Prinzip auf das Bild anwenden:
{
= [ ] *
}
Wenn die Identitätsmatrix als Matrix fungiert, ändert sich nichts, das wissen wir bereits. Was passiert, wenn Sie Filter anwenden, die den Übersetzungs- und Skalierungstransformationen ähneln?
OU. Das Ergebnis sind Helligkeits- und Kontrastfilter. Interessant.
Wenn Sie mit solchen Filtern experimentieren, sollten Sie immer daran denken, die Werte so anzupassen, dass das Bild nicht überbelichtet wird. Wenn Sie etwas mit einer großen Zahl multiplizieren, müssen Sie höchstwahrscheinlich irgendwo etwas subtrahieren oder dividieren. Wie im vorherigen Beispiel gezeigt.
Aber wie macht man aus einer Farbe ein Schwarzweißbild? Das erste, was Ihnen in den Sinn kommt, ist, die RGB-Kanalwerte zu addieren, durch 3 zu teilen und den resultierenden Wert für alle drei Kanäle zu verwenden. Im Matrixformat sieht es ungefähr so aus:
Und obwohl wir ein Schwarzweißbild erhalten haben, kann es noch verbessert werden. Unser Auge nimmt die Helligkeit verschiedener Farben tatsächlich unterschiedlich wahr. Und um dies während der Entsättigung irgendwie zu vermitteln, erstellen wir für jeden RGB-Kanal in dieser Matrix unterschiedliche Koeffizienten.
Das folgende Beispiel zeigt die allgemein akzeptierten Werte für diese Koeffizienten, aber niemand stört sich daran, mit ihnen zu spielen. Insgesamt sollten diese Koeffizienten 1 ergeben, aber abhängig von ihren Proportionen erhalten wir leicht unterschiedliche Schwarzweißbilder. Dies kann bis zu einem gewissen Grad eine unterschiedliche Farbwiedergabe bei der Arbeit mit Filmkameras simulieren.
Und wenn wir auch die Hauptdiagonale ein wenig multiplizieren, erhalten wir einen universellen Sättigungsfilter:
Er funktioniert in beide Richtungen - sowohl bei der Entsättigung (Sie können ein vollständig schwarzweißes Bild erreichen) als auch bei der Sättigung. Es hängt alles vom entsprechenden Koeffizienten ab.
Im Allgemeinen können Sie lange Zeit mit Filtern spielen und verschiedene Ergebnisse erzielen:
* Die in diesem Beispiel verwendeten Matrizen können auf GitHub angezeigt werdenwenn du sie plötzlich brauchst. Um in den Artikel eingefügt zu werden, ist ihr Volumen zu groß.
Aber lassen Sie uns noch ein wenig darauf achten, wo dies tatsächlich zutrifft. Es ist klar, dass die Idee, die Farbe für jedes Pixel zu ersetzen, Shader für die Verarbeitung eines Fotos oder für die Nachbearbeitung einer 3D-Szene vorschlägt, aber vielleicht befindet sie sich noch irgendwo im Frontend?
Filter in CSS
In CSS haben wir eine Filtereigenschaft. Und dort gibt es insbesondere solche Optionen für farbbezogene Filter:
- Helligkeit (wir haben es geschafft)
- Kontrast (fertig)
- invertieren (wie Kontrast, nur Hauptdiagonalkoeffizienten mit einem anderen Vorzeichen)
- gesättigt (fertig)
- Graustufen (wie bereits erwähnt, ist dies ein Sonderfall von gesättigt)
- Sepia (ein sehr vages Konzept, verschiedene Versionen von Sepia werden durch Spielen mit Koeffizienten erhalten, wobei wir das Vorhandensein von Blau irgendwie reduzieren)
Und diese Filter akzeptieren Koeffizienten als Eingabe, die dann in der einen oder anderen Form in die zuvor erstellten Matrizen eingesetzt werden. Jetzt wissen wir, wie diese Magie von innen wirkt. Und jetzt ist klar, wie diese Filter im Darm des CSS-Interpreters kombiniert werden, denn hier wird alles nach dem gleichen Prinzip wie bei Koordinaten aufgebaut: Matrizen multiplizieren - Effekte hinzufügen. Es stimmt, dass diese Eigenschaft in CSS keine benutzerdefinierte Funktionsmatrix enthält. Aber es ist in SVG!
Filtermatrizen in SVG
In SVG haben wir feColorMatrix, mit der Filter für Bilder erstellt werden. Und hier haben wir bereits völlige Freiheit - wir können eine Matrix nach unserem Geschmack erstellen. Die Syntax ist ungefähr so:
<filter id=’my-color-filter’>
<feColorMatrix in=’SourceGraphics’
type=’matrix’,
values=’1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1‘
/>
</filter>
Sie können SVG-Filter auch auf reguläre DOM-Elemente in CSS anwenden. Dafür gibt es eine spezielle URL-Funktion ... Aber das habe ich Ihnen nicht gesagt!
Tatsächlich werden SVG-Filter in CSS immer noch nicht von allen Browsern unterstützt (ohne den Finger auf den IE zu richten), aber es gibt Gerüchte, dass Edge endlich auf die Chrom-Engine umsteigt und ältere Versionen in absehbarer Zukunft die Unterstützung verlieren werden. Daher ist es Zeit für diese Technologie Meister, Sie können viele interessante Dinge damit machen.
Was passiert sonst noch?
Zusätzlich zu den Effekten für Bilder, die auf dem Prinzip der Transformationen basieren, gibt es verschiedene Dinge, die auf Pixelverschiebungen, dem Mischen ihrer Farben und anderen Manipulationen beruhen, wobei die Matrix ein gutes Format zum Speichern von Daten sein kann, mit denen genau diese Manipulation stattfinden sollte.
Kernel-Matrix
Insbesondere im Frontend stoßen wir auf so etwas wie die Kernelmatrix und die damit verbundenen Auswirkungen. Der Punkt ist einfach - es gibt eine quadratische Matrix, normalerweise 3x3 oder 5x5, obwohl es mehr geben kann, und Koeffizienten werden darin gespeichert. In der Mitte der Matrix - für das "aktuelle" Pixel, um die Mitte herum - für benachbarte Pixel. Wenn die Matrix 5 x 5 ist, wird eine weitere Ebene um die Mitte angezeigt - für Pixel, die eins von der aktuellen sind. Wenn 7x7 - dann eine andere Schicht usw. Mit anderen Worten, wir betrachten die Matrix als ein solches zweidimensionales Feld, auf dem Sie die Koeffizienten nach eigenem Ermessen anordnen können, bereits ohne Bezugnahme auf Gleichungen. Und sie werden wie folgt interpretiert:
{
=
,
}
Eine leere Leinwand ist für solche Aufgaben nicht sehr geeignet, aber die Shader sind sehr gleichmäßig. Es ist jedoch leicht zu erraten, dass je größer die Matrix ist, desto mehr benachbarte Pixel werden verwendet. Wenn die Matrix 3x3 ist, fügen wir 9 Farben hinzu, wenn 5x5 - 25, wenn 7x7 - 49 usw. Mehr Betrieb - mehr Belastung des Prozessors oder der Grafikkarte. Dies wirkt sich unweigerlich auf die Leistung der gesamten Seite aus.
Verwenden Sie für solche Effekte nach Möglichkeit kleine Matrizen, wenn Sie sie irgendwo in Echtzeit überlagern müssen.
Innerhalb von SVG haben wir ein spezielles feConvolveMatrix-Tag, das nur zum Erstellen solcher Effekte erstellt wurde:
<filter id=’my-image-filter’>
<feConvolveMatrix
kernelMatrix=’0 0 0
0 1 0
0 0 0’
/>
</filter>
Hier haben wir den einfachsten Filter für das Bild erstellt, der nichts bewirkt - die neue Farbe für jedes Pixel entspricht der aktuellen Farbe, multipliziert mit 1, und die Werte für die Farben benachbarter Pixel werden mit 0 multipliziert.
Beachten Sie, dass verschiedene Browser SVGs unterschiedlich rendern und die Farbwiedergabe auch sehr weit verbreitet sein kann. Manchmal ist der Unterschied einfach katastrophal. Testen Sie also immer Ihre SVG-Filter oder verwenden Sie Canvas, was in unserem Kontext vorhersehbarer ist.
Wenn wir anfangen, die Zahlen in Ebenen anzuordnen, von größer zu kleiner, erhalten wir Unschärfe:
Je größer die Matrix, je mehr benachbarte Pixel wir berühren, desto mehr wird das Bild gewaschen. Die Hauptsache hier ist, nicht zu vergessen, die Werte zu normalisieren, sonst leuchtet das Bild einfach auf.
Wenn wir nun wissen, wie Unschärfe funktioniert, können wir verstehen, warum die aktive Verwendung auf einer Seite in CSS oder SVG zu Bremsen führt - für jedes Pixel führt der Browser eine Reihe von Berechnungen durch.
Wenn Sie anfangen, mit dem Ändern der Vorzeichen der Koeffizienten zu experimentieren und sie in verschiedenen Mustern anzuordnen, erhalten Sie Schärfeeffekte, Kantenerkennung und einige andere. Versuche selbst mit ihnen zu spielen. Dies könnte hilfreich sein.
Auf diese Weise können Sie in Echtzeit verschiedene Effekte für Fotos oder sogar Videos erzielen und diese von bestimmten Benutzeraktionen abhängig machen. Es hängt alles von Ihrer Vorstellungskraft ab.
Zwischensummen
Fassen wir zusammen, was in diesem Teil gesagt wurde:
- Matrizen können nicht nur für Transformationen in Bezug auf Koordinaten verwendet werden, sondern auch zum Erstellen von Farbfiltern. Alles wird nach dem gleichen Prinzip gemacht.
- Matrizen können als praktischer 2D-Speicher für einige Daten verwendet werden, einschließlich verschiedener Koeffizienten für visuelle Effekte.
Fazit
Wenn wir ein wenig von den komplizierten Algorithmen abstrahieren, werden Matrizen zu einem erschwinglichen Werkzeug zur Lösung praktischer Probleme. Mit ihrer Hilfe können Sie geometrische Transformationen mit Ihren eigenen Händen berechnen, auch im Rahmen von CSS und SVG, 3D-Szenen erstellen und im Rahmen von WebGL alle Arten von Filtern für Fotos oder für die Nachbearbeitung von Bildern erstellen. Alle diese Themen gehen normalerweise über das klassische Frontend hinaus und beziehen sich eher auf Computergrafiken im Allgemeinen. Selbst wenn Sie diese Probleme nicht direkt lösen, können Sie die Funktionsweise einiger Ihrer Tools besser verstehen, wenn Sie die Prinzipien ihrer Lösung kennen. Es wird niemals überflüssig sein.
Ich hoffe, dieser Artikel hat Ihnen geholfen, das Thema der praktischen Verwendung von Matrizen im Frontend zu verstehen, oder Ihnen zumindest eine Grundlage gegeben, auf der Sie in Ihrer weiteren Entwicklung aufbauen können. Wenn Sie der Meinung sind, dass einige andere Themen im Zusammenhang mit Mathematik oder Physik im Zusammenhang mit dem Layout dieselbe Überprüfung wert sind, schreiben Sie Ihre Gedanken in die Kommentare. Vielleicht werden sie in einem der nächsten Artikel behandelt.