opengl
Texturierung
Suche…
Grundlagen der Texturierung
Eine Textur ist eine Form der Datenspeicherung, die einen bequemen Zugriff nicht nur auf bestimmte Dateneinträge ermöglicht, sondern auch auf Punkte, die mehrere Einträge vermischen (interpolieren).
In OpenGL können Texturen für viele Zwecke verwendet werden, meistens wird jedoch ein Bild einem Polygon (z. B. einem Dreieck) zugeordnet. Um die Textur einem Dreieck (oder einem anderen Polygon) zuzuordnen, müssen wir jedem Scheitelpunkt sagen, welchem Teil der Textur er entspricht. Wir weisen jedem Scheitelpunkt eines Polygons eine Texturkoordinate zu, die dann zwischen allen Fragmenten in diesem Polygon interpoliert wird. Texturkoordinaten liegen in der Regel zwischen 0 und 1 in der X- und Y-Achse, wie in der folgenden Abbildung dargestellt:
Die Texturkoordinaten dieses Dreiecks würden folgendermaßen aussehen:
GLfloat texCoords[] = {
0.0f, 0.0f, // Lower-left corner
1.0f, 0.0f, // Lower-right corner
0.5f, 1.0f // Top-center corner
};
Setzen Sie diese Koordinaten in VBO (Vertex Buffer Object) und erstellen Sie ein neues Attribut für den Shader. Sie sollten bereits mindestens ein Attribut für die Stützpunktpositionen haben, erstellen Sie ein anderes für die Texturkoordinaten.
Textur erzeugen
Zuerst müssen Sie ein Texturobjekt generieren, auf das eine ID verweist, die in einer vorzeichenlosen int- Textur gespeichert wird:
GLuint texture;
glGenTextures(1, &texture);
Danach muss es gebunden werden, damit alle nachfolgenden Texturbefehle diese Textur konfigurieren:
glBindTexture(GL_TEXTURE_2D, texture);
Bild wird geladen
Um ein Image zu laden, können Sie einen eigenen Image Loader erstellen oder eine Image-Ladebibliothek wie SOIL (Simple OpenGL Image Library) in c ++ oder TWL PNGDecoder in Java verwenden.
Ein Beispiel für das Laden eines Bildes mit SOIL wäre:
int width, height;
unsigned char* image = SOIL_load_image("image.png", &width, &height, 0, SOIL_LOAD_RGB);
Nun können Sie dieses Bild dem Texturobjekt zuordnen:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
Danach sollten Sie das Texturobjekt lösen:
glBindTexture(GL_TEXTURE_2D, 0);
Umbruchparameter für Texturkoordinaten
Wie oben zu sehen ist, hat die untere linke Ecke der Textur die UV-Koordinaten (st) (0, 0) und die obere rechte Ecke der Textur die Koordinaten (1, 1), aber die Texturkoordinaten eines Netzes können darin liegen beliebiger Bereich Um damit umgehen zu können, muss definiert werden, wie die Texturkoordinaten in die Textur eingefügt werden.
Der Umbruchparameter für die Texturkoordinate kann mit glTextureParameter mit GL_TEXTURE_WRAP_S
, GL_TEXTURE_WRAP_T
und GL_TEXTURE_WRAP_R
.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Die möglichen Parameter sind:
GL_CLAMP_TO_EDGE
bewirkt, dass die Texturkoordinaten auf den Bereich [1 / 2N, 1 - 1 / 2N] geklemmt werden, wobei N die Größe der Textur in der Richtung ist.GL_CLAMP_TO_BORDER
macht dasselbe wieGL_CLAMP_TO_EDGE
, aber in Fällen, in denen das Klemmen erfolgt, werden die abgerufenenGL_TEXTURE_BORDER_COLOR
durch die vonGL_TEXTURE_BORDER_COLOR
angegebene FarbeGL_TEXTURE_BORDER_COLOR
.GL_REPEAT
bewirkt, dass der ganzzahlige Teil der Texturkoordinate ignoriert wird. Die Textur ist gefliest .
-
GL_MIRRORED_REPEAT
: Wenn der ganzzahlige Teil der Texturkoordinate gerade ist, wird er ignoriert. Wenn der ganzzahlige Teil der Texturkoordinate ungerade ist, wird die Texturkoordinate auf 1 - Frac (s) gesetzt . fract (s) ist der gebrochene Teil der Texturkoordinate. Dadurch wird die Textur jedes zweite Mal gespiegelt .
-
GL_MIRROR_CLAMP_TO_EDGE
bewirkt, dass die Textue-Koordinate wie fürGL_MIRRORED_REPEAT
für eine Wiederholung der Textur wiederholt wird, wobei die Koordinate wie inGL_CLAMP_TO_EDGE
festgeklemmtGL_CLAMP_TO_EDGE
.
Notieren Sie den Standardwert für GL_TEXTURE_WRAP_S
, GL_TEXTURE_WRAP_T
und GL_TEXTURE_WRAP_R
ist GL_REPEAT
.
Texturen anwenden
Als letztes müssen Sie die Textur vor dem Zeichnungsaufruf binden:
glBindTexture(GL_TEXTURE_2D, texture);
Textur und Framebuffer
Sie können ein Bild in einer Textur an einen Framebuffer anhängen, sodass Sie direkt auf diese Textur rendern können.
glGenFramebuffers (1, &framebuffer);
glBindFramebuffer (GL_FRAMEBUFFER, framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
texture,
0);
Hinweis: In derselben Renderaufgabe können Sie nicht von derselben Textur lesen und schreiben, da undefiniertes Verhalten aufgerufen wird. Sie können jedoch glTextureBarrier()
verwenden: glTextureBarrier()
zwischen glTextureBarrier()
.
Texturdaten lesen
Mit der Funktion glGetTexImage
können Sie glGetTexImage
:
char *outBuffer = malloc(buf_size);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glGetTexImage(GL_TEXTURE_2D,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
outBuffer);
Hinweis: Texturart und -format sind nur beispielhaft und können unterschiedlich sein.
PBOs verwenden
Wenn Sie einen Puffer binden GL_PIXEL_UNPACK_BUFFER
die dann data
Parameter in glTexImage2D
wird ein Offset in diesem Puffer.
Dies bedeutet, dass das glTexImage2D nicht warten muss, bis alle Daten aus dem Anwendungsspeicher der Anwendung kopiert wurden, bevor es zurückkehren kann, wodurch der Aufwand im Haupt-Thread reduziert wird.
glGenBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glBufferData(GL_PIXEL_UNPACK_BUFFER, width*height*3, NULL, GL_STREAM_DRAW);
void* mappedBuffer = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
//write data into the mapped buffer, possibly in another thread.
int width, height;
unsigned char* image = SOIL_load_image("image.png", &width, &height, 0, SOIL_LOAD_RGB);
memcpy(mappedBuffer, image, width*height*3);
SOIL_free_image(image);
// after reading is complete back on the main thread
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
PBOs leuchten jedoch vor allem dann, wenn Sie das Ergebnis eines Renderings in den Anwendungsspeicher zurücklesen müssen. Um Pixeldaten in einen Puffer zu lesen, binden Sie sie an GL_PIXEL_PACK_BUFFER
Der Datenparameter von glGetTexImage
wird dann in diesen Puffer versetzt:
glGenBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
glBufferData(GL_PIXEL_PACK_BUFFER, buf_size, NULL, GL_STREAM_COPY);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glGetTexImage(GL_TEXTURE_2D,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
null);
//ensure we don't try and read data before the transfer is complete
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
// then regularly check for completion
GLint result;
glGetSynciv(sync, GL_SYNC_STATUS, sizeof(result), NULL, &result);
if(result == GL_SIGNALED){
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
void* mappedBuffer = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
//now mapped buffer contains the pixel data
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
}
Verwenden von Texturen in GLSL-Shadern
Der Vertex-Shader akzeptiert nur die Texturkoordinaten als Vertex-Attribut und leitet die Koordinaten an den Fragment-Shader weiter. Standardmäßig wird auch garantiert, dass das Fragment basierend auf seiner Position in einem Dreieck die richtig interpolierte Koordinate erhält:
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texCoordIn;
out vec2 texCoordOut;
void main()
{
gl_Position = vec4(position, 1.0f);
texCoordOut = texCoordIn;
}
Der Fragment-Shader akzeptiert dann die texCoord
als Eingabevariable. Sie können dann dem Fragment-Shader eine Textur hinzufügen, indem Sie einen uniform sampler2D
. Ein Fragment der Textur probieren wir eine integrierte Funktion verwenden texture
, die zwei Parameter. Der erste ist die Textur, von der wir probieren möchten, und der zweite ist die Koordinate dieser Textur:
in vec2 texCoordOut;
out vec4 color;
uniform sampler2D image;
void main()
{
color = texture(image, texCoordOut);
}
Beachten Sie, dass das image
hier nicht die direkte Textur-ID ist. Es ist die ID der Textureinheit , die abgetastet wird. Texturen wiederum sind nicht direkt an Programme gebunden. Sie sind an Textureinheiten gebunden. Dies wird erreicht, indem zuerst die Textureinheit mit glActiveTexture
wird und dann der Aufruf von glBindTexture
diese bestimmte Textureinheit beeinflusst. Da die Standard-Textureinheit jedoch Textureinheit 0
, können Programme, die eine Textur verwenden, einfacher gemacht werden, indem dieser Aufruf weggelassen wird.