Vertexarray: Unterschied zwischen den Versionen

Aus DGL Wiki
Wechseln zu: Navigation, Suche
(Hinweis auf VBOs)
K (Der Ausdruck ''<cpp>(.*?)</cpp>'' wurde ersetzt mit ''<source lang="cpp">$1</source>''.)
 
Zeile 17: 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 26: Zeile 26:
 
}
 
}
 
glEnd();
 
glEnd();
</cpp>
+
</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 35: 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;
</cpp>
+
</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 52: 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
 
};
 
};
</cpp>
+
</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
Zeile 61: Zeile 61:
  
 
static const int indexCount = 8;
 
static const int indexCount = 8;
</cpp>
+
</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 75: Zeile 75:
 
glColorPointer (3, GL_FLOAT, sizeof(Vertex), &(vertices[0].r));
 
glColorPointer (3, GL_FLOAT, sizeof(Vertex), &(vertices[0].r));
 
}
 
}
</cpp>
+
</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 83: 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 90: Zeile 90:
 
indices); /*Index-Array*/
 
indices); /*Index-Array*/
 
}
 
}
</cpp>
+
</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>
 
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>
 
Das Ergebnis sollte nun so aussehen:<br>
Zeile 97: Zeile 97:
 
====Die kompletten C-Dateien====
 
====Die kompletten C-Dateien====
 
Der Übersicht halber die komplette vertexarray.h:
 
Der Übersicht halber die komplette vertexarray.h:
<cpp>
+
<source lang="cpp">
 
#ifndef _VERTEXARRAY_H
 
#ifndef _VERTEXARRAY_H
 
#define _VERTEXARRAY_H
 
#define _VERTEXARRAY_H
Zeile 106: Zeile 106:
  
 
#endif
 
#endif
</cpp>
+
</source>
 
Und die vertexarray.c:
 
Und die vertexarray.c:
<cpp>
+
<source lang="cpp">
 
#include "vertexArray.h"
 
#include "vertexArray.h"
  
Zeile 150: Zeile 150:
 
indices); /* Index-Array */
 
indices); /* Index-Array */
 
}
 
}
</cpp>
+
</source>
  
 
===Hinweis zu dem Array===
 
===Hinweis zu dem Array===
Zeile 159: Zeile 159:
 
===Weitere Pointer===
 
===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:
 
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:
<cpp>
+
<source lang="cpp">
 
glEnableClientState(GL_NORMAL_ARRAY);
 
glEnableClientState(GL_NORMAL_ARRAY);
 
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
</cpp>
+
</source>
 
OpenGL muss natürlich im Init entsprechende Pointer mittels [[glNormalPointer]] und [[glTexCoordPointer]] bekommen.
 
OpenGL muss natürlich im Init entsprechende Pointer mittels [[glNormalPointer]] und [[glTexCoordPointer]] bekommen.
 
Die entsprechenden Werte funktionieren genau so wie bei dem Colorpointer.<br>
 
Die entsprechenden Werte funktionieren genau so wie bei dem Colorpointer.<br>

Aktuelle Version vom 10. März 2009, 19: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