Vertexarray: Unterschied zwischen den Versionen

Aus DGL Wiki
Wechseln zu: Navigation, Suche
K (Der Ausdruck ''<cpp>(.*?)</cpp>'' wurde ersetzt mit ''<source lang="cpp">$1</source>''.)
 
(9 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
{{Offline}}
+
Bei einem Vertexarray werden die Vertexdaten in einem Array zur Grafikkarte geschickt.
 
 
===Vertexarray===
 
  
 
==Was sind Vertexarrays==
 
==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==
 
==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==
 
==Verwendung==
 +
 +
===Prinzip===
 +
Wie wird nun so ein Vertexarray aufgebaut und gezeichnet?<br>
 +
Beispiel, wie es intern abläuft für zwei nebeneinanderliegende 2D-Quads:<br>
 +
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)}<br>
 +
Indexarray indices: {0, 2, 3, 1, 2, 4, 5, 3}<br>
 +
Beim Zeichnen wird nun folgendermaßen vorgegangen:
 +
<source lang="cpp">
 +
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();
 +
</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.
 +
 +
===Umsetzung in C===
 +
Das Beispiel von eben soll nun in wirklichen Code umgesetzt werden. Dazu sind zwei Schritte nötig:<br>
 +
1. Die Initialisierung.
 +
2. Das eigentliche Zeichnen
 +
====Die Initialisierung====
 +
Als erstes definieren wir einen Vertex:
 +
<source lang="cpp">
 +
typedef struct {
 +
GLfloat x, y, z;
 +
GLfloat r, g, b;
 +
} Vertex;
 +
</source>
 +
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
 +
<source lang="cpp">
 +
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
 +
};
 +
</source>
 +
Nun müssen noch die Indices aufgebaut werden:
 +
GLuint *indices;
 +
<source lang="cpp">
 +
static const GLuint indices[8] = {
 +
0, 2, 3, 1, 2, 4, 5, 3
 +
};
 +
 +
static const int indexCount = 8;
 +
</source>
 +
Damit hätten wir alle Daten beisammen. Nun muss nur noch OpenGL davon wissen.
 +
<source lang="cpp">
 +
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));
 +
}
 +
</source>
 +
Was geschieht hier?<br>
 +
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:
 +
<source lang="cpp">
 +
void drawVertexArray(void) {
 +
glDrawElements(GL_QUADS, /* Primitivtyp */
 +
indexCount, /* Anzahl Indizes */
 +
GL_UNSIGNED_INT, /* Typ der Indizes */
 +
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.<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, 20:48 Uhr

Bei einem Vertexarray werden die Vertexdaten in einem Array zur Grafikkarte geschickt.

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:
Varray.png

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:

Weitere Zeichenfunktionen

Neben glDrawElements gibt es noch weitere Funktionen, um ein Vertexarray zu zeichnen.

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