Tutorial MultiTexturing

Aus DGL Wiki
Wechseln zu: Navigation, Suche

Tutorial Multitexturing

Einleitung

Mit der Markteinführung von NVidias „Riva TNT“ (TwiNTexel Engine) hat auch das Multitexturing im PC-Grafikkartenbereich Einzug gefunden, und wird seit einigen Jahren von allen neu entwickelten Grafikchips unterstützt. Beim Multitexturing können den einzelnen Textureinheiten der Grafikkarte unterschiedliche Texturen zugeordnet werden, die dann später in einem Durchgang auf ein Polygon tapeziert bzw. verknüpft werden. Vor der Einführung dieser Technik musste man das Polygon im ersten Durchgang mit Textur A rendern und in einem zweiten Durchgang Blending aktivieren und es mit Textur B zeichnen. Dadurch verdoppelte sich nicht nur die Anzahl der Polygone, sondern auch die Verknüpfungsmöglichkeiten des Blendings waren weit weniger nützlich als die beim Multitexturing.

Die Anzahl der Textureinheiten (TMU = Textur Mapping Unit) gibt an, wie viele Texturen in einem Durchgang auf eine Textur gezeichnet werden können. Bei einer „GeForce4“ sind dies z. B. 4 und bei den „Kyro“-Karten von PowerVR sogar bis zu 8 (obwohl man eher selten 8 Texturstufen benötigt). Besitzt eine Grafikkarte nun weniger TMUs als man gleichzeitig Texturen auf ein Polygon zeichnen möchte, dann wird die Arbeit in mehreren Durchgängen erledigt. Ein Grafikchip der nur 2 TMUs besitzt, braucht also 3 Durchgänge um ein Polygon mit 6 Texturen zu zeichnen.

Multitexturing ist heute besonders in Spielen ein unverzichtbares Mittel geworden. Dabei werden zwei Anwendungsmöglichkeiten häufig genutzt: Zum einen das Lightmap, bei dem Schattenwurf und Lichteinfall auf ein Polygon vorberechnet und in einer separaten Textur abgelegt werden, sowie das Detailtexturing, bei dem die Basistextur mit einem Muster überzogen wird, das kleine Details simulieren soll.

Im Falle des Beispielprogramms werden diese beiden Texturen mit Hilfe des Multitexturings mit der Basistextur verknüpft:

Schritt1
Basistextur
+
Lightmap
=
Zwischenergebnis
Schritt2
Zwischenergebnis
+
Detailtextur
=
Endergebnis

Schritt 1 - Hardware auf Multitexturing-Fähigkeit prüfen

Bevor man das Multitexturing nutzen kann, sollte man natürlich erst einmal prüfen ob dies auch von der Hardware unterstützt wird. Es wird zwar kaum noch Grafikkarten geben, bei denen dies nicht der Fall ist, aber um sicherzugehen, ist dieser Schritt doch nötig.

Dabei hat man zwei einfache Möglichkeiten. Entweder man prüft, ob die Erweiterung GL_ARB_multitexture von der Grafikkarte untersützt wird, oder man liest die Anzahl der TMUs mit dem Befehl glGetIntegerv und dem Parameter GL_MAX_TEXTURE_UNITS_ARB aus [06]. Ist dieser Wert kleiner als zwei, so wird das Multitexturing nicht unterstützt.

[01]  procedure TForm1.GLInit;
[02]  var
[03]     TMUs  : Integer;
[04]  begin
[05]       ActivateRenderingContext(FDC, FRC);
[06]       glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, @TMUs);
[07]       if TMUs < 2 then
[08]       begin
[09]            ShowMessage('Sorry!Your card doesn''t support Multitexturing');
[10]            Close;
[11]       end;
...
[12]  end;

Schritt 2 - Texturstufen festlegen

