• Keine Ergebnisse gefunden

Kontakt und Updates

2.2 Einführung in die 3D-Grafik

2.2.6 Das RGB-Farbsystem

// Ebene aus den drei Punkten erstellen tbPlane Plane(tbPlaneFromPoints(a, b, c));

tbWritePlaneToLog(Plane);

Listing 2.47 Berechnen einer Ebene durch drei Punkte mit der TriBase-Engine

2.2.6 Das RGB-Farbsystem

Das RGB-Farbsystem ist zwar nicht unbedingt spezifisch für 3D-Grafik – es wird auch in sehr vielen anderen Bereichen verwendet –, trotzdem ist es auf Grund seiner Einfachheit aus der 3D-Grafikprogrammierung so gut wie nicht wegzudenken.

2.2.6.1 8-Bit-Grafik ist Vergangenheit

Früher, als der von den Grafikkarten und vom System gebotene Speicherplatz noch recht be-grenzt war, benutzte man 8-Bit-Farben, um Grafiken zu speichern. Das heißt, dass jeder Pixel genau 1 Byte an Speicher verbrauchte. Jedem der damit maximal 256 verschiedenen Werte wurde ein Paletteneintrag zugeordnet, welcher die Eigenschaften der Farbe bestimmte. Zum Beispiel konnte man der Farbe 0 Schwarz zuweisen oder der Farbe 237 ein helles Pink. Frühe 3D-Spiele wie Duke Nukem 3D oder Tomb Raider arbeiteten noch mit 8-Bit-Palettengrafik.

Einige Zeit später stieg man dann auf 16-Bit-Grafik um. Außer dass ein Pixel nun genau 1 Byte mehr Speicher benötigte, gab es aber noch einen weiteren, viel wichtigeren Unterschied:

die interne Darstellung eines Pixels – also eine Zahl – basierte nun auf dem RGB-System!

Dabei wurden meistens 5 Bits für den Rotanteil, 6 Bits für den Grünanteil und 5 Bits für den Blauanteil einer Farbe auf die 16 Bits aufgeteilt. RGB steht für Red, Green, Blue – also Rot, Grün und Blau. Mit Hilfe dieser drei Grundfarben lässt sich nämlich jede vom menschlichen Auge wahrnehmbare Farbe zusammenmischen. Auch Fernseher und Computermonitore grei-fen darauf zurück. Schaut man sich deren Bildfläche einmal ganz genau an, so stellt man fest, dass jeder Bildpunkt aus drei sehr dicht nebeneinander liegenden „Farbklötzchen“ in den Far-ben Rot, Grün und Blau besteht. Diese können einzeln mit Hilfe eines Elektronenstrahls ange-steuert und unterschiedlich stark zum Leuchten angeregt werden, wodurch sich sehr viele Far-ben mischen lassen.

Das 16-Bit-Farbsystem brachte viele Vorteile mit sich: Man brauchte nun für eine Grafik kei-ne Palette mehr zu speichern, und es gab keikei-ne Probleme mehr, wenn man zwei Grafiken mit jeweils verschiedenen Paletten gleichzeitig darstellen wollte (das endete meist in einer stark reduzierten Farbpalette, in die man versuchte, wenigstens halbwegs die Farben beider Paletten einzubringen). Einen Pixel kann man mit Hilfe von Bit-Shifting und bitweisen Und-Operatoren ganz leicht in seine Farbkomponenten zerlegen, und es sind maximal 65536 (= 216) Farben gleichzeitig darstellbar, ausreichend für die meisten Fälle.

Als der Speicher der Grafikkarten und der Hauptspeicher der Computer immer größer wurde, machte man einen weiteren Schritt: die 24-Bit-Grafik und später die 32-Bit-Grafik. Beide un-terscheiden sich lediglich durch den Speicherbedarf – die Anzahl der darstellbaren Farben blieb gleich (mehr als 16.7 Millionen). Bei 32 Bits bleiben noch ganze 8 Bits für zusätzliche Farbinformationen wie zum Beispiel Transparenz (Durchsichtigkeit). Außerdem sind 32-Bit-Informationen besser (= schneller) für moderne Prozessoren ansprechbar. Für Rot, Grün und Blau sind in diesem Format ebenfalls 8 Bits reserviert.

Direct3D unterstützt für die Darstellung von 3D-Grafik schon seit einiger Zeit nur noch 16, 24 und 32 Bits pro Pixel: 8 Bits sind nur noch für Texturen erlaubt. Mittlerweile geht man sogar so weit, dass man Fließkommazahlen für die einzelnen Farbkomponenten verwendet und da-mit die Grenzen zwischen 0 und 1 verlassen kann.

2.2.6.2 Eine Farbe wird gemischt

Im 32-Bit-RGB-Farbsystem verwendet man DWORD-Werte (unsigned long), um eine Farbinfor-mation zu speichern. Dabei sind diese 32 Bits wie folgt aufgeteilt:

Rot Grün Blau Unbestimmt

