Vertexarray: Unterschied zwischen den Versionen
Philip (Diskussion | Beiträge) (Meilenstein 1) |
DGLBot (Diskussion | Beiträge) K (Der Ausdruck ''<cpp>(.*?)</cpp>'' wurde ersetzt mit ''<source lang="cpp">$1</source>''.) |
||
(7 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt) | |||
Zeile 1: | Zeile 1: | ||
− | |||
− | |||
− | |||
− | |||
Bei einem Vertexarray werden die Vertexdaten in einem Array zur Grafikkarte geschickt. | Bei einem Vertexarray werden die Vertexdaten in einem Array zur Grafikkarte geschickt. | ||
Zeile 21: | Zeile 17: | ||
Indexarray indices: {0, 2, 3, 1, 2, 4, 5, 3}<br> | Indexarray indices: {0, 2, 3, 1, 2, 4, 5, 3}<br> | ||
Beim Zeichnen wird nun folgendermaßen vorgegangen: | Beim Zeichnen wird nun folgendermaßen vorgegangen: | ||
− | <cpp> | + | <source lang="cpp"> |
int vertexCount = length(indices); | int vertexCount = length(indices); | ||
glBegin(GL_QUADS); | glBegin(GL_QUADS); | ||
Zeile 30: | Zeile 26: | ||
} | } | ||
glEnd(); | glEnd(); | ||
− | </ | + | </source> |
Dieses Minimalbeispiel soll nur verdeutlichen, wie Vertexarrays prinzipiell funktionieren. Pro Vertex gibt es natürlich noch andere interessante Informationen wie dessen Normale, dessen Farbe und Texturkoordinate. Dazu später mehr. | Dieses Minimalbeispiel soll nur verdeutlichen, wie Vertexarrays prinzipiell funktionieren. Pro Vertex gibt es natürlich noch andere interessante Informationen wie dessen Normale, dessen Farbe und Texturkoordinate. Dazu später mehr. | ||
Zeile 39: | Zeile 35: | ||
====Die Initialisierung==== | ====Die Initialisierung==== | ||
Als erstes definieren wir einen Vertex: | Als erstes definieren wir einen Vertex: | ||
− | <cpp> | + | <source lang="cpp"> |
typedef struct { | typedef struct { | ||
GLfloat x, y, z; | GLfloat x, y, z; | ||
GLfloat r, g, b; | GLfloat r, g, b; | ||
} Vertex; | } Vertex; | ||
− | </ | + | </source> |
Dieser Typ besteht aus zwei Teilen: Einmal 3 Floats für die Koordinate und einmal 3 Floats für die Farbe. | Dieser Typ besteht aus zwei Teilen: Einmal 3 Floats für die Koordinate und einmal 3 Floats für die Farbe. | ||
Das Vertexarray besteht eben aus einem Array mit diesem Typen. Bei einer Zeile sind die ersten drei Zahlen die Koordinate und die nächsten drei die Farbe | Das Vertexarray besteht eben aus einem Array mit diesem Typen. Bei einer Zeile sind die ersten drei Zahlen die Koordinate und die nächsten drei die Farbe | ||
− | <cpp> | + | <source lang="cpp"> |
static const Vertex vertices[18] = { | static const Vertex vertices[18] = { | ||
0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, | 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, | ||
Zeile 56: | Zeile 52: | ||
2.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f | 2.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f | ||
}; | }; | ||
− | </ | + | </source> |
Nun müssen noch die Indices aufgebaut werden: | Nun müssen noch die Indices aufgebaut werden: | ||
GLuint *indices; | GLuint *indices; | ||
− | <cpp> | + | <source lang="cpp"> |
static const GLuint indices[8] = { | static const GLuint indices[8] = { | ||
0, 2, 3, 1, 2, 4, 5, 3 | 0, 2, 3, 1, 2, 4, 5, 3 | ||
}; | }; | ||
− | static int indexCount = 8; | + | static const int indexCount = 8; |
− | </ | + | </source> |
Damit hätten wir alle Daten beisammen. Nun muss nur noch OpenGL davon wissen. | Damit hätten wir alle Daten beisammen. Nun muss nur noch OpenGL davon wissen. | ||
− | <cpp> | + | <source lang="cpp"> |
void initVertexArray(void) { | void initVertexArray(void) { | ||
glEnableClientState(GL_VERTEX_ARRAY); | glEnableClientState(GL_VERTEX_ARRAY); | ||
Zeile 79: | Zeile 75: | ||
glColorPointer (3, GL_FLOAT, sizeof(Vertex), &(vertices[0].r)); | glColorPointer (3, GL_FLOAT, sizeof(Vertex), &(vertices[0].r)); | ||
} | } | ||
− | </ | + | </source> |
Was geschieht hier?<br> | Was geschieht hier?<br> | ||
Als erstes wird das Vertex-Array eingeschaltet sowie das zugehörige Farbarray. | Als erstes wird das Vertex-Array eingeschaltet sowie das zugehörige Farbarray. | ||
Zeile 87: | Zeile 83: | ||
====Das eigentliche Zeichnen==== | ====Das eigentliche Zeichnen==== | ||
Nun, da alles initialisiert ist, sollen die Quads auch auf den Bildschirm. Dazu folgende Funktion: | Nun, da alles initialisiert ist, sollen die Quads auch auf den Bildschirm. Dazu folgende Funktion: | ||
− | <cpp> | + | <source lang="cpp"> |
void drawVertexArray(void) { | void drawVertexArray(void) { | ||
glDrawElements(GL_QUADS, /* Primitivtyp */ | glDrawElements(GL_QUADS, /* Primitivtyp */ | ||
Zeile 94: | Zeile 90: | ||
indices); /*Index-Array*/ | indices); /*Index-Array*/ | ||
} | } | ||
− | </ | + | </source> |
− | Als erstes wird gesagt, dass die gezeichneten Elemente Quads sind. Dann kommt die Anzahl der Indices sowie deren Typ. Als letztes das eigentliche Indexarray. | + | Als erstes wird gesagt, dass die gezeichneten Elemente Quads sind. Dann kommt die Anzahl der Indices sowie deren Typ. Als letztes das eigentliche Indexarray.<br> |
+ | Das Ergebnis sollte nun so aussehen:<br> | ||
+ | [[Bild:Varray.png]] | ||
+ | |||
+ | ====Die kompletten C-Dateien==== | ||
+ | Der Übersicht halber die komplette vertexarray.h: | ||
+ | <source lang="cpp"> | ||
+ | #ifndef _VERTEXARRAY_H | ||
+ | #define _VERTEXARRAY_H | ||
+ | |||
+ | void initVertexArray(void); | ||
+ | |||
+ | void drawVertexArray(void); | ||
+ | |||
+ | #endif | ||
+ | </source> | ||
+ | Und die vertexarray.c: | ||
+ | <source lang="cpp"> | ||
+ | #include "vertexArray.h" | ||
+ | |||
+ | #include <GL/glut.h> | ||
+ | |||
+ | typedef struct { | ||
+ | GLfloat x, y, z; | ||
+ | GLfloat r, g, b; | ||
+ | } Vertex; | ||
+ | |||
+ | static const Vertex vertices[18] = { | ||
+ | 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, | ||
+ | 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, | ||
+ | 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, | ||
+ | 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, | ||
+ | 2.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, | ||
+ | 2.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f | ||
+ | }; | ||
+ | |||
+ | static const GLuint indices[8] = { | ||
+ | 0, 2, 3, 1, 2, 4, 5, 3 | ||
+ | }; | ||
+ | |||
+ | static int indexCount = 8; | ||
+ | |||
+ | void initVertexArray(void) { | ||
+ | glEnableClientState(GL_VERTEX_ARRAY); | ||
+ | glEnableClientState(GL_COLOR_ARRAY); | ||
+ | glVertexPointer(3, /* Komponenten pro Vertex (x,y,z) */ | ||
+ | GL_FLOAT, /* Typ der Komponenten */ | ||
+ | sizeof(Vertex), /* Offset zwischen 2 Vertizes im Array */ | ||
+ | &(vertices[0].x)); /* Zeiger auf die 1. Komponente */ | ||
+ | |||
+ | glColorPointer (3, GL_FLOAT, sizeof(Vertex), &(vertices[0].r)); | ||
+ | } | ||
+ | |||
+ | void drawVertexArray(void) { | ||
+ | glDrawElements(GL_QUADS, /* Primitivtyp */ | ||
+ | indexCount, /* Anzahl Indizes */ | ||
+ | GL_UNSIGNED_INT, /* Typ der Indizes */ | ||
+ | indices); /* Index-Array */ | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | ===Hinweis zu dem Array=== | ||
+ | In dem Minimalbeispiel wurde für Vertices und Farben das selbe Array verwendet. Meistens ist es sinnvoller, das in verschiedenen Arrays zu halten. Die entsprechenden Aufrufe, die OpenGL die Pointer mitteilen, müssen dann natürlich entsprechend angepasst werden. | ||
+ | |||
+ | ==Weiterführendes== | ||
+ | |||
+ | ===Weitere Pointer=== | ||
+ | Bisher wurden nur Vertices und Farben als Array benutzt. Nützlich sind noch Pointer auf Normale und Texturkoordinaten. Dazu müssen die entsprechenden Clientstates aktiviert werden: | ||
+ | <source lang="cpp"> | ||
+ | glEnableClientState(GL_NORMAL_ARRAY); | ||
+ | glEnableClientState(GL_TEXTURE_COORD_ARRAY); | ||
+ | </source> | ||
+ | OpenGL muss natürlich im Init entsprechende Pointer mittels [[glNormalPointer]] und [[glTexCoordPointer]] bekommen. | ||
+ | Die entsprechenden Werte funktionieren genau so wie bei dem Colorpointer.<br> | ||
+ | Desweiteren gibt es noch diese Pointer: | ||
+ | *[[glFogCoordPointer]] setzt einen Pointer auf Nebelkoordinaten. | ||
+ | *[[glSecondaryColorPointer]] setzt einen Pointer auf Sekundärfarben, siehe [[glSecondaryColor]]. | ||
+ | |||
+ | ===Weitere Zeichenfunktionen=== | ||
+ | Neben [[glDrawElements]] gibt es noch weitere Funktionen, um ein Vertexarray zu zeichnen. | ||
+ | *[[glDrawRangeElements]] funktioniert wie [[glDrawElements]], jedoch kann hier ein Start- und Endindex angegeben werden. | ||
+ | *[[glArrayElement]] zeichnet ein einzelnes Element des Vertexarrays. | ||
+ | *[[glDrawArrays]] zeichnet ein komplettes Vertexarray ohne Indexarray. | ||
+ | *[[glMultiDrawArrays]] funktioniert ähnlich wie [[glDrawArrays]], nimmt aber Arrays für die Start- und Endwerte und arbeitet die so ab, als ob [[glDrawArrays]] entsprechend in einer Schleife aufgerufen werden würde. | ||
+ | *[[glMultiDrawElements]]funktioniert ähnlich wie [[glDrawElements]], nimmt aber statt eines Indexarrays ein Array von Indexarrays und arbeitet es so ab, als ob [[glDrawElements]]in einer Schleife aufgerufen werden würde. | ||
+ | |||
+ | ===Anwendungshinweise=== | ||
+ | Vertex-Arrays sind zwar weitaus besser als der Immediate-Mode mit [[glBegin]]/[[glEnd]], aber noch nicht das Wahre. Weitaus performanter sind Vertexbufferobjekte. Sie arbeiten ähnlich wie Vertexarrays, bloß dass die Daten nicht im Hauptspeicher liegen, sondern direkt im Grafikkartenspeicher. Es gibt im Wiki hier ein Tutorial dazu: [[Tutorial_Vertexbufferobject]] |
Aktuelle Version vom 10. März 2009, 19:48 Uhr
Bei einem Vertexarray werden die Vertexdaten in einem Array zur Grafikkarte geschickt.
Inhaltsverzeichnis
Was sind Vertexarrays
Ein Vertexarray ist ein großes Array, in dem die Vertexdaten stehen. Idealerweise existiert dabei jeder Vertex nur einmal in diesem Array. Daneben wird noch ein Indexarray erstellt, dass auf die Vertices in dem Vertexarray verweist. Anhand dieses Indexarrays wird gezeichnet. Natürlich können auf einzelne Vertices aus dem Indexarray heraus mehrfach verwiesen werden.
Wie helfen Vertexarrays bei der Performance
Dadurch, dass alle Daten an einem Block liegen und die GPU stumpf die Daten abarbeiten kann, sind die Vertexarrays weitaus schneller als die glBegin/glEnd-Methode. Werden die Daten dann auch noch als Displayliste an die Grafikkarte geschickt, kommt Freude auf. :) Außerdem wird Speicher gespart, indem durch die Indirektstufe, die man durch das Indexarray hat, einen Vertex mehrfach zeichnen kann.
Verwendung
Prinzip
Wie wird nun so ein Vertexarray aufgebaut und gezeichnet?
Beispiel, wie es intern abläuft für zwei nebeneinanderliegende 2D-Quads:
Vertexarray vertices: {(0.0f, 0.0f), (0.0f, 1.0f), (1.0f, 0.0f), (1.0f, 1.0f), (2.0f, 0.0f), (2.0f, 1.0f)}
Indexarray indices: {0, 2, 3, 1, 2, 4, 5, 3}
Beim Zeichnen wird nun folgendermaßen vorgegangen:
int vertexCount = length(indices);
glBegin(GL_QUADS);
for (int i = 0; i < vertexCount; ++i) {
int vIndex = indices[i];
Vertex2F v = vertices[vIndex];
glVertex2f(v[0], v[1]);
}
glEnd();
Dieses Minimalbeispiel soll nur verdeutlichen, wie Vertexarrays prinzipiell funktionieren. Pro Vertex gibt es natürlich noch andere interessante Informationen wie dessen Normale, dessen Farbe und Texturkoordinate. Dazu später mehr.
Umsetzung in C
Das Beispiel von eben soll nun in wirklichen Code umgesetzt werden. Dazu sind zwei Schritte nötig:
1. Die Initialisierung.
2. Das eigentliche Zeichnen
Die Initialisierung
Als erstes definieren wir einen Vertex:
typedef struct {
GLfloat x, y, z;
GLfloat r, g, b;
} Vertex;
Dieser Typ besteht aus zwei Teilen: Einmal 3 Floats für die Koordinate und einmal 3 Floats für die Farbe. Das Vertexarray besteht eben aus einem Array mit diesem Typen. Bei einer Zeile sind die ersten drei Zahlen die Koordinate und die nächsten drei die Farbe
static const Vertex vertices[18] = {
0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
2.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
2.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f
};
Nun müssen noch die Indices aufgebaut werden: GLuint *indices;
static const GLuint indices[8] = {
0, 2, 3, 1, 2, 4, 5, 3
};
static const int indexCount = 8;
Damit hätten wir alle Daten beisammen. Nun muss nur noch OpenGL davon wissen.
void initVertexArray(void) {
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(3, /* Komponenten pro Vertex (x,y,z) */
GL_FLOAT, /* Typ der Komponenten */
sizeof(Vertex), /* Offset zwischen 2 Vertizes im Array */
&(vertices[0].x)); /* Zeiger auf die 1. Komponente */
glColorPointer (3, GL_FLOAT, sizeof(Vertex), &(vertices[0].r));
}
Was geschieht hier?
Als erstes wird das Vertex-Array eingeschaltet sowie das zugehörige Farbarray.
Dann wird als erstes der Vertexpointer gesetzt. Der erste Parameter sagt, dass es 3 Komponenten pro Vertex sind (x, y, z), der zweite, dass es sich um Floats handelt, der dritte der Offset zwischen zwei Vertices im Array (Wie viele Bytes er überspringen muss um zum nächsten Vertex zu gelangen) und der Dritte ist ein Pointer auf das erste Vertex.
Genau so sieht der Colorpointer aus.
Das eigentliche Zeichnen
Nun, da alles initialisiert ist, sollen die Quads auch auf den Bildschirm. Dazu folgende Funktion:
void drawVertexArray(void) {
glDrawElements(GL_QUADS, /* Primitivtyp */
indexCount, /* Anzahl Indizes */
GL_UNSIGNED_INT, /* Typ der Indizes */
indices); /*Index-Array*/
}
Als erstes wird gesagt, dass die gezeichneten Elemente Quads sind. Dann kommt die Anzahl der Indices sowie deren Typ. Als letztes das eigentliche Indexarray.
Das Ergebnis sollte nun so aussehen:
Die kompletten C-Dateien
Der Übersicht halber die komplette vertexarray.h:
#ifndef _VERTEXARRAY_H
#define _VERTEXARRAY_H
void initVertexArray(void);
void drawVertexArray(void);
#endif
Und die vertexarray.c:
#include "vertexArray.h"
#include <GL/glut.h>
typedef struct {
GLfloat x, y, z;
GLfloat r, g, b;
} Vertex;
static const Vertex vertices[18] = {
0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
2.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
2.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f
};
static const GLuint indices[8] = {
0, 2, 3, 1, 2, 4, 5, 3
};
static int indexCount = 8;
void initVertexArray(void) {
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glVertexPointer(3, /* Komponenten pro Vertex (x,y,z) */
GL_FLOAT, /* Typ der Komponenten */
sizeof(Vertex), /* Offset zwischen 2 Vertizes im Array */
&(vertices[0].x)); /* Zeiger auf die 1. Komponente */
glColorPointer (3, GL_FLOAT, sizeof(Vertex), &(vertices[0].r));
}
void drawVertexArray(void) {
glDrawElements(GL_QUADS, /* Primitivtyp */
indexCount, /* Anzahl Indizes */
GL_UNSIGNED_INT, /* Typ der Indizes */
indices); /* Index-Array */
}
Hinweis zu dem Array
In dem Minimalbeispiel wurde für Vertices und Farben das selbe Array verwendet. Meistens ist es sinnvoller, das in verschiedenen Arrays zu halten. Die entsprechenden Aufrufe, die OpenGL die Pointer mitteilen, müssen dann natürlich entsprechend angepasst werden.
Weiterführendes
Weitere Pointer
Bisher wurden nur Vertices und Farben als Array benutzt. Nützlich sind noch Pointer auf Normale und Texturkoordinaten. Dazu müssen die entsprechenden Clientstates aktiviert werden:
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
OpenGL muss natürlich im Init entsprechende Pointer mittels glNormalPointer und glTexCoordPointer bekommen.
Die entsprechenden Werte funktionieren genau so wie bei dem Colorpointer.
Desweiteren gibt es noch diese Pointer:
- glFogCoordPointer setzt einen Pointer auf Nebelkoordinaten.
- glSecondaryColorPointer setzt einen Pointer auf Sekundärfarben, siehe glSecondaryColor.
Weitere Zeichenfunktionen
Neben glDrawElements gibt es noch weitere Funktionen, um ein Vertexarray zu zeichnen.
- glDrawRangeElements funktioniert wie glDrawElements, jedoch kann hier ein Start- und Endindex angegeben werden.
- glArrayElement zeichnet ein einzelnes Element des Vertexarrays.
- glDrawArrays zeichnet ein komplettes Vertexarray ohne Indexarray.
- glMultiDrawArrays funktioniert ähnlich wie glDrawArrays, nimmt aber Arrays für die Start- und Endwerte und arbeitet die so ab, als ob glDrawArrays entsprechend in einer Schleife aufgerufen werden würde.
- glMultiDrawElementsfunktioniert ähnlich wie glDrawElements, nimmt aber statt eines Indexarrays ein Array von Indexarrays und arbeitet es so ab, als ob glDrawElementsin einer Schleife aufgerufen werden würde.
Anwendungshinweise
Vertex-Arrays sind zwar weitaus besser als der Immediate-Mode mit glBegin/glEnd, aber noch nicht das Wahre. Weitaus performanter sind Vertexbufferobjekte. Sie arbeiten ähnlich wie Vertexarrays, bloß dass die Daten nicht im Hauptspeicher liegen, sondern direkt im Grafikkartenspeicher. Es gibt im Wiki hier ein Tutorial dazu: Tutorial_Vertexbufferobject