|
|
Wie der Name bereits erahnen lässt, befassen wir uns in dieser Lektion mit dem Texture-Mapping. Ein Basiswissen von Texturen in OpenGL (Zuweisung, Texturkoordinaten, Textureineinheiten...) wird hierbei vorrausgesetzt. Nachdem die grundlegenden Shaderfunktionen zum Zugriff auf Texturen erläutert sind, werden wir uns dem Schreiben eines eigenen Multitexturshaders widmen.
|
|
Um in einem Shader Texturen zu benutzen gibt es eine Reihe von Funktionen und Variablen, die von GLSL bereitgestellt werden. Diese werden im Folgenden sowohl für den Vertex- als auch für den Pixelshader erläutert.
|
|
Wie immer befassen sich Vertexshader mit Koordinaten. In diesem Falle sind es die Texturkoordinaten der zu bearbeitenden Vertex, die als attribute Variable von OpenGL übergeben werden. Auf diese wird über die Bezeichner gl_MultiTexCoord[0-7] zugegriffen. Die Felder s, t, r und q einer Texturkoordinate können wie in Lektion 2 beschrieben über den Klammeroperator [] oder den Punktoperator über die Aliasnamen .s, .t, .p, .q angesprochen werden. Das für die dritte Komponente .p nicht der Name .r verwendet werden kann liegt ganz einfach daran, dass dieser bereits für die erste Komponente .r eines Farbvektors vergeben ist.
Nachdem der Vertexshader eine Texturkoordinate bearbeitet hat muss er diese über die varying Variable gl_TexCoord[0-7] speichern. Die interpolierten Texturkoordinaten können dann im Pixelshader verarbeitet werden.
In den meisten Fällen wird die Texturkoordinate einfach unverändert der Pipeline übergeben:
gl_TexCoord[0] = gl_MultiTexCoord0;
|
|
Um in einem Pixelshader auf eine Textur zugreifen zu können müssen wir diese erst über eine uniform Variable vom Typ sampler[1-3]D bekannt geben. Diese muss im Programmcode der Anwendung über OpenGL Funktionsaufrufe mit einer Textureinheit verbunden werden.
Da die Ausgabe eines Pixelshaders sich auf den Farbwert eines einzelnen Fragmentes beschränkt, müssen wir eine Möglichkeit haben auf einen einzelnen Farbwert einer Textur zugreifen zu können. Die dafür vorgegebenen GLSL-Funktionen heißen texture1D(sampler1D, float), texture2D(sampler2D, vec2) und texture3D(sampler3D, vec3).
In der Regel wird der Farbwert an der –durch die interpolierten Texturkoordinaten bestimmten– Position ausgelesen:
[texturize.frag]
uniform sampler2D tex0;
void main(void)
{
gl_FragColor = texture2D(tex0, gl_TexCoord[0].st);
}
|
|
Nachdem in den letzten Lektionen relativ simple Shader programmiert wurden deren visuelle Ausgaben eher bescheiden ausfielen sollen diesmal sowohl Multitexturing als auch Vertexanimationen programmiert werden.
|
|
Die Aufgabe dieser Lektion besteht darin einen Flaggeneffekt zu programmieren bei dem die Techniken Vertexanimation und Multitexturing benutzt werden sollen. Dabei kann das in dieser Lektion erworbene Wissen über Texturierung in GLSL-Shadern gleich zur Anwendung gebracht werden. Diese Grafik zeigt, wie das Endergebnis aussehen könnte:

|
|
Um diesen Effekt umzusetzen sind einige OpenGL Ressourcen nötig, die ausserhalb eines Shaders erstellt werden müssen. Diese wurden bereits von ShaderSchool erstellt und zur Benutzung vorbereitet.
Zum einen gibt es ein Gittermodell, das später zu einer Flagge animiert werden soll. Dafür müssen die Vertizes im Vertexshader manipuliert werden. Im Wireframe-Modus sieht diese Modell so aus:

Die ersten beiden Textureinheiten sind mit zwei Texturen belegt. Die erste Textur besteht aus einer Fahne und die zweite aus einer Alphawert-Textur. Wenn beide Texturen durch eine Multiplikation verknüpft werden, ergibt sich das zerissenes Aussehen, wie im Beispielscreenshot zu sehen ist. Die Stauchung der Texturen muss nicht explizit aufgehoben werden, die Texturkoordinaten des Gittermodells sind so gewählt, dass sie die Stauchung aufheben.
![]()
|
| Abbildung 3: Die Texturen auf Textureinheit 0 und 1 |
Damit eine Animation überhaupt möglich ist, benötigt ein Shader einen sich ständig verändernden Wert. Für diesen Effekt existiert eine uniform-Variable vom Typ float mit dem Namen time. Sie gibt die vergangene Zeit seit der letzten Neukompilation in Sekunden an.
vertex.x += sin(time);
|
|