Abbildung 2.15 Aufteilung der 32 Bits im 32-Bit-RGB-Farbsystem

Die vier Komponenten betrachtet man jeweils als ein Byte – 0 ist also der Minimalwert und 255 der Maximalwert. Die Farbe (127, 0, 0) entspräche zum Beispiel einem mitteldunklen Rot oder (255, 0, 255) einem Violett (Rot und Blau gleich stark vertreten).

Weiterhin ist es auch möglich, jede Farbkomponente als eine Fließkommazahl, also einen

float-Wert zu betrachten. Das ist vor allem dann vorteilhaft, wenn die Farben durch Rechen-operationen miteinander kombiniert werden, die Genauigkeit ist einfach höher. Ist dann der wirkliche Wert der Farbe erforderlich, so wandelt man die Fließkommazahlen wieder in Bytes um, wobei 0.0 der 0 entspricht und 1.0 der 255. Werte kleiner als 0.0 oder größer als 1.0 wer-den zu 0 beziehungsweise 255.

2.2.6.3 Die Klasse tbColor

In den Dateien TBCOLOR.CPP und TBCOLOR.H ist die TriBase-Klasse tbColor definiert. Man kann sie im Prinzip mit der Vektorklasse tbVector3 vergleichen. Die Klasse besitzt vier float -Variablen: r, g, b und a (Rot, Grün, Blau und Alpha – wird später für Transparenz verwendet) und definiert einige Operatoren.

Konstruktoren

Es gibt viele verschiedene Möglichkeiten, wie ein neues Farbobjekt erstellt werden kann:

// Konstruktor 1: keine Parameter tbColor a();

// Konstruktor 2: eine Fließkommazahl. Rot, Grün und Blau bekommen diesen Wert, // Alpha wird 1.

tbColor b(0.5f); // b = tbColor(0.5f, 0.5f, 0.5f, 1.0f) // Konstruktoren 3 und 4: drei bzw. vier float-Werte,

// welche die drei bzw. vier Farbkomponenten angeben (entweder mit oder ohne Alpha) // Die Werte liegen normalerweise zwischen 0 und 1.

tbColor c(1.0f, 0.0f, 0.0f); // Rot ohne Alpha (Alpha = 1) tbColor d(0.0f, 1.0f, 0.0f, 0.5f); // Halb transparentes Grün // Konstruktoren 5 und 6: drei bzw. vier BYTE-Werte.

// Der erste Parameter sollte explizit als BYTE angegeben werden, damit die Konstruktoren // von den float-Versionen unterschieden werden können. Die Werte reichen von 0 bis 255.

tbColor e((BYTE)(255), 0, 0); // Rot ohne Alpha (Alpha = 255) tbColor f((BYTE)(0), 255, 0, 128); // Halb transparentes Grün // Konstruktor 7: ein DWORD-Wert

tbColor g((DWORD)(0xFF00FF80)); // 0x80FF00FF: Halb transparentes Violett // AARRGGBB

Listing 2.48 Die Konstruktoren der tbColor-Klasse

Operatoren

Auch mit Farben kann man rechnen! Addiert man zum Beispiel zwei Farben, so ist das Ergeb-nis die additive Mischung der beiden. Oder multipliziert man eine Farbe mit einem positiven Wert kleiner als 1, dunkelt man sie ab. Durch Multiplikation mit einem Wert größer als 1 wird sie aufgehellt, bis sie irgendwann Weiß erreicht. Hier ist kein erläuterndes Listing notwendig – alles ist genau wie bei den Vektoren.

Casting

Die tbColor-Klasse arbeitet intern mit vier float-Werten; das 32-Bit-RGB-Farbsystem erfor-dert aber 32 Bits für die gesamte Farbe und nicht 32 Bits für eine einzelne Farbkomponente (ein float-Wert alleine ist aber schon 32 Bits groß). Das folgende kurze Listing zeigt, wie Sie eine Farbe ganz leicht in einen DWORD-Wert verwandeln können, welcher dem 32-Bit-Farbsystem gerecht wird (und umgekehrt):

tbColor Red(1.0f, 0.0f, 0.0f);

DWORD dwRed = (DWORD)(Red); // Hin (Casting verwenden) Red = tbColor(dwRed); // Zurück (Konstruktor verwenden) Listing 2.49 Umwandlung von tbColor nach DWORD und wieder zurück

Hilfsfunktionen

Abgesehen von Funktionen wie Minimum (tbColorMin), Maximum (tbColorMax) und Interpola-tion (tbColorInterpolate), die Sie schon von tbVector3 kennen, gibt es hier noch eine Funktion zum Berechnen des Negativs einer Farbe (tbColorNegate) und eine zur Berechnung der Hellig-keit (tbColorBrightness) zwischen 0 und 1. Das Negativ berechnet man, indem man jede Farb-komponente der Originalfarbe von 1 abzieht. Bei der Helligkeitsberechnung trägt der Rotanteil der Farbe zu 30% zur Helligkeit bei, Grün zu 60% und Blau zu 10%.

