Ich beschloss, etwas über den Programmieraspekt zu schreiben und wie Komponenten miteinander kommunizieren. Vielleicht werden einige Bereiche beleuchtet.
Die Präsentation
Was braucht es, um dieses einzelne Bild, das Sie in Ihrer Frage gepostet haben, überhaupt auf dem Bildschirm zeichnen zu lassen?
Es gibt viele Möglichkeiten, ein Dreieck auf dem Bildschirm zu zeichnen. Nehmen wir zur Vereinfachung an, dass keine Scheitelpunktpuffer verwendet wurden. (Ein Vertex-Puffer ist ein Speicherbereich, in dem Sie Koordinaten speichern.) Angenommen, das Programm teilt der Grafikverarbeitungs-Pipeline einfach jeden einzelnen Vertex (ein Vertex ist nur eine Koordinate im Raum) in einer Reihe mit.
Aber bevor wir etwas zeichnen können, müssen wir zuerst ein Gerüst bauen. Wir werden später sehen, warum
// Clear The Screen And The Depth Buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Reset The Current Modelview Matrix glMatrixMode(GL_MODELVIEW); glLoadIdentity(); // Drawing Using Triangles glBegin(GL_TRIANGLES); // Red glColor3f(1.0f,0.0f,0.0f); // Top Of Triangle (Front) glVertex3f( 0.0f, 1.0f, 0.0f); // Green glColor3f(0.0f,1.0f,0.0f); // Left Of Triangle (Front) glVertex3f(-1.0f,-1.0f, 1.0f); // Blue glColor3f(0.0f,0.0f,1.0f); // Right Of Triangle (Front) glVertex3f( 1.0f,-1.0f, 1.0f); // Done Drawing glEnd();
Also, was hat das gemacht?
Wenn Sie ein Programm schreiben, das die Grafikkarte verwenden möchte, wählen Sie normalerweise eine Art Schnittstelle zum Treiber. Einige bekannte Schnittstellen zum Treiber sind:
- OpenGL
- Direct3D
- CUDA
Für dieses Beispiel bleiben wir bei OpenGL. Jetzt haben Sie über die Schnittstelle zum Treiber alle Tools, die Sie benötigen, um Ihr Programm mit der Grafikkarte (oder dem Treiber, der dann mit der Karte spricht ) zu veranlassen.
Diese Schnittstelle bietet Ihnen bestimmte Werkzeuge . Diese Tools haben die Form einer API, die Sie von Ihrem Programm aus aufrufen können.
Diese API wird im obigen Beispiel verwendet. Lass uns genauer hinschauen.
Das Gerüst
Bevor Sie wirklich zeichnen können, müssen Sie ein Setup durchführen . Sie müssen Ihr Ansichtsfenster (den Bereich, der tatsächlich gerendert wird), Ihre Perspektive (die Kamera in Ihre Welt), das zu verwendende Anti-Aliasing definieren (um die Kanten Ihres Dreiecks zu glätten) ...
Aber wir werden uns das nicht ansehen. Wir werfen einen Blick auf die Dinge, die Sie für jeden Frame machen müssen . Mögen:
Bildschirm löschen
Die Grafik-Pipeline wird den Bildschirm nicht für jeden Frame löschen. Du musst es sagen. Warum? Deshalb:
Wenn Sie den Bildschirm nicht löschen, zeichnen Sie einfach jeden Frame darüber. Deshalb rufen wir glClear
mit dem GL_COLOR_BUFFER_BIT
Set an. Das andere Bit ( GL_DEPTH_BUFFER_BIT
) teilt OpenGL mit den löschen Tiefen Puffern. Dieser Puffer wird verwendet, um zu bestimmen, welche Pixel vor (oder hinter) anderen Pixeln liegen.
Transformation
Transformation ist der Teil, in dem wir alle Eingabekoordinaten (die Eckpunkte unseres Dreiecks) nehmen und unsere ModelView-Matrix anwenden. Dies ist die Matrix, die erklärt, wie unser Modell (die Scheitelpunkte) gedreht, skaliert und verschoben (verschoben) wird.
Als nächstes wenden wir unsere Projektionsmatrix an. Dadurch werden alle Koordinaten so verschoben, dass sie unserer Kamera korrekt gegenüberliegen.
Jetzt verwandeln wir uns noch einmal mit unserer Viewport-Matrix. Wir tun dies, um unser Modell an die Größe unseres Monitors anzupassen. Jetzt haben wir eine Reihe von Scheitelpunkten, die gerendert werden können!
Wir werden später etwas zurückkehren.
Zeichnung
Um ein Dreieck zu zeichnen, können Sie OpenGL einfach anweisen, eine neue Liste von Dreiecken zu starten, indem Sie glBegin
mit der GL_TRIANGLES
Konstante aufrufen .
Es gibt auch andere Formen, die Sie zeichnen können. Wie ein Dreieckstreifen oder ein Dreieckfächer . Dies sind in erster Linie Optimierungen, da sie weniger Kommunikation zwischen der CPU und der GPU erfordern, um die gleiche Anzahl von Dreiecken zu zeichnen.
Danach können wir eine Liste von drei Scheitelpunkten bereitstellen, aus denen jedes Dreieck bestehen soll. Jedes Dreieck verwendet 3 Koordinaten (da wir uns im 3D-Raum befinden). Zusätzlich kann ich für jeden Scheitelpunkt eine Farbe angeben, indem ich glColor3f
vor dem Aufruf aufrufe glVertex3f
.
Die Schattierung zwischen den 3 Eckpunkten (den drei Ecken des Dreiecks) wird von OpenGL automatisch berechnet . Die Farbe wird über die gesamte Fläche des Polygons interpoliert.
Interaktion
Wenn Sie jetzt auf das Fenster klicken. Die Anwendung muss nur die Fenstermeldung erfassen, die den Klick signalisiert. Dann können Sie eine beliebige Aktion in Ihrem Programm ausführen.
Dies wird viel schwieriger, wenn Sie mit Ihrer 3D-Szene interagieren möchten.
Sie müssen zunächst klar wissen, bei welchem Pixel der Benutzer auf das Fenster geklickt hat. Unter Berücksichtigung Ihrer Perspektive können Sie dann die Richtung eines Strahls vom Mausklick in Ihre Szene berechnen. Sie können dann berechnen, ob sich ein Objekt in Ihrer Szene mit diesem Strahl schneidet . Jetzt wissen Sie, ob der Benutzer auf ein Objekt geklickt hat.
Also, wie lässt du es drehen?
Transformation
Mir sind zwei Arten von Transformationen bekannt, die im Allgemeinen angewendet werden:
- Matrixbasierte Transformation
- Knochenbasierte Transformation
Der Unterschied ist, dass Knochen einzelne Scheitelpunkte beeinflussen . Matrizen wirken sich immer auf alle gezeichneten Scheitelpunkte auf dieselbe Weise aus. Schauen wir uns ein Beispiel an.
Beispiel
Zuvor haben wir unsere Identitätsmatrix geladen , bevor Sie unser Dreieck gezeichnet haben. Die Identitätsmatrix ist eine, die einfach keine Transformation liefert . Was immer ich zeichne, wird nur von meiner Perspektive beeinflusst. Das Dreieck wird also überhaupt nicht gedreht.
Wenn ich es jetzt drehen möchte, könnte ich entweder selbst rechnen (auf der CPU) und einfach glVertex3f
mit anderen Koordinaten (die gedreht werden) aufrufen . Oder ich könnte die GPU die ganze Arbeit machen lassen, indem ich glRotatef
vor dem Zeichnen anrufe:
// Rotate The Triangle On The Y axis glRotatef(amount,0.0f,1.0f,0.0f);
amount
ist natürlich nur ein fester Wert. Wenn Sie animieren möchten, müssen Sie amount
ihn bei jedem Frame nachverfolgen und vergrößern.
Also, warten Sie, was ist mit all den Matrix-Gesprächen früher passiert?
In diesem einfachen Beispiel müssen wir uns nicht um Matrizen kümmern. Wir rufen einfach an glRotatef
und kümmern uns um all das.
glRotate
erzeugt eine Drehungangle
um den Vektor xyz. Die aktuelle Matrix (siehe glMatrixMode ) wird mit einer Rotationsmatrix multipliziert, wobei das Produkt die aktuelle Matrix ersetzt, als ob glMultMatrix mit der folgenden Matrix als Argument aufgerufen würde:x 2 - 1 - c + cx - y - 1 - c - z - sx - z - 1 - c + y - 0 - y - x - 1 - c + z - sy 2 - 1 - c + cy - z 1 - c - x s 0 x z 1 - c - y sy z 1 - c + x sz 2 1 - c + c 0 0 0 0 1
Vielen Dank dafür!
Fazit
Es wird offensichtlich, dass mit OpenGL viel geredet wird . Aber es sagt uns nichts. Wo ist die Kommunikation?
Das einzige, was OpenGL uns in diesem Beispiel sagt, ist, wann es fertig ist . Jede Operation dauert eine gewisse Zeit. Manche Operationen dauern unglaublich lange, andere sind unglaublich schnell.
Das Senden eines Scheitelpunkts an die GPU ist so schnell, dass ich gar nicht weiß, wie ich es ausdrücken soll. Das Senden tausender Scheitelpunkte von der CPU an die GPU, jedes einzelne Frame, ist höchstwahrscheinlich überhaupt kein Problem.
Das Löschen des Bildschirms kann eine Millisekunde oder weniger dauern (denken Sie daran, dass Sie normalerweise nur etwa 16 Millisekunden Zeit haben, um jedes Bild zu zeichnen), je nachdem, wie groß Ihr Ansichtsfenster ist. Um es zu löschen, muss OpenGL jedes einzelne Pixel in der Farbe zeichnen, in der Sie löschen möchten, dh Millionen Pixel.
Abgesehen davon können wir OpenGL so gut wie nur nach den Fähigkeiten unseres Grafikadapters fragen (maximale Auflösung, maximales Anti-Aliasing, maximale Farbtiefe, ...).
Wir können eine Textur aber auch mit Pixeln füllen, die jeweils eine bestimmte Farbe haben. Jedes Pixel enthält somit einen Wert und die Textur ist eine riesige "Datei", die mit Daten gefüllt ist. Wir können das in die Grafikkarte laden (durch Erstellen eines Texturpuffers), dann einen Shader laden, diesen Shader anweisen, unsere Textur als Eingabe zu verwenden, und einige extrem schwere Berechnungen an unserer "Datei" ausführen.
Wir können dann das Ergebnis unserer Berechnung (in Form neuer Farben) in eine neue Textur "umwandeln".
So können Sie die GPU auf andere Weise für Sie arbeiten lassen. Ich gehe davon aus, dass CUDA diesem Aspekt ähnlich ist, aber ich hatte nie die Gelegenheit, damit zu arbeiten.
Wir haben das ganze Thema wirklich nur wenig berührt. Die Programmierung von 3D-Grafiken ist eine Hölle.