Lade Inhalt...

Shader mit GLSL

Eine Einführung in die OpenGL Shading Language

©2009 Diplomarbeit 132 Seiten

Zusammenfassung

Inhaltsangabe:Einleitung:
Diese Arbeit ist das Ergebnis von dreimonatiger, intensiver Auseinandersetzung mit dem Thema Shader und der OpenGL Shading Language. Sie befasst sich im Groben mit der Erstellung von Shadern mit GLSL und den dafür nötigen Vorbereitungen. Der Autor hat sich während der Erstellung vom blutigen Anfänger zum begeisterten Shader Programmierer entwickelt.
Die Arbeit ist so gestaltet, dass ein Leser mit Grundkenntnissen der Informatik und Computergrafik das Prinzip von Shadern ohne Probleme erlernt. Sichtbare Ergebnisse sollen zum Ausprobieren und Experimentieren ermutigen. Beginnend mit den notwendigen Vorbereitungen werden die wichtigsten Funktionen der GLSL anhand von Beispielen erklärt.
Selbst wer vorher noch nie etwas mit Shadern zu tun hatte, soll nach dem Lesen dieser Arbeit in der Lage sein Shader zu lesen, zu schreiben und diese in OpenGL-Anwendungen zu integrieren. Inhaltsverzeichnis:Inhaltsverzeichnis:
1.Einleitung6
1.1Begriffliche Voraussetzungen6
1.1.1Shader6
1.1.2Shader Objekt7
1.1.3Shader Programm8
1.1.4Fixed Functionality8
1.1.5Vertex9
1.1.6Edge9
1.1.7Face9
1.1.8Fragment10
1.1.9Vertex Shader10
1.1.10Geometry Shader10
1.1.11Fragment Shader10
1.1.12Object-Space11
1.1.13Eye-Space12
1.1.14Diffusemap / Colormap12
1.1.15Alphamap / Transparencymap13
1.1.16Bumpmap13
1.1.17Heightmap / Displacementmap13
1.1.18Normalmap13
1.1.19Specularitymap / Reflectivitymap13
1.1.20Luminositymap14
1.1.21Environmentmap / Reflectionmap14
2.Gründe für Shader mit GLSL15
2.1Über Shader15
2.2Die Shadertypen15
2.3Die High Level Shading Languages16
2.3.1HLSL17
2.3.2Cg17
2.3.3GLSL18
3.Shader in OpenGL20
3.1Die OpenGL Shading Language20
3.1.1Unterschiede zwischen Vertex und Fragment Shader20
3.1.2Unterschiede zu C/++21
3.1.3Typecasting und Konstruktoren21
3.1.4Neue Datentypen21
3.1.5Zugriff auf Vektor-Komponenten22
3.1.6Die attribute, uniform und varying Qualifiers22
3.2Die OpenGL Shading Language API23
4.Vier Schritte zum eigenen Shader25
4.1Die Objekte vorbereiten25
4.1.1Vorbereiten in Blender25
4.1.2Vorbereiten in OpenGL29
4.2Die Texturen vorbereiten32
4.2.1Diffusemap33
4.2.2Alphamap38
4.2.3Bumpmap41
4.2.4Normalmap43
4.2.5Huemap44
4.2.6Lightnessmap / Brightnessmap48
4.2.7Environmentmap49
4.2.8Kanten entfernen54
4.2.9CrazyBump55
4.3Die Shader einbinden57
4.3.1Shader erstellen und kompilieren57
4.3.2Shader verwenden59
4.3.3Werte übergeben60
4.3.4Texturen übergeben61
4.3.5Das […]

Leseprobe

Inhaltsverzeichnis


Inhalt

1 Einleitung
1.1 Begriffliche Voraussetzungen
1.1.1 Shader
1.1.2 Shader Objekt
1.1.3 Shader Programm
1.1.4 Fixed Functionality
1.1.5 Vertex
1.1.6 Edge
1.1.7 Face
1.1.8 Fragment
1.1.9 Vertex Shader
1.1.10 Geometry Shader
1.1.11 Fragment Shader
1.1.12 Object-Space
1.1.13 Eye-Space
1.1.14 Diffusemap / Colormap
1.1.15 Alphamap / Transparencymap
1.1.16 Bumpmap
1.1.17 Heightmap / Displacementmap
1.1.18 Normalmap
1.1.19 Specularitymap / Reflectivitymap
1.1.20 Luminositymap
1.1.21 Environmentmap / Reflectionmap

2 Gründe für Shader mit GLSL
2.1 Über Shader
2.2 Die Shadertypen
2.3 Die High Level Shading Languages
2.3.1 HLSL
2.3.2 Cg
2.3.3 GLSL

3 Shader in OpenGL
3.1 Die OpenGL Shading Language
3.1.1 Unterschiede zwischen Vertex und Fragment Shader
3.1.2 Unterschiede zu C/++
3.1.3 Typecasting und Konstruktoren
3.1.4 Neue Datentypen
3.1.5 Zugriff auf Vektor-Komponenten
3.1.6 Die attribute, uniform und varying Qualifiers
3.2 Die OpenGL Shading Language API

4 Vier Schritte zum eigenen Shader
4.1 Die Objekte vorbereiten
4.1.1 Vorbereiten in Blender
4.1.2 Vorbereiten in OpenGL
4.2 Die Texturen vorbereiten
4.2.1 Diffusemap
4.2.2 Alphamap
4.2.3 Bumpmap
4.2.4 Normalmap
4.2.5 Huemap
4.2.6 Lightnessmap / Brightnessmap
4.2.7 Environmentmap
4.2.8 Kanten entfernen
4.2.9 CrazyBump
4.3 Die Shader einbinden
4.3.1 Shader erstellen und kompilieren
4.3.2 Shader verwenden
4.3.3 Werte übergeben
4.3.4 Texturen übergeben
4.3.5 Das Resultat
4.3.6 Die Shader in GLSLDemo einbinden
4.4 Die Shader programmieren
4.4.1 Farbe
4.4.2 Fragmente verwerfen
4.4.3 Direktionales Licht
4.4.4 Punktlicht
4.4.5 Drei Punktlichter
4.4.6 Drei Punktlichter (optimiert)
4.4.7 Glanz
4.4.8 Prozedurale Textur 2D
4.4.9 Prozedurale Textur 3D
4.4.10 Selektiv Fragmente verwerfen
4.4.11 Prozedurale Textur 3D (optimiert)
4.4.12 Einbinden von Textur-Maps
4.4.13 Nebel
4.4.14 Simulation der Fixed Functionality mit ShaderGen
4.4.15 Mehrfarb-Lack
4.4.16 Hue-Shifter
4.4.17 Displacement
4.4.18 Displacement Wasser