Außerdem sieht die Funktion tbColorRandom, die eine Zufallsfarbe erzeugt, ein wenig anders aus als tbVector3Random. Sie erwartet nämlich noch einen float-Parameter, welcher die Alpha-komponente der Zufallsfarbe bestimmt. Geben Sie einen negativen Wert an, um auch den Al-phawert per Zufall generieren zu lassen.

2.2.7 Rückblick

In diesem Abschnitt gab es wirklich sehr viel zu lernen! Damit Sie nicht den Überblick verlie-ren, hier eine kleine Zusammenfassung:

ƒ In der 3D-Grafik verpasst man dem zweidimensionalen kartesischen Koordinatensystem eine dritte Achse: die z-Achse, die senkrecht auf der x- und y-Achse steht und in die Tiefe zeigt. Ein Punkt wird damit durch drei Koordinaten (x, y, z) beschrieben.

ƒ Vektoren bestehen in der 3D-Grafik aus drei Komponenten: x, y und z. Ein Vektor kann entweder eine Position (Positionsvektor) oder eine Richtung (Richtungsvektor) beschrei-ben. Richtungsvektoren haben eine Länge, die sich mit dem Satz des Pythagoras berechnen lässt. Bei einem Bewegungsvektor ist die Länge gleich der Geschwindigkeit. Ein normali-sierter Vektor (der zum Beispiel für pure Richtungsangaben einer Bewegung ohne jegliche Aussage über die Geschwindigkeit verwendet wird) hat immer die Länge 1. Die TriBase-Engine stellt für die Arbeit mit zwei- und dreidimensionalen Vektoren zwei Klassen na-mens tbVector2 und tbVector3 zur Verfügung.

ƒ Dreidimensionale Objekte bestehen aus einer Ansammlung von Scheitelpunkten, die neben einem zum Objektmittelpunkt relativen Positionsvektor noch andere Angaben wie Farben besitzen. Diese werden durch Dreiecke miteinander verbunden und bilden so eine Oberflä-che. Mit Hilfe der Projektion lässt sich einer dreidimensionalen Position ein Punkt auf der zweidimensionalen Bildschirmoberfläche zuordnen.

ƒ Matrizen können verwendet werden, um Vektoren (und damit 3D-Objekte) zu transfor-mieren. Transformieren bedeutet Skalierung, Rotation, Translation (Verschiebung), Kame-ratransformation (Simulation einer Kamera mit Position und Blickrichtung) und Projekti-on. Eine Matrix wird durch die Klasse tbMatrix repräsentiert.

ƒ Ebenen sind Gebilde, die sich auf zwei Achsen im dreidimensionalen Raum unendlich weit erstrecken. Die Ebenengleichung beschreibt diejenigen Punkte, die auf einer Ebene liegen. Viele komplizierte Berechnungen lassen sich mit der Hilfe von Ebenen vereinfa-chen und beschleunigen.

ƒ In der 3D-Grafik verwendet man 16- oder 32-Bit-Grafik und das RGB-Farbsystem, bei dem eine Farbe durch drei Farbkomponenten zusammengemischt wird: Rot, Grün und Blau. Auch für eine vierte Komponente ist noch Platz, der üblicherweise für die Transpa-renz einer Farbe verwendet wird. Die Klasse für eine Farbe heißt tbColor.

2.2.8 Übungsaufgaben

1. 5000 Zufallsvektoren mit zufälliger Länge zwischen 0 und 10 sollen addiert werden. Die Summe wird durch 5000 dividiert. Dadurch lässt sich der Mittelwert (Vektor) berechnen, der ungefähr den Nullvektor (0, 0, 0) ergeben sollte. Schreiben Sie ein Programm, das dies erledigt und den Mittelwert in die Logbuchdatei schreibt.

Tipp: Verwenden Sie tbVector3Random und tbFloatRandom!

2. Schreiben Sie ein Programm, das eine Matrix und beliebig viele Vektoren aus einer Text-datei EINGABE.TXT einliest. Nach dem Einlesen sollen alle Vektoren mit der Matrix trans-formiert werden (sie sollen als Positionsvektoren betrachtet werden). Die transtrans-formierten Vektoren sollen in eine weitere Textdatei namens AUSGABE.TXT geschrieben werden.

Schaffen Sie die Transformation auch ohne tbVector3TransformCoords?

3. Es sind 5000 beliebige Positionsvektoren zu erzeugen. Dann wird eine Ebene generiert, die durch drei zufällig (aus den 5000) ausgewählte Punkte läuft. Es soll gezählt werden, wie viele der 5000 Punkte sich vor und wie viele sich hinter der Ebene befinden. Punkte, die fast genau auf der Ebene liegen (mindestens die drei ausgewählten), sind auch zu zählen.

Tipp: Verwenden Sie tbPlaneFromPoints und tbPlaneDotCoords (Vorder- oder Rückseite?).