Hat man sich nun von den Multitexture-Fähigkeiten der Grafikkarte überzeugt, kann man endlich damit beginnen, die einzelnen Texturen ihren Texturstufen zuzuordnen. Auf die Texturstufen wird in OpenGL mit Hilfe der Konstanten GL_TEXTURE0...GL_TEXTURE31 zugegriffen.

[01]  procedure DrawRoom;
[02]  begin
...
[03]       glActiveTexture(GL_TEXTURE1);
[04]       if TSActive[0] then
[05]       begin
[06]            glEnable(GL_TEXTURE_2D);
[07]            Texture[2].Bind;
[08]            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
[09]       end
[10]       else
[11]           glDisable(GL_TEXTURE_2D);

[12]       glActiveTexture(GL_TEXTURE2);
[13]       if TSActive[1] then
[14]       begin
[15]            glEnable(GL_TEXTURE_2D);
[16]            Texture[4].Bind;
[17]            glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
[18]       end
[19]       else
[20]           glDisable(GL_TEXTURE_2D);

[21]       glActiveTexture(GL_TEXTURE0);
[22]       glEnable(GL_TEXTURE_2D);
[23]       glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

Mit der Funktion glActiveTexture(target : Cardinal) wird die im Parameter target angegebene Texturstufe aktiviert, Zeile [03], [12] und [21]. Von nun an beziehen sich alle Befehle, die etwas an einer Textur verändern (Binden von Texturen, Blending, Alphamasking, Veränderung der Texturmatrix, etc.) auf diese aktivierte Texturstufe, und zwar solange, bis auf ihr das Texturing deaktiviert wird oder die Texturstufe gewechselt wird.

Um nun die Texturstufe zu aktiveren, muss wie gewohnt mit dem Befehl glEnable und dem Parameter GL_TEXTURE_2D das 2D-Texturing aktiviert werden. Dies muss für jede Texturstufe geschehen, die genutzt wird, Zeile [06], [15] und [22]. Möchte man eine Texturstufe ausschalten, so wählt man diese aus und nutzt glDisable mit dem Parameter GL_TEXTURE_2D, Zeile [11] und [20].

Bevor nun die Polygone gezeichnet werden, muss für jede Texturstufe die Art der Verknüpfung mit den anderen Texturstufen festgelegt werden. Dies geschieht mit dem Befehl glTexEnvi, der als Parameter das Ziel des Parameters (in unseren Falle GL_TEXTURE_ENV, was bedeutet, das als Grundwerte die Ausgabewerte der vorherigen Texturstufe genutzt werden), den Parameter selbst (auch GL_TEXTURE_ENV) und die Art der Verknüpfung erhält.

Für die Basistextur wird dieser Parameter auf GL_REPLACE gesetzt [23], alles was vor dieser Textur kam wird also überschrieben. Der Parameter GL_MODULATE für die Lightmap [08] und die Detailtextur [17] geben an, das deren Farbwerte mit den vorherigen Werten multipliziert werden. Neben diesen Verknüpfungen gibt es noch etliche andere, die jedoch weniger oft, oder für andere Anwendungen wie z. B. BumpMapping verwendet werden.

Schritt 3 - Übergabe der Texturkoordinaten

Nachdem die Texturstufen nun vorbereitet wurden, müssen für diese noch die Texturkoordinaten festgelegt werden. Wurde dies vorher mit der Funktion glTexCoord2f(s,t : single) erledigt, so wird nun die an das Multitexturing angepasste Variante glMultiTexCoord2f(target : cardinal;s,t : single) genutzt. Im Parameter target wird die Texturstufe gewählt und in s bzw. t wie gewohnt die Texturkoordinaten übergeben:

[01]  glActiveTexture(GL_TEXTURE1);
[02]  Texture[6].Bind;

[03]  glActiveTexture(GL_TEXTURE0);
[04]  Texture[0].Bind;

[05]  glBegin(GL_QUADS);
[06]    glMultiTexCoord2f(GL_TEXTURE0, 0, 0);
[07]    glMultiTexCoord2f(GL_TEXTURE1, 0, 0);
[08]    glMultiTexCoord2f(GL_TEXTURE2, 0, 0);
[09]      glVertex3f(px, py, pz);
[10]    glMultiTexCoord2f(GL_TEXTURE0, 0, 2);
[11]    glMultiTexCoord2f(GL_TEXTURE1, 0, 1);
[12]    glMultiTexCoord2f(GL_TEXTURE2, 0, 8);
[13]      glVertex3f(px, py, pz + pLength);
[14]    glMultiTexCoord2f(GL_TEXTURE0, 2, 2);
[15]    glMultiTexCoord2f(GL_TEXTURE1, 1, 1);
[16]    glMultiTexCoord2f(GL_TEXTURE2, 8, 8);
[17]      glVertex3f(px + pWidth, py, pz + pLength);
[18]    glMultiTexCoord2f(GL_TEXTURE0, 2, 0);
[19]    glMultiTexCoord2f(GL_TEXTURE1, 1, 0);
[20]    glMultiTexCoord2f(GL_TEXTURE2, 8, 0);
[21]      glVertex3f(px + pWidth, py, pz);
[22]  glEnd;

[23]  glActiveTexture(GL_TEXTURE2);
[24]  if TSActive[1] then
[25]  begin
[26]       glEnable(GL_TEXTURE_2D);
[27]       Texture[4].Bind;
[28]       glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
[29]  end
[30]  else
[31]      glDisable(GL_TEXTURE_2D);

[32]  glActiveTexture(GL_TEXTURE0);
[33]  glEnable(GL_TEXTURE_2D);
[34]  glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

Wie in dieser, aus dem Beispielprogramm entnommenen Prozedur sichtbar, werden vor dem Zeichnen des Quads zuerst die Texturen an die einzelnen Texturstufen gebunden. In [01] und [02] wird die Lightmap an die Texturstufe 1 gebunden und in [03] und [04] wird die Basistextur für diesen Quad an Texturstufe 0 gebunden. Da die an Texturstufe 2 gebundene Detailtextur bereits vorher definiert wurde und sich für diesen Quad nicht ändert, muss diese hier nicht gebunden werden.

Danach werden nur noch die Texturkoordinaten an jede genutzte Texturstufe für jeden Eckpunkt des Polygons übergeben um es mit den an die Stufen gebundenen Texturen zu überziehen.

Das Beispielprogramm

Das von mir zu diesem Tutorial geschriebene Beispielprogramm benutzt 3 Texturstufen. Texturstufe 0 für die Basistextur, Texturstufe 1 für die Lightmap und Texturstufe 2 für die Detailtextur. Die Texturstufen 1 und 2 können mit den entsprechenden Ziffern auf der Tastatur aktiviert bzw. deaktiviert werden. Mit der Maus wird der Blickwinkel verändert und mit Cursor Hoch/Runter kann man sich bewegen.

Die Lightmaps wurden von mir in einem herkömmlichen Zeichenprogramm erstellt, es steckt also kein Radiosity-Renderer dahinter.

Das Programm wurde unter Delphi 6.0 geschrieben, sollte jedoch ab Delphi 4.0 kompilierbar sein. Das Programm läuft natürlich nur auf Grafikkarten, die Multitexturing in der Hardware unterstützen. Dies ist jedoch seit der Riva TNT bei allen Karten der Fall.

Zu guter Letzt noch ein Screenshot aus dem Beispielprogramm:

Multitex06.jpg

Autor: Sascha Willems

Download

Das Multitexturingdemo (inklusive Quellcode) herunterladen


Vorhergehendes Tutorial:
Tutorial Bumpmaps mit Blender
Nächstes Tutorial:
Tutorial StencilSpiegel

Schreibt was ihr zu diesem Tutorial denkt ins Feedbackforum von DelphiGL.com.
Lob, Verbesserungsvorschläge, Hinweise und Tutorialwünsche sind stets willkommen.