5 Fazit und Aussicht

6 Eigenständigkeitserklärung

7 Anhang 1: Literaturverzeichnis

8 Anhang 2: Verwendete Software
8.1 Programmierung
8.2 Grafik
8.3 Sonstiges

9 Anhang 4: GLSL Kurz-Referenz
9.1 Data types
9.2 Data type qualifiers
9.2.1 Global variable declarations
9.2.2 Function parameters
9.3 Vector components
9.4 Preprocessor
9.5 Vertex shader variables
9.5.1 Special Output Variables
9.5.2 Special input variables
9.5.3 Varying outputs
9.5.4 Attribute inputs
9.6 Fragment shader variables
9.6.1 Special Output Variables
9.6.2 Special input variables
9.6.3 Varying inputs
9.7 Built-in constants
9.8 Built-in uniforms
9.9 Built-in functions
9.9.1 Angle and Trigonometry Functions
9.9.2 Exponential Functions
9.9.3 Common Functions
9.9.4 Geometric Functions
9.9.5 Matrix Functions
9.9.6 Vector Relational Functions
9.9.7 Texture Lookup Functions
9.9.8 Texture Lookup Functions with LOD
9.9.9 Fragment Processing Functions
9.9.10 Noise Functions

10 Stichwortverzeichnis

1 Einleitung

« UNSER ZIEL: Grundlagen von GLSL lernen

Diese Arbeit ist das Ergebnis von dreimonatiger, intensiver Auseinandersetzung mit dem Thema Shader und der OpenGL Shading Language. Sie befasst sich im Groben mit der Erstellung von Shadern mit GLSL und den dafür nötigen Vorbereitungen. Der Autor hat sich während der Erstellung vom blutigen Anfänger zum begeisterten Shader Programmierer entwickelt.

Die Arbeit ist so gestaltet, dass ein Leser mit Grundkenntnissen der Informatik und Computergrafik das Prinzip von Shadern ohne Probleme erlernt. Sichtbare Ergebnisse sollen zum Ausprobieren und Experimentieren ermutigen. Beginnend mit den notwendigen Vorbereitungen werden die wichtigsten Funktionen der GLSL anhand von Beispielen erklärt.

Selbst wer vorher noch nie etwas mit Shadern zu tun hatte, soll nach dem Lesen dieser Arbeit in der Lage sein Shader zu lesen, zu schreiben und diese in OpenGL-Anwendungen zu integrieren.

1.1 Begriffliche Voraussetzungen

Zunächst wollen wir uns mit der Erklärung einiger Begriffe, denen wir im Verlauf dieser Arbeit noch begegnen werden, vertraut machen.

1.1.1 Shader

In der Computer-Grafik wird eine Sammlung von Anweisungen, die anstelle einer festen Funktionalität im GPU (Graphics Processing Unit) auf der Grafik-Hardware läuft, als Shader bezeichnet. Der Shader ist zuständig für die Berechnung der Farbe eines Objektes. Shader wurden bei OpenGL in der Version 1.5 und bei Direct3D in Version 8 eingeführt.

Abbildung in dieser Leseprobe nicht enthalten
OpenGL Rendering Pipeline
(vgl. [ROS - OpenGL Shading Language] Seite 10)

Dabei wird in der OpenGL Rendering Pipeline die Per-Vertex-Operationen durch Vertex-Shader (läuft auf dem Vertex-Processor), und das Fragment-Processing durch Fragment-Shader (läuft auf dem Fragment-Processor) ersetzt.

(vgl. [WIK2 - Shader])

1.1.2 Shader Objekt

Ein Shader Objekt beinhaltet und verwaltet den Quellcode der einen Shader definiert. Solch ein Objekt kann vom Typ Vertex oder Fragment Shader sein.

(vgl. [ROS - OpenGL Shading Language] Seite 170-172)

1.1.3 Shader Programm

Ein Shader Programm kann aus einem oder mehreren Shader Objekten bestehen. Sie ersetzen je nach Typ die Fixed Functionality komplett. So ersetzt ein Shader Programm, das nur einen Vertex Shader beinhaltet, die Per-Vertex-Operationen. Das Fragment-Processing der Fixed Functionality wird jedoch nicht ersetzt. Andersherum ersetzt ein Shader Programm, das nur Fragment Shader beinhaltet, das Fragment Processing, jedoch nicht die Fixed Functionality der Per-Vertex-Operationen.

Sind sowohl Vertex als auch Fragment Shader im Shader Programm enthalten, so müssen gegebenenfalls die gewünschten Funktionen der festen Funktionalität im Shader per GLSL implementiert werden.

Im Kapitel Fehler! Verweisquelle konnte nicht gefunden werden. schauen wir uns ein Programm an, das uns hilft, die Fixed Functionality in einem Shader zu simulieren.

(vgl. [OGL1 - Shading Language Documentation])

1.1.4 Fixed Functionality

Benutzen wir kein Shader Programm, wird ein 3D-Objekt in OpenGL anhand der Fixed Functionality berechnet. Mit dem Setzen von States (Zuständen) kann das Ergebnis dieser Berechnung beeinflußt werden.

Um mehr Einfluss auf Ergebnis und Performance der Berechnung nehmen zu können, ersetzen wir nun diese feste Funktionalität durch eigene Programme. Durch Shader.

Im Kapitel Fehler! Verweisquelle konnte nicht gefunden werden. folgen einige Beispiele, in denen wir die Fixed Functionality durch Shader ersetzen werden.

Mehr Informationen über die Fixed Functionality und OpenGL States können wir in den OpenGL Spezifikationen und Dokumentationen auf http://opengl.org bzw. http://opengl.org/documentation/specs finden.

(vgl. [OGL1 - Shading Language Documentation])

1.1.5 Vertex

Ein Vertex ist ein Eckpunkt. Aus mehreren Vertices setzt sich das 3D-Modell zusammen. Ein Vertex-Shader wird einmal pro Vertex ausgeführt. Also im Falle dieser Abbildung dreimal. In OpenGL können wir für jeden Vertex eine eigene Normale definieren.

Abbildung in dieser Leseprobe nicht enthalten
Die Vertices (rot)

1.1.6 Edge

Zwischen den Vertices verlaufen die Edges. Sie beginnen und enden in einem Vertex. Die Edge ist eine Kante eines Faces.

Abbildung in dieser Leseprobe nicht enthalten
Die Edges (rot)

1.1.7 Face

Die Flächen eines Objektes bestehen meist aus vielen Faces. Eine Normale bestimmt, in welche Richtung das Face zeigt. Dadurch können wir ermitteln, welche Seite die Vorderseite und welche Seite die Rückseite ist.

Abbildung in dieser Leseprobe nicht enthalten
Das Face (rot)

1.1.8 Fragment

Ein Fragment besteht aus den Daten die nötig sind einen einzelnen Pixel zu zeichnen. Dabei kann ein Fragment z.B. Farbwerte, Tiefenwerte, Texturkoordinaten, Alphawerte, etc. beeinhalten. Ein Fragment enthält also wesentlich mehr Informationen als ein einfacher Pixel.

(vgl. [WIK9 - Fragment])

1.1.9 Vertex Shader

Im Vertex Shader können z.B. Position, Farbe, Textur-Koordinaten und Normalen eines Vertex beeinflußt werden. Dem Fragment Shader können hier errechnete Werte als varying Variablen übergeben werden.

(vgl. [WIK2 - Shader] Types of shaders)

Pro Vertex eines Objektes wird ein Vertex Shader einmal ausgeführt. Es können weder neue Vertices hinzugefügt, noch Eigenschaften von anderen Vertices gelesen oder verändert werden. Der Vertex Shader muss die homogenen Koordinaten eines Vertex errechnen.

(vgl. [SPEC1 - The OpenGL® Shading Language 1.30] Seite 13)

1.1.10 Geometry Shader

Geometry Shader werden nach dem Vertex Shader ausgeführt. Hier kann Geometrie prozedural erstellt, kopiert oder verändert werden.

Die Geometry Shader sind jedoch nur in Direct3D 10 (und somit nur auf Windows Vista) verfügbar. In OpenGL sind sie noch nicht offiziell (sondern nur mit Erweiterung) nutzbar. Daher beschäftigen wir uns in dieser Arbeit nur mit Vertex und Fragment Shadern.

(vgl. [WIK2 - Shader] Types of shaders)

1.1.11 Fragment Shader

Der Fragment Shader (auch Pixel Shader genannt) wird einmal pro Bildpunkt auf dem mit diesem Shader berechneten Objekt ausgeführt. Dem Bildpunkt muss entweder ein Farbwert gegeben, oder der Bildpunkt verworfen werden.

Ein Fragment Shader wird meist für Per Pixel Lighting (z.B. Bump Mapping), prozedurale Oberflächen, Reflexion, Refraktion und weitere Textur-Modifikation benutzt.

(vgl. [WIK2 - Shader] Types of shaders)

Ein Fragment Shader kann parallel für mehrere Fragmente gleichzeitig ausgeführt werden. Die X- bzw. Y-Position des Fragments kann hier nicht verändert werden (nur die Z-Position für den Z-Buffer). Ebenso wenig können Werte von anderen Fragmenten ausgelesen oder beeinflußt werden.

Die Ausgaben aus dem Fragment Shader beeinflussen meist den Speicher des Framebuffers oder auch den Texturspeicher. Verworfene Fragmente verändern den vorhandenen Framebuffer nicht.

(vgl. [SPEC1 - The OpenGL® Shading Language 1.30] Seite 13)

1.1.12 Object-Space

Objekt-Space Koordinaten sind relativ zum Ursprung des Objektes. Wird das Objekt in der Welt verschoben, rotiert oder skaliert, bleiben die Object-Space Koordinaten eines Vertex unverändert.

Abbildung in dieser Leseprobe nicht enthalten
Vertexposition vom Objekt-Ursprung

Wir benötigen diese Objekt-Space Koordinaten z.B. für Prozedurale 3D-Texturen. Ein Beispiel dazu folgt im Kapitel Fehler! Verweisquelle konnte nicht gefunden werden..

(vgl. [ROS - OpenGL Shading Language] Seite 678)

1.1.13 Eye-Space

Eye-Space Koordinaten sind relativ zur Kamera. Verschieben wir ein Objekt oder die Kamera, verändern sich die Eye-Space Koordinaten eines betroffenen Vertex.

Abbildung in dieser Leseprobe nicht enthalten
Vertexposition vom Kamera-Ursprung

Wir benutzen diese Koordinaten z.B. zur Lichtberechnung oder für Nebel. Beispiele dazu folgen im Kapitel Fehler! Verweisquelle konnte nicht gefunden werden..

(vgl. [ROS - OpenGL Shading Language] Seite 674)

Neben Object- und Eye-Space gibt es noch weitere Koordinaten-Systeme. In OpenGL sind jedoch diese Beiden am wichtigsten. Mehr über Transformation und Koordinatensysteme in OpenGL hier:

www.opengl.org/resources/faq/technical/transformations.htm

1.1.14 Diffusemap / Colormap

Die Diffusemap bildet die Färbung einer Oberfläche ab. Etwa so, wie der Aufdruck auf einem Blatt Papier oder einer Verpackung. Sie sollte möglichst flach, ohne Verzerrungen durch die Oberflächenstruktur und frei von Beleuchtungseffekten und Schatten sein.

Die Oberflächenstruktur, sowie die Licht und Schatten-Effekte, können später dynamisch errechnet werden. Wäre die Beleuchtung in der Textur enthalten, gäbe es verfälschte Ergebnisse bei der Berechnung des dynamischen Lichtes.

(vgl. [BIR - Digital Lighting & Rendering] Seite 204)

1.1.15 Alphamap / Transparencymap

In der Alphamap wird die Transparenz in Graustufen abgebildet. So stehen beispielsweise die weißen Stellen in der Map für komplett durchsichtig und die Schwarzen für 100% opak.

(vgl. [BIR - Digital Lighting & Rendering] Seite 209)

1.1.16 Bumpmap

Das Bumpmap enthält die Höheninformationen für die einzelnen Bildpunkte. Hier steht zum Beispiel ein weißer Wert für eine starke und ein schwarzer Wert für eine geringe (bzw. gar keine) Erhebung.

(vgl. [BIR - Digital Lighting & Rendering] Seite 211)

1.1.17 Heightmap / Displacementmap

Im Gegensatz zum Bumpmap wird im Displacementmap die Geometrie eines 3D-Objektes verändert. Dabei wird ein betroffener Vertex entlang seiner Normalen verschoben. Da dies für jeden vorhandenen Vertex geschieht, ist das Ergebnis stark abhängig von der Auflösung des 3D-Modells.

Um Darstellungsfehler bei mangelnder Auflösung zu vermeiden, sollten Displacementmaps wenig Details enthalten, harte Kanten (direkter Wechsel von z.B. 100% Schwarz auf 100% Weiß bei benachbarten Pixeln) vermeiden und nur grob die Höheneigenschaften der Oberfläche darstellen.

(vgl. [BIR - Digital Lighting & Rendering] Seite 212)

1.1.18 Normalmap

Die Normale gibt die Richtung an, in die eine Fläche zeigt. Mit der Normalmap können wir pro Bildpunkt eine Richtung angeben. Damit können wir eine Oberfläche simulieren, die in der Beleuchtung nicht flach erscheint. Im Gegensatz zur Bumpmap, das Informationen zur Höhe eines jeden Bildpunktes enthält, gibt die Normalmap die Neigung des Bildpunktes an.

(vgl. [BLN1 - Normal Maps])

1.1.19 Specularitymap / Reflectivitymap

In der Specular- oder Specularitymap wird die Glanzlichtstärke bestimmt. Sie kann im Shader mit der Glanzlichtstärke (Specularity) multipliziert werden. Je nach Bedarf kann das Specularmap hierbei aus drei Farbkanälen bestehen und eine Färbung des Glanzlichtes verursachen. Ist eine Verfärbung nicht erwünscht, reicht eine Graustufen-Textur.

Die Reflectivitymap wird z.B. auf die Sichtbarkeit einer Environmentmap gerechnet und beeinflußt damit die Stärke (und falls gewünscht auch die Farbe) der auftretenden Reflexion.

Mit Hilfe der Specularitymap lassen sich Oberflächen simulieren die z.B. stumpf / glatt oder trocken / feucht aussehen. Dabei sind dann die dunkleren Stellen stumpf oder trocken und die helleren Stellen glatt oder feucht.

(vgl. [BIR - Digital Lighting & Rendering] Seite 206)

1.1.20 Luminositymap

Luminositymap ist ähnlich dem Specularmap. Hier wird jedoch das Eigenleuchten des Objektes in Farbe und Leuchtstärke abgebildet. Besonders nützlich z.B. für partiell glühende Kohle, Lampen, ein beleuchteter Globus oder beleuchtete Fenster an einer Hauswand oder eine elektronische Anzeige.

Farbige oder helle Bildpunkte beeinflussen das Eigenleuchten des Objektes, während schwarze Bildpunkte die Grundfarbe des Objektes unverändert lassen.

(vgl. [BIR - Digital Lighting & Rendering] Seite 208)

1.1.21 Environmentmap / Reflectionmap

Ein Environmentmap beinhaltet die Umgebung der 3D Szene als Bildinformationen. So lässt sich auch ohne Raytracing ein überzeugend wirkendes reflektierendes Material simulieren.

(vgl. [ROS - OpenGL Shading Language] Seite 265)

2 Gründe für Shader mit GLSL

“The recent trend in graphics hardware has been to replace fixed functionality with programmability in areas that have grown exceedingly complex (e.g., vertex processing and fragment processing). The OpenGL Shading Language has been designed to allow application programmers to express the processing that occurs at those programmable points of the OpenGL pipeline.”
([OGL1 - Shading Language Documentation])

« NEUES ZIEL: Die Wahl der Sprache

Dieser Abschnitt erklärt die Auswahl des Themas dieser Arbeit. Hier zeigt sich warum und wann man mit Shadern mehr erreicht als über die Fixed Functionality der OpenGL Rendering Pipeline. Ein kurzer Vergleich der verschiedenen Shading Sprachen soll die Wahl von GLSL erklären.

2.1 Über Shader

Bevor es Shader gab, war ein Grafik-Programmierer von den steifen Vorgaben der Rendering-Pipeline abhängig. Diese konnten nur durch Setzen von States beeinflusst werden. Man spricht hier von Fixed Functionality.

Die Einführung von Vertex und Fragment Shadern erlaubt eine bisher unerreichte Flexibilität und Programmierbarkeit der Grafikprozessoren.

(vgl. [WIK1 - GLSL] Background)

2.2 Die Shadertypen

“The Direct3D and OpenGL graphic libraries use three types of shaders.“
([WIK2 - Shader] Types of shaders)

In OpenGL und Direct3D werden standardmäßig zwei Typen von Shadern unterstützt. Dazu kommt ein dritter Shadertyp. Der Geometrie-Shader ist noch kein fester Bestandteil der GLSL und kann bisher nur durch Erweiterungen benutzt werden. Aus diesem Grund werden wir uns in dieser Arbeit nur mit den etablierten Vertex und Fragment Shadern beschäftigen.

2.3 Die High Level Shading Languages

“Until recently, developers did not have the same level of control over the output from the graphics pipeline of graphics cards, but shading languages for real-time rendering are now widespread. They provide both higher hardware abstraction and a more flexible programming model when compared to previous paradigms which hardcoded transformation and shading equations. This results in both giving the programmer greater control over the rendering process, and delivering richer content at lower overhead.

[…] This kind of shading language is usually bound to a graphics API, although some applications also provide built-in shading languages with limited functionalities. ”
([WIK6 - Shading Language])

Neben der Möglichkeit mit Assembler low level auf der Grafikkarte zu arbeiten, gibt es verschiedene High Level Shading Languages. Die drei etablierten Sprachen sind HLSL, Cg und GLSL.

In dem folgenden Vergleich zeigt sich der Grund für die Entscheidung die OpenGL Shading Language als Shading Language für diese Arbeit zu nutzen.

Ein nützliches Tool zum Erstellen von Shadern für die drei folgenden Sprachen ohne Programmierung:

Abbildung in dieser Leseprobe nicht enthalten
http://www.mentalimages.com/products/mental-mill.html

Von AMD (ehemals von ATI) gibt es das kostenlose Tool Rendermonkey für Shader Entwicklung in DirectX 9.0c, OpenGL 2.0, und OpenGL ES 2.0:

Abbildung in dieser Leseprobe nicht enthalten
developer.amd.com/ gpu /rendermonkey/Pages/default.aspx

2.3.1 HLSL

Die High Level Shading Language (kurz HLSL genannt) ist Teil von Direct3D. Sie wurde in der Version 8 eingeführt. Die aktuelle Version Direct3D10 ist nur auf Systemen mit Windows Vista erhältlich. Benutzer früherer Windows Versionen müssen sich mit dem Funktionsumfang von DirectX 9.0c zufrieden geben.

(vgl. [WIK3 - HLSL] High Level Shader Language)

Als Hersteller von Direct3D und Windows ist Microsoft nicht an Entwicklung für andere Plattformen interessiert. Selbst ihre alten Windows Versionen werden nicht auf dem aktuellen Stand der Technik gehalten. So läuft z.B. nur auf Computern mit Betriebssystem ab Windows Vista die neuste DirectX Version mit Shader Model 4.

(vgl. [MSD1 - DirectX FAQ] Will DirectX 10 be available for Windows XP?)

Nützliche Links:

DirectX
msdn.microsoft.com/directx
Microsoft MSDN DirectX Developer Center

2.3.2 Cg

Die Sprache Cg wurde von dem Grafikkarten-Hersteller nVidia und Microsoft entwickelt. Sie ist an HLSL angelehnt. Cg bietet den Vorteil, dass von einem Cg Programm sowohl DirectX- als auch OpenGL-Shader kompiliert werden können.

Soll eine Anwendung also sowohl Direct3D als auch OpenGL als Möglichkeit anbieten, macht es Sinn, die Shader in Cg zu programmieren. Ansonsten müssten Shader für GLSL und HLSL parallel entwickelt werden.

Da sowohl ein Grafikkarten-Hersteller als auch Direct3D und Windows Hersteller Microsoft hinter der Entwicklung von Cg stecken, ist auch hier keine totale Unabhängigkeit geboten.

(vgl. [FUS1 - Cg and HLSL Faq] The Cg/HLSL Language)

Abbildung in dieser Leseprobe nicht enthalten

2.3.3 GLSL

Im Gegensatz zu DirectX ist OpenGL unabhängig von Plattform und Hersteller. Die Nutzung von OpenGL ist für Software-Entwickler kostenlos. Dokumentationen sind unter der Website www.opengl.org frei verfügbar.

(vgl. [WIK7 - OpenGL])

OpenGL wird von der Khronos Group Inc. verwaltet. Diese vereint über 100 Firmen, darunter auch namhafte Hersteller von Grafik-Hardware. Die Gefahr der Abhängigkeit von einem bestimmten Hersteller und Optimierung ausschließlich für bestimmte Grafikkarten ist dadurch minimal.

Als minimale Systemanforderung für die Benutzung von GLSL ist eine mindestens OpenGL 1.5 (besser OpenGL 2.0) Shader fähige Grafikkarte nötig.

Abbildung in dieser Leseprobe nicht enthalten
Khronos Members (http://www.khronos.org/about)

Die kontinuierliche Weiterentwicklung und starke Verbreitung auf diversen Plattformen und die Unterstützung von so vielen Herstellern lässt OpenGL zukunftssicher erscheinen. Das soll uns als Begründung dienen, uns näher mit der zu OpenGL gehörigen Shading Language GLSL zu befassen.

Von 3DLabs entwickelt. Gute Shaderbeispiele incl. Sourcecode. Mit GLSLShaderGen kann man Shader generieren, die die Fixed Functionality emulieren. Das kann gerade für den Anfang sehr nützlich sein.

3 Shader in OpenGL

« NEUES ZIEL: Mit GLSL Theorie vertraut machen

Im Kapitel Fehler! Verweisquelle konnte nicht gefunden werden. werden wir sehen, wie Shader konkret in GLSL erstellt werden. Die Einbindung in die OpenGL Anwendung folgt im Kapitel Fehler! Verweisquelle konnte nicht gefunden werden.. An dieser Stelle wollen wir uns jedoch schon mit der Theorie von GLSL vertraut machen.

3.1 Die OpenGL Shading Language

“The OpenGL Shading Language is actually two closely related languages. These languages are used to create shaders for the programmable processors contained in the OpenGL processing pipeline.”
([SPEC1 - The OpenGL® Shading Language 1.30] Seite 4)

Obwohl die OpenGL Shading Language (Kurz GLSL) eigentlich zwei Sprachen vereint (GLSL für den Vertex- und GLSL für den Fragment-Prozessor), werden wir sie hier der Einfachheit halber als eine einzige Sprache behandeln.

Die Konkreten Anwendungsbeispiele zum Erstellen von Shadern in OpenGL folgen in dem Kapitel Fehler! Verweisquelle konnte nicht gefunden werden..

3.1.1 Unterschiede zwischen Vertex und Fragment Shader

Die Unterschiede machen sich größtenteils bei den Built-In Variables bemerkbar. Diese sind von Shadertyp zu Shadertyp unterschiedlich. Dies ergibt sich durch die unterschiedliche Nutzung und die unterschiedlichen Anforderungen. So kann der Vertex Shader z.B. die Variable gl_Vertex oder gl_Normal auslesen, der Fragment Shader kann hingegen z.B. auf gl_FragCoord oder gl_FrontFacing zugreifen.

Mehr Informationen über die Built-In Variablen folgen im Kapitel Fehler! Verweisquelle konnte nicht gefunden werden. bzw. Fehler! Verweisquelle konnte nicht gefunden werden. im Fehler! Verweisquelle konnte nicht gefunden werden..

Auch in den Built-In Funktionen gibt es je nach Shadertyp leichte Abweichungen. So kann z.B. die Vertex Shader Funktion ftransform() in einem Fragment Shader nicht benutzt werden.

3.1.2 Unterschiede zu C/++

“The OpenGL Shading Language is a high-level procedural language designed specifically for the OpenGL environment. This language allows applications to specify the behavior of programmable, highly parallel graphics hardware. It contains constructs that allow succinct expression of graphics shading algorithms in a way that is natural for programmers experienced in C and C++.”
([ROS - OpenGL Shading Language] Seite 95)

Die OpenGL Shading Language ist stark an C und C++ angelegt. Dies erleichtert Programmierern mit Erfahrung in den genannten Sprachen den Einstieg. Es gibt jedoch einige Unterschiede.

3.1.3 Typecasting und Konstruktoren

“The OpenGL Shading Language includes support for scalar, vector, and matrix types; structures and arrays; sampler types that access textures; data type qualifiers that define shader input and output; constructors for initialization and type conversion; and operators and flow-control statements like those in C and C++.”
([ROS - OpenGL Shading Language] Seite 95)

Anstelle von Typecasting wird zur Umwandlung von Dateitypen mit Konstruktoren gearbeitet. So schreiben wir z.B. anstelle von (float) 25 zur Umwandlung float(25).

3.1.4 Neue Datentypen

“Vector types are supported for floating-point, integer, and Boolean values. For floating-point values, these vector types are referred to as vec2 (two floats), vec3 (three floats), and vec4 (four floats). Operators work as readily on vector types as they do on scalars. To sum vectors v1 and v2, you simply would say v1 + v2.”
([ROS - OpenGL Shading Language] Seite 50)

Da die GLSL speziell zum Ausdrücken von Shading Algorithmen in OpenGL konzipiert wurde, gibt es hierfür nützliche Datentypen. Diese Datentypen wie z.B. vec3 oder mat3 erleichtern den Umgang mit Matrizen oder Vektoren enorm. Um einen vec3 zu erhalten, nutzen wir den Konstruktor für Vektoren mit 3 Komponenten indem wir z.B. vec3(0.0, 1.0, 2.0) schreiben.

Eine Übersicht über die Datentypen folgt im Kapitel Fehler! Verweisquelle konnte nicht gefunden werden. im Fehler! Verweisquelle konnte nicht gefunden werden..

3.1.5 Zugriff auf Vektor-Komponenten

“Individual components of a vector can be accessed either with array syntax or as fields of a structure. Color values can be accessed by appending .r to the name of a vector variable to access the first component, .g to access the second component, .b to access the third, and .a to access the forth. Position values can be accessed with .x, .y, .z and .w, and texture values can be accessed with .s, .t, .p, .q. Multiple components can be selected by specification of multiple names like .xy”
([ROS - OpenGL Shading Language] Seite 50f)

Um auf einzelne Komponenten eines Vektors (z.B. eines vec4 mit Farb- und Alpha-Werten) zuzugreifen, kann Array-Schreibweise (z.B. theColor[0]) benutzt werden. Nützlicher jedoch ist der Zugriff als Field einer Structure. Diese Schreibweise erleichtert das Lesen sowie das Zugreifen auf mehrere Komponenten.

Wollen wir z.B. auf einen roten Farbwert eines Vektors zugreifen, so können wir einfach theColor.r schreiben. Dies entspricht dann einer float Variable. Benötigen wir nur die grüne und blaue Komponente, so können wir einfach theColor.gb schreiben. Damit erhalten wir einen vec2 als Ergebnis.

Eine Auflistung der Komponenten-Bezeichner folgt im Kapitel Fehler! Verweisquelle konnte nicht gefunden werden. im Fehler! Verweisquelle konnte nicht gefunden werden.

3.1.6 Die attribute, uniform und varying Qualifiers

„Qualifiers have been added to manage the input and output of shaders. The attribute, uniform, and varying qualifiers specify what type of input or output a variable serves. Attribute variables communicate frequently changing values from the application to a vertex shader, uniform variables communicate infrequently changing values from the application to any shader, and varying variables communicate interpolated values from a vertex shader to a fragment shader.“
([ROS - OpenGL Shading Language] Seite 51)

Für die Kommunikation zwischen Anwendung und Shader und zwischen Vertex und Fragment Shader gibt es die attribute, uniform und varying Qualifier. Sie werden vor dem Variablentyp in der Definition, wie z.B. uniform float eineVariable angehängt.

Abbildung in dieser Leseprobe nicht enthalten
Die attribute, uniform und varying Qualifier (vereinfacht)

Die uniform und attribute Variablen können in der OpenGL Anwendung definiert werden. Während die attribute Variablen pro Vertex angegeben und nur im Vertex Shader ausgelesen werden können, kann eine uniform Variable vor dem Zeichnen des Objektes angegeben werden. Auf diese uniform Variablen können sowohl der Vertex als auch der Fragment Shader zugreifen.

3.2 Die OpenGL Shading Language API

„GLSL shaders are not stand-alone applications; they require an application that utilizes the OpenGL API.„
([WIK1 - GLSL] Compilation and Execution)

Wie bereits im Kapitel Fehler! Verweisquelle konnte nicht gefunden werden. erwähnt, ist GLSL der Sprache C (bzw. C++) nachempfunden. Das zeigt sich nicht nur am Quellcode, sondern auch beim Kompilieren. Ein Shader wird, wie ein C/++ Programm, zuerst geschrieben. Danach wird es an den Compiler und dann dem Linker übergeben.

Abbildung in dieser Leseprobe nicht enthalten
Weg des Shaders bis auf die Grafikkarte (vereinfacht)

Mit Hilfe der OpenGL Shading Language API kann die OpenGL Anwendung Shader erstellen und mit diesen kommunizieren. Sie ist die Schnittstelle zwischen OpenGL und GLSL (bzw. zwischen OpenGL Anwendung und Shadern auf der Grafikhardware).

Die nötigen Befehle zur Ausführung eines Shaders in OpenGL:

glCreateShader()
Ein Shader Objekt generieren

glShaderSource()
Den Quellcode des Shaders an Shader Objekt übergeben

glCompileShader()
Das Shader Objekt kompilieren

glCreateProgram()
Ein Shader Programm erstellen

glAttachShader()
Bereits erstellte Shader Objekte dem Shader Programm hinzufügen

glLinkShader()
Programm binden

glUseProgram()
Das Shader Programm anstelle der Fixed Functionality nutzen

(vgl. [ROS - OpenGL Shading Language] Seite 212)

Dieses Kapitel dient dem groben Verständnis der nötigen Schritte und Reihenfolge. Eine detailliertere Beschreibung der einzelnen Schritte und Anwendungsbeispiele folgen in den späteren Kapiteln Fehler! Verweisquelle konnte nicht gefunden werden. und Fehler! Verweisquelle konnte nicht gefunden werden..

4 Vier Schritte zum eigenen Shader

« NEUES ZIEL: Umsetzen der Shader

Nun wollen wir die Shader umsetzen und einbinden. Da Shader jedoch nicht ohne ein 3D-Objekt darstellbar sind, lernen wir hier auch Basis-Wissen zum Erstellen von 3D-Modellen. Häufig werden auch Texturen benötigt. Daher auch zur Textur-Erstellung einige Anleitungen.

4.1 Die Objekte vorbereiten

« NEUES ZIEL: Objekte für OpenGL vorbereiten

Alle Shader brauchen ein Objekt, auf dem sie ausgeführt werden können. Der Vertex-Shader wird für jeden Vertex des Objektes, und der Fragment-Shader für jeden sichtbaren Bildpunkt des Objektes ausgeführt. Schauen wir uns nun einmal zwei Möglichkeiten an, ein Objekt zu erstellen und Vertex-Attribute zu definieren.

Beim Arbeiten mit 3D-Modeling-Tools kann man sich durch Umschalten zwischen den Bearbeitungsmodi die Objektbearbeitung erleichtern. So muss man z.B. im Face-Modus nur ein Face (und nicht 3 Vertices) auswählen.

4.1.1 Vorbereiten in Blender

„Generally the easiest method when you first start learning. You'd be amazed by how many things get their start as a cube. Start by adding a pre-made primitive (usually a cube, but can be any of the other primitives i.e. Sphere, Cone, Tube etc). Then extrude, delete edges and vertices as needed to achieve your desired shape.“
([MAG1 - Blenderart Issue 05] About Box Modeling)

Ein üblicher Weg, ein 3D-Objekt zu erstellen, ist das Modellieren in einem 3D-Modeling-Tool. In diesem Fall benutzen wir das OpenSource-Programm Blender. Wir benutzen ein Modellier-Verfahren namens Box-Modeling. Damit erstellen wir eine halbe Figur und spiegeln die 2. Hälfte durch einen Modifikator automatisch dazu.

Als erstes erstellen wir einen Cube. Mit der Tab-Taste wechseln wir (falls nicht automatisch erfolgt) in den Edit-Mode. Im Face-Select-Mode wählen wir eine der seitlichen Faces aus und löschen es mit der Entfernen-Taste. Die somit entstandene Öffnung ist nach dem Spiegeln unsere Objektmitte.

Abbildung in dieser Leseprobe nicht enthalten
Cube in Blender: Ein Face entfernt

Mit viel Geduld und (hoffentlich genügend) Bildmaterial modellieren wir nun die gewünschte Figur zur Hälfte. Schon hierbei kann der Mirror-Modifier aktiviert werden. Damit können wir eine Kopie unseres bisherigen Modells an der gewünschten Achse spiegeln lassen. Dieser Modifikator kann dabei helfen, die Proportionen der Figur besser abzuschätzen.

Abbildung in dieser Leseprobe nicht enthalten
Halber Teddy aus Cube modelliert

Spätestens wenn die eine Hälfte soweit fertig ist, sollte der Mirror-Modifier in den Modifier-Stack hinzugefügt werden. Somit ist unser Lowpoly-Modell komplett.

Abbildung in dieser Leseprobe nicht enthalten
Mirror-Modifier ergänzt interaktiv die 2. Hälfte

Um zur Kontrolle die Normalen anzeigen zu lassen, können wir im Button-Window unter Editing/Mesh-Tools-More die Anzeige der Normals (und auch Vertex-Normals / VNormals) aktivieren.

Als nächstes benutzen wir den Subsurf-Modifier. Subsurf steht für Subdivision-Surfaces. Hier werden die Flächen des bestehenden Modells aufgeteilt und verfeinert. Die Vertex-Anzahl steigt.

Abbildung in dieser Leseprobe nicht enthalten
Subsurf fügt zusätzliche Vertices ein

Sind wir mit dem Ergebnis nicht zufrieden, können wir am Lowpoly-Mesh weiterarbeiten. Der Subsurf-Modifier kann dabei aktiv bleiben, um während des Bearbeitens bereits das Ergebnis sehen zu können.

Dabei können wir auch gleich dem Modell UV-Coordinaten geben. Im Edit-Mode können wir über das Mesh-Menü den UV-Unwrap-Dialog öffnen. Hier haben wir die Möglichkeit, zwischen verschiedenen Projektionen auszuwählen. Im UV-Editor können die UV-Koordinaten dann noch editiert und (meist anhand einer Textur) angepasst werden.

Abbildung in dieser Leseprobe nicht enthalten
UV-Editor und UV-Unwrap-Dialog

Bei Bedarf können den Vertices noch Vertex-Colors vergeben werden. Speziell für diesen Zweck gibt es in Blender einen Vertex-Paint-Mode. Mit einem Pinsel kann die Oberfläche wie eine echte Modellfigur bemalt werden.

Abbildung in dieser Leseprobe nicht enthalten
Der Vertex-Paint-Mode

Sind wir mit dem Modell soweit zufrieden, können wir einen geeigneten Exporter wählen. Hier benutze ich den Wavefront (.obj) Exporter. Hier sollten für den Export auf jeden Fall die Normalen, die UV und Apply Modifiers aktiviert werden.

Abbildung in dieser Leseprobe nicht enthalten
Blenders Obj-Exporter

Obj ist ein häufig benutztes Datei-Format für 3D-Modelle. Es ist jedoch nicht das Einzige. Für Blender ist eine Vielzahl an Exportern verfügbar. Darunter auch ein Exporter, der direkt OpenGL Quellcode schreibt.

4.1.2 Vorbereiten in OpenGL

“In the GL, most geometric objects are drawn by enclosing a series of coordinate sets that specify vertices and optionally normals, texture coordinates, and colors between Begin/End pairs. There are ten geometric objects that are drawn this way: points, line segments, line segment loops, separated line segments, polygons, triangle strips, triangle fans, separated triangles, quadrilateral strips, and separated quadrilaterals.”
([SPEC4 - The OpenGL® Graphics System Specification 2.10] Seite 12)

Einfache oder komplexe prozedurale Objekte können auch direkt durch OpenGL Befehle erzeugt werden. Nun folgen einige dieser Befehle am Beispiel der Erstellung einer Fläche (Plane) in JoGL.

Abbildung in dieser Leseprobe nicht enthalten

Dies ist der komplette Quellcode, um von JoGL ein rotes Plane incl. UV-Koordinaten, Vertex Attributen für den Vertex-Shader und senkrechter Normale erzeugen zu lassen. Die Vertex Attribute können vom Vertex Shader gelesen werden. Sie bieten uns die Möglichkeit dem Vertex Shader für jeden Vertex individuelle Werte zu übergeben.

glBindAttribLocation(myShaderProgram, 1, “FloatAttrib”);

Mit glBindAttribLocation wird eine Verbindung zu einem, im Vertex-Shader benutzten, Attribut namens FloatAttrib hergestellt.

gl.glBegin(gl.GL_QUADS);
// ...gl.glEnd();

Umschlossen wird der Block mit glBegin() und glEnd() Dazwischen stehen die OpenGL Anweisungen zum Zeichnen des Objektes. Der Parameter gibt dabei den Modus an, in dem das Mesh erstellt werden soll. Die Konstante GL_QUADS bewirkt, dass pro angegebener 4 Vertices ein komplettes Face gezeichnet werden soll.

Mehr Informationen über die Parameter für glBegin() gibt es hier: http://www.opengl.org/sdk/docs/man/xhtml/glBegin.xml

Schauen wir uns die Befehle im Einzelnen an. Die ersten drei Befehle gelten für alle folgenden gezeichneten Vertices. Sie können mit erneutem Aufruf und veränderten Parametern geändert werden.

gl.glNormal3f(0.0f, 0.0f, 1.0f);

Mit Hilfe der Software Scilab habe ich die Normale manuell berechnet. Mit ein bisschen Logik und mathematischem Verständnis ist das bei solch einem einfachen Plane jedoch nicht nötig. Die Normale steht senkrecht auf der Fläche. Sie kann in diesem Fall nur entweder komplett in die negative oder in die positive Z-Achse zeigen. Der in der Funktion glNormal3f übergebene Wert (0.0f, 0.0f, 1.0f) trifft also zu.

gl.glColor3f(1.0f, 0.0f, 0.0f);

Der Befehl glColor3f gibt die Vertex-Color für die folgenden Vertices an. In diesem Fall stellen wir die Farbe auf Rot. Der zweite Parameter steht für den Grünanteil, der Dritte für den Blauanteil.

gl.VertexAttrib1f(1, 1.0f)

Durch gl.VertexAttrib1f wird dem Shader an die an den generischen Referenzwert 1 gebundene Variable der float Wert 1.0f übergeben. Dieser Wert kann pro Vertex individuell angegeben werden. Für jedes weitere Vertex Attribut muss ein neuer Referenzwert (2, 3, 4, …) gewählt werden.

gl.glTexCoord2d(0.0f, 0.0f);

Der Befehl glTexCoord2d gibt die 2D-Textur-Koordinaten für die nachfolgenden Vertices an.

Benötigen wir pro Vertex mehrere unterschiedliche UV-Koordinaten, können wir die Funktion glMultiTexCoord2d(GLenum target, GLdouble s, GLdouble t) nutzen. Wobei ein target GL_TEXTUREi angegeben muss (i steht dabei für eine Zahl aus dem Bereich der von der Grafik-Hardware unterstützten Anzahl von GL-Texturen).

Weitere Informationen:
http://www.opengl.org/sdk/docs/man/xhtml/glMultiTexCoord.xml

gl.glVertex3f(1.0f, 1.0f, 0f);

Mit glVertex3f wird ein Vertex an der angegebenen Position erstellt. Da wir im Modus GL_QUADS zeichnen, brauchen wir nach diesem noch drei weitere Vertices für unser Plane.

« ZIEL ERREICHT: Objekte für OpenGL vorbereitet

4.2 Die Texturen vorbereiten

„Texture mapping is the art of adding variation and detail to a surface that goes beyond the level of detail modeled into the geometry.“
([BIR - Digital Lighting & Rendering] Seite 204)

« NEUES ZIEL: Texturen für Shader vorbereiten

Für viele Shader und deren Berechnungen benötigen wir spezielle Texturen. Diese werden meist aus drei Farbkanälen mit je 8bit Farbtiefe (Werte von 0 bis 255) bestehen. Diese Texturen wollen wir nun erstellen.

Teilweise werden wir jedoch mit Graustufen-Texturen arbeiten. Diese können dann in einem der Farbkanäle einer einzigen Bilddatei abgelegt werden (z.B. Huemap im roten Kanal, Brightnessmap im grünen Kanal und Alphamap im blauen Kanal). Das spart Speicher und verbessert die Performance. Zusätzlich kann auch (falls im Bildformat vorhanden) der vierte Kanal, der Alphakanal, genutzt werden.

Nicht alle Bildformate unterstützen einen Alphakanal. Die beiden Bildformate PNG und TGA unterstützen z.B. einen Alphakanal.

Abbildung in dieser Leseprobe nicht enthalten
Ein Face mit Diffuse-, Alpha-, Normal- und Specularmap

Zu den Textur-Arten nun einige Beispiele, Techniken und Lösungsansätze, die sich bei dieser Arbeit, aber auch bei meiner Arbeit als Grafiker für Computerspiele bewährt haben.

[...]

Details

Seiten
Erscheinungsform
Originalausgabe
Jahr
2009
ISBN (eBook)
9783836628716
DOI
10.3239/9783836628716
Dateigröße
1.8 MB
Sprache
Deutsch
Institution / Hochschule
Beuth Hochschule für Technik Berlin – Informatik, Medieninformatik
Erscheinungsdatum
2009 (April)
Note
1,3
Schlagworte
shader opengl glsl grafik texturing
Zurück

Titel: Shader mit GLSL
book preview page numper 1
book preview page numper 2
book preview page numper 3
book preview page numper 4
book preview page numper 5
book preview page numper 6
book preview page numper 7
book preview page numper 8
book preview page numper 9
book preview page numper 10
book preview page numper 11
book preview page numper 12
book preview page numper 13
book preview page numper 14
book preview page numper 15
book preview page numper 16
book preview page numper 17
book preview page numper 18
book preview page numper 19
book preview page numper 20
book preview page numper 21
book preview page numper 22
book preview page numper 23
book preview page numper 24
book preview page numper 25
book preview page numper 26
book preview page numper 27
book preview page numper 28
132 Seiten
Cookie-Einstellungen