Elemente der 3D-Grafikprogrammierung

Slides:



Advertisements
Ähnliche Präsentationen
Computer Graphics Shader
Advertisements

der Universität Oldenburg
Objektorientierte Programmierung
Definition von Klassen in Java
5. Sortier-Algorithmen Vorbemerkungen:
der Universität Oldenburg
Datenstrukturen Look-Up Tabellen, Zufallszahlen, Listen, Speichermanagement und Dateiverwaltung.
Kapitel 9.4 Blooming (Postprocess-Effekt). Postprocess-Effekte 2D-Effekte Werden auf das gerenderte Bild im Framebuffer angewendet Beispiele: Bewegungsunschärfe.
Christos, Kornelia, Jan Christos, Kornelia, Jan Entwicklungsumgebung Versteht unseren Java Programm Code Versteht unseren Java Programm.
Gliederung Motivation / Grundlagen Sortierverfahren
Java: Objektorientierte Programmierung
FH-Hof Geometrie Richard Göbel. FH-Hof Aufbau des virtuellen Universums.
Sortierverfahren Richard Göbel.
Java: Dynamische Datentypen
Listen Richard Göbel.
Sortierverfahren Richard Göbel.
Indirekte Adressierung
FH-Hof Indirekte Adressierung Richard Göbel. FH-Hof Einfache Speicherung von Daten Eine "einfache" Deklaration definiert direkt eine Speicherplatz für.
Java: Grundlagen der Objektorientierung
Strukturen. In einer Struktur kann eine beliebige Anzahl von Komponenten (Daten) mit unterschiedlichen Datentypen (im Gegensatz zu Feldern) zusammengefaßt.
Ein Beispiel in Java.
Dynamisches Array als "verkettete Liste". Ein Vergleich.
Dynamischer Speicher und Struktur
Klassenvariable. Da man für jede Kuh bzw. jede Henne auf dem Markt den gleichen Preis für ein Liter Milch, bzw. den gleichen Preis für ein Ei bekommt,
Konstruktoren.
Polymorphie (Vielgestaltigkeit)
Polymorphie (Vielgestaltigkeit)
Objekte und Arbeitsspeicher
Dynamischer Speicher. In einer Funktion wird z.B. mit der Deklaration int i; Speicher auf dem sogenannten Stack reserviert. Wenn die Funktion verlassen.
Vorlesung Informatik 2 Algorithmen und Datenstrukturen (05 – Elementare Datenstrukturen) Prof. Th. Ottmann.
Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 6 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.
Shaderprogrammierung T. Goeschel + F. Theilacker
Simulation und 3D-Programmierung Praxis: Dreiecke, Texturen, Vertex-Buffer Universität zu Köln Historisch Kulturwissenschaftliche Informationsverarbeitung.
Softwaretechnologie II (Teil 1): Simulation und 3D Programmierung
Tag 2 Look-up-Tabellen, Zufallszahlen, Listen, Speichermanagement und Dateiverwaltung Quelle: 3D-Spiele mit C++ und DirectX in 21 Tagen, Alexander.
Java3d „Licht und Material“
DVG Klassen und Objekte
Special Effects Realistischeres Rendern einer Scene.
Wir müssen also überlegen: Implementierung der Knoten, Implementierung der Kanten, daraus: Implementierung des Graphen insgesamt. Annahme: die Knoteninhalte.
PRJ 2007/1 Stefan Dissmann Verkettete datenstruktur: Liste Problem: Liste, die eine beliebige Zahl von Elementen verwaltet Operationen: Erzeugen, Anfügen,
Java programmieren mit JavaKara
FHP - Fachbereich Bauingenieurwesen
Computergraphik mit OpenGL Einführung. Bilder Objekt existiert im Raum unabhängig vom Betrachter Objekte sind beschrieben durch die Position verschiedener.
Einführung in die Programmierung
3D- Spieleprogrammierung
Javakurs FSS 2012 Lehrstuhl Stuckenschmidt
Computergraphische Visualisierungs- verfahren für 3D-Stadtmodelle
Einführung in die Programmierung Wintersemester 2008/09 Prof. Dr. Günter Rudolph Lehrstuhl für Algorithm Engineering Fakultät für Informatik TU Dortmund.
Einführung in die Programmierung Wintersemester 2009/10 Prof. Dr. Günter Rudolph Lehrstuhl für Algorithm Engineering Fakultät für Informatik TU Dortmund.
Einführung in die Informatik für Naturwissenschaftler und Ingenieure (alias Einführung in die Programmierung) (Vorlesung) Prof. Dr. Günter Rudolph Fachbereich.
Einführung in die Programmiersprache C 4
Vom Umgang mit Daten. public void myProgram() { int[] saeulenWerte = new int[world.getSizeX()]; for (int i = 0; i < saeulenWerte.length; i++) { saeulenWerte[i]
OOP-Begriffe Abstraktion Modellieren Klasse Objekt Attribute Methoden
Variablenkonzept Klassisch, in Java Basistyp
Objectives Verstehen was unterDelegate verstanden wird
Programmieren in C Grundlagen C 2
Geometric Representation
Mag. Thomas Hilpold, Universität Linz, Institut für Wirtschaftsinformatik – Software Engineering 1 Algorithmen und Datenstrukturen 1 SS 2002 Mag.Thomas.
HG13_ Herzgen, Jung & Lorkowski1 Java Programmierung BlueJ Workshop.
Schritt für Schritt-Anleitung
Pointer. Grundsätzliches: Im Arbeitsspeicher werden Daten gespeichert. Um auf die Daten eindeutig zugreifen zu können, werden diesen Daten Adressen zugeordnet.
Java Programme nur ein bisschen objektorientiert.
C++ FÜR cOMPUTERSPIELENTWICKLER
Tutorium Software-Engineering SS14 Florian Manghofer.
Strukturen (Eigenschaften) Strukturen dienen zur Zusammenfassung mehrerer Komponenten verschiedener Typen zu einer Einheit, die dann mit gemeinsamen Namen.
Pointer. * und &  Bei der Definition int var1; ○ // „normale“ Variable int *var2; ○ // Zeiger auf einen Integer int *var2 = NULL; ○ // … incl. Initialisierung.
Arrays in Java Ein Array ist eine Variable, die aus einer An-zahl von Elementen des gleichen Datentyps besteht, die aufeinanderfolgend im Speicher liegen.
Arrays in C Ein Array ist eine Variable, die aus einer Anzahl von Elementen des gleichen Datentyps besteht, die aufeinanderfolgend im Speicher liegen.
Implementieren von Klassen
 Präsentation transkript:

Elemente der 3D-Grafikprogrammierung

Render - Pipeline Aufgabe eines Grafikpakets wie DirectX: mit Grafikkarte eingehende Grafikdaten, Primitive, in Pixel auf dem Bildschirm transformieren Prozess aus mehreren Verarbeitungsschritten: Fixed Function Pipeline: 1.) Vorverarbeitung: Tesselieren: komplexe Geometrieelemente in einfache Geometrieelemente (Grafikprimitive) zerlegen Um weitere Operationen zu vereinheitlichen Z.B. Meshes immer aus Dreiecken 2.) Tranformation und Beleuchtung Geometrische Umrechnung der Eingangsdaten wird durchgeführt Teilschritte: World – Transformation View-Transformation Perspektivische Transformation

3.) Clipping: Abschneiden von Teilen, die außerhalb des Blickfeldes liegen Backface Culling: Entfernen von Rückseiten Beide Prozesse automatisch, aber Programmierer kann durch Setzen von Render-States eingreifen -> Culling abschalten: auch Rückseiten der Objekte erzeugt 4.) Texturierung Texturen bzw Bilder werden auf Grafikprimitive aufgebracht entsprechend Lichtverhältnissen eingefärbt 5.) Ergebnisse zusammenführen Farbwerte der Pixel für Bildschirmausgabe berechnen Anhand Tiefen-Information (Z-Buffer) wird entschieden, welche Pixel im Vordergrund liegen Übereinander liegende Pixel werden verschnitten

Fixed Function Pipeline: geeignet für feste Strukturen Organische Strukturen wie Gesichter, Pflanzen, Wasser mit Verformungen oder Farbenspiel -> Berechnung aller geometrischen und farblichen Übergänge im Anwendungsprogramm nötig, dann Übergabe an Grafikkarte -> dazu „programmierbare“ Render-Pipeline: Geometrische und farbliche Transformationen nah an der Hardware durchgeführt Vertexshader: zur geometrischen Transformation Pixelshader: zur Texturierung Shader: kleine Programme, die bei Bedarf in Grafikkarte geladen werden um Vertex- oder Pixelberechnungen durchzuführen Vorrangig dann, wenn spezielle geometrische pder farbliche Effekte erzielt werden sollen

7.2 Vertices und Vertexbuffer Interne Darstellung eines 3D-Modells: Datenstruktur, die performant gerendert werden kann maximale Geschwindigkeit in Render – Pipeline: am besten Arrays mit Aneinanderreihung von Geometriedaten, die als Ganzes in Grafikkarte geladen und mit einfachen Algorithmen verarbeitet werden können Verzicht auf Kopien -> es darf nur einen Originaldatensatz geben, auf dem dann sowohl Anwendungsprogramm als auch Grafiksystem arbeiten kann In DirectX: Vertexbuffer sind auf Verwendung an Schnittstelle zwischen Grafiksystem und Anwendungsprogramm hin optimiert

Daher flexibles Vertex - Format Vertexbuffer: reservierter Speicherbereich Informationen über Knoten (Vertices) eines 3D Modells sind hier abgelegt Programm kann mehrere Vertexbuffer anlegen und unabhängig voneinander verwenden in Vertexbuffer steht Aneinanderreihung von Vertices Vertices: Eckpunkte oder Knoten eines dreidimensionalen Mesh Hat Positionsdaten und ggf. weitere Daten über den Knoten wie Farbwerte, Texturkoordinaten Möglichst performant: schlichte, starre Struktur für Vertex -> einfachste Möglichkeit: immer gleiche Struktur, kann auch enorme Speichervergeudung bedeuten Daher flexibles Vertex - Format

7.2.1 Das Flexible Vertex-Format Flexibles Vertex-Format (FVF): zur Speicherung von Vertices in Vertexbuffer verwendetes Datenformat wird festgelegt Für jedes Attribut bestimmter Datentyp Position des Vertex durch Vektor D3DXVECTOR3 beschrieben, Farbe durch RGBA-Wert D3DCOLOR Zusätzlich ist verbindliche Reihenfolge der Attribute festgelegt -> Gesamtstruktur mit allen Feldern ist festgelegt, nicht verwendete Felder werden gelöscht

Vertexformat mit Positionsangabe, Normale, diffusem Farbwert: # define MEINVERTEXFORMAT (D3DFVF_XYZ I D3DFVF_NORMAL I D3DFVF_DIFFUSE) struct meinvertex { D3DXVECTOR3 pos; D3DXVECTOR3 normale; D3DCOLOR color; }; Symbolische Konstante MEINVERTEXFORMAT verwendet man später zur Reservierung von Vertexbuffer Datenstruktur meinvertex verwendet als Overlay, wenn man von Vertexbuffer schreiben oder lesen will Deklaration des Vertexformats -> dieses wird später mit Funktion SetFVF dem Device bekannt gegeben Bei Deklaration von MEINVERTEXFORMAT kommt es nicht auf die Reihenfolge einzelner Flags an die Reihenfolge der Felder in der Datenstruktur meinvertex muss sich jedoch streng an Reihenfolge der Attribute in der Tabelle orientieren -> bei abweichender Reihenfolge erhält man später falsche Werte durch unpassendes Overlay

Umfassendere Methode zur Definition von Vertexformaten: Array mit Feldbeschreibungen anlegen D3DVERTEXELEMENT9 Diesen an Funktion CreateVertexDeclaration übergeben um Vertexdeklaration zu erzeugen SetVertexDeclaration setzt Deklaration im Device -> Feldbeschreibungen D3DVERTEXELEMENT9 entsprechen in etwa symbolischen Konstanten -> Aufruf von CreateVertexDeclaration entspricht Montage der symbolischen Konstanten durch Oder – Verbindung -> SetVertexDeclaration entspricht Funktion SetFVF -> Vertexdeklaration verwendet man im Zusammenhang mit Vertex- und Pixelshadern, wenn man selbst definierte Formate in der Renderpipeline verwenden will

7.2.2 Verwendung von Vertexbuffern Vertexbuffern anlegen für ausgewähltes Vertexformat: Position, Normale, Farbe wird für Vertices benötigt Vertexbuffer bereitstellen über Memberfunktion CreateVertexBuffer aus Interface des DirectX-Device (IDirect3DDevice9) HRESULT CreateVertexBuffer (UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool, IDirect3DVertexBuffer9 **ppVertexBuffer, HANDLE pHandle) -> Länge des Buffers in Bytes, Verwendungsart, flexibles Vertexformat, Memorypool, aus dem Speicher bereitgestellt wird, Pointer für erzeugten Buffer, reservierter Parameter, muss NULL sein Als Länge übergibt man Anzahl gewünschter Vertices x Größe der Datenstruktur für einen Vertex, etwa sizeof(meinvertex) FVF-Parameter sollte zu Vertexstruktur passende Bitmaske, etwa MEINVERTEXFORMAT enthalten im Parameter ppVertexBuffer erhalten wir Funktionsergebnis, d.h. Vertexbuffer

Konkretes Beispiel für Anforderung eines Buffers für 1000 Vertices LPDIRECT3DVERTEXBUFFER9 vertexbuffer; device->CreateVertexBuffer (1000*sizeof(meinvertex), 0, MEINVERTEXFORMAT, D3DPOOL_MANAGED, &vertexbuffer, NULL); Device als Zeiger auf zuvor initialisiertes DirectX-Device Zugriff auf Vertexbuffer über Interface (LPDIRECT3DVERTEXBUFFER9) Buffer kann in anderem Adressraum als dem unseres Prozesses liegen und damit konkurrierend von anderen Prozessen verwendet werden Daher mit Funktion Lock für exklusiven Zugriff ganz oder teilweise sperren ->

HRESULT Lock (UINT OffsetToLock, UINT SizeToLock, VOID HRESULT Lock (UINT OffsetToLock, UINT SizeToLock, VOID **ppbData, DWORD Flags) Offset, ab dem gesperrt wird; Anzahl Bytes, die gesperrt werden; unspezifizierter Zeiger, der nach Aufruf auf den Buffer zeigt; Flags, die Art des gewünschten Locks bestimmen Bei erfolgreichem Aufruf erhalten wir in ppbData einen Zeiger auf reservierten Vertexbuffer konkret: meinvertex *pv; vertexbuffer->Lock(0, 0, (void**)&pv, 0); Wenn SizeToLock auf 0, wird der gesamte Vertexbuffer gesperrt Nach Funktionsaufruf finden wir in pv einen Zeiger auf Vertexbuffer

Struktur meinvertex dient dann als Overlay zum Lesen/Schreiben in Vertexbuffer for (int i=0; i<1000; i++) { pv[i].pos = D3DXVECTOR3 (1, 1, 1); pv[i].normale = D3DXVECTOR3 (0, 1, 0); pv[i].color = D3DCOLOR_ARGB (255, 255, 255, 0); } Nach Zugriff Freigabe des Vertexbuffer für andere Prozesse über Funktion Unlock: HRESULT Unlock (VOID) Freigabe über Aufruf von Release – Funktion: vertexbuffer -> Release();

7.3 Grafikprimitive 7.3.1 Grafikprimitive in DirectX Grafikprimitive: einfache geometrische Struktur, aus der komplexere Strukturen aufgebaut werden können DirectX kennt Punkte, Linien, Dreiecke als Primitive -> alle räumlichen Modelle werden in Dreiecke aufgelöst >>Triangulierung<< Vertexbuffer kann Punktliste, Linienliste, Linienzug, Dreiecksliste, Dreiecksstreifen oder Dreiecksfächer enthalten

Punktliste: D3DPT_POINTLIST Vertexbuffer enthält einzelne Punkte Linienliste: D3DPT_LINELIST Zwei aufeinanderfolgende Vertices werden als Anfangs- bzw Endpunkt einer Linie aufgefasst Anzahl der Vertices muss größer als 1 und gerade sein Linienzüge: D3DPT_LINESTRIP Können ebenfalls im Vertexbuffer modelliert werden Punkte, Linien, Linienzüge eher selten, wichtiger sind Dreiecke -> flexibelste Form dazu Dreiecksliste D3DPT_TRIANGLELIST Je drei aufeinanderfolgende Vertices bilden Dreieck Dreiecke haben Vorder- und Rückseite -> nur Vorderseite wird gerendert Vorderseite: Windungsrichtung der im Vertexbuffer aufeinanderfolgenden Eckpunkte verläuft im Uhrzeigersinn Ausblenden der Rückseite: Backface - Culling

Culling stellt man mit Funktion SetRenderState um device->SetRenderState ( D3DRS_CULLMODE, D3DCULLL_CCW); Im obigen Beispiel wird Culling so eingestellt, dass Rückseiten, deren Windungsrichtung gegen Uhrzeigersinn (CCW) verläuft, nicht dargestellt werden Anzahl zur Beschreibung geometrischer Strukturen verwendete Vertices verringern: Dreiecksstreifen (D3DPT_TRIANGLESTRIP) Erste drei Vertices bilden Dreick, jeder weitere Vertex bildet mit seinen zwei Vorgängern das nächste Dreieck Wenn Backface-Culling aktiviert: erstes Dreieck im eingestellten Cullingmode gerendert, bei jedem weiteren Dreieck wird Wichtungsrichtung umgekehrt Dreiecksfächer (D3DPT_TRIANGLEFAN): drei erste Vertices ein Dreieck, jeder weitere Vertex mit Vorgänger und erstem Vertex Culling für alle Dreiecke gleich

7.3.2 Rendern von Primitiven Vertex-Format festlegen -> Vertexbuffer allokieren -> Vertexbuffer mit Primitiven füllen -> rendern Vertexbuffer als Eingabequelle der Render-Pipeline unseres Devices festlegen: mit Funktion SetStreamSource HRESULT SetStreamSource (UINT StreamNumber, IDirect3DVertexBuffer9 *pStreamData, UINT OffsetInBytes, UINT Stride) Nummer des Streams, Vertexbuffer als Quelle, Offset zum Startpunkt im Vertexbuffer, Größe des Datensatzes für einen Vertex in Bytes StreamNumber ist logische Nummer -> mehrere Vertexbuffer als Streamsource festlegen, sofern man jedes Mal eine andere Nummer wählt Beispiel: device->SetStreamSource (0, vertexbuffer, 0, sizeof(meinvertex));

Device mitteilen, welches Vertexformat man im Vertexbuffer verwendet: HRESULT SetFVF (DWORD FVF) -> flexibles Vertex – Format Als Parameter definiertes Vertexformat übergeben Beispiel: device-> SetFVF(MEINVERTEXFORMAT); Funktion DrawPrimitive rendert Vertexbuffer HRESULT DrawPrimitive (D3DPRIMITIVETYPE PrimitiveType, UINT StartVertex, UINT PrimitiveCount) Art der Primitive, Startindex im Vertexbuffer, Anzahl der zu rendernden Primitiven idR kompletten Vertexbuffer rendern und Startindex auf 0 setzen, aber auch andere Startpunkte sind möglich Beispiel: Ab dem 10ten Vertex im Buffer 100 Dreiecke rendern: device->DrawPrimitive (D3DPT_TRIANGLELIST, 10, 100);

7.3.3 Beispiele mit Dreiecken und Linien Schritt: Festlegen des Vertexformats Für jeden Vertex Position und Farbwert im Vertexbuffer speichern # define MEINVERTEXFORMAT (D3DFVF_XYZ I D3DFVF_DIFFUSE) struct meinvertex { D3DXVECTOR3 pos; D3DCOLOR color; }; Liste von Dreiecken mit Umrandung erstellen Dazu Dreiecksliste und Linienliste rendern Voraussetzung: Device (LPDIRECT3DDEVICE9) wurde erfolgreich initialisiert Funktionalität zum Erstellen und rendern konzipieren wir als eine in sich geschlossene Klasse (trianglelist), in die von außen nur ein Zeiger auf Device eingeht

class trianglelist { private: LPDIRECT3DDEVICE9 device; LPDIRECT3DVERTEXBUFFER9 dreieckbuffer; LPDIRECT3DVERTEXBUFFER9 linienbuffer; public: trianglelist(); ~trianglelist(); void create(LPDIRECT3DDEVICE9 dev); void setup(); void render(); }; Zwei Vertexbuffer: der erste (dreieckbuffer) soll Eckpunkte der Dreiecke, der zweite (linienbuffer) Eckpunkte der Umrandung aufnehmen Fehlende Arbeitsschritte werden in öffentlichen Methoden create, setup, render abgehandelt

Konstruktor und Destruktor der Klasse: trianglelist::trianglelist() { dreieckbuffer = 0; linienbuffer = 0; } trianglelist::~trianglelist() { if (dreieckbuffer) dreieckbuffer -> Release(); if (linienbuffer) linienbuffer-> Release(); Konstruktor initialisiert Bufferzeiger Destruktor gibt Buffer frei

2. Schritt: Allokieren des Vertexbuffers Zwei Vertexbuffer: erste soll 2 Dreiecke = 6 Vertices aufnehmen, zweite soll 6 Randlinien = 12 Vertices aufnehmen Daraus ergibt sich erforderlicher Speicherplatz void trianglelist::create (LPDIRECT3DDEVICE9 dev) { device = dev; device -> CreateVertexBuffer (6*sizeof(meinvertex),0, MEINVERTEXFORMAT, D3DPOOL_MANAGED, &dreieckbuffer, NULL); device->CreateVertexBuffer (12*sizeof(meinvertex),0, MEINVERTEXFORMAT, D3DPOOL_MANAGED, &linienbuffer, NULL); } - System legt beide Buffer an, kein direkter Zugriff -> bei Zugriff auf Inhalt zuvor Funktion Lock aufrufen

3. Schritt: Befüllen des Vertexbuffers Zunächst beide Vertexbuffer für Zugriff durch unser Programm reservieren void trianglelist::setup() { meinvertex *dv, *lv; int i; dreieckbuffer -> Lock (0, 0, (void**)&dv, 0); linienbuffer ->Lock(0,0, (void**)&lv, 0); /* Zeiger auf jeweilige Bufferinhalte werden in Variablen dv und lv übertragen */ dv[o].pos = D3DXVECTOR3 (0, 0, 0); dv[1].pos = D3DXVECTOR3 (2, 1, 0); dv[2].pos = D3DXVECTOR3 (2, 0, 0); // … bis dv[5] /* Koordinatenwerte für sechs Eckpunkte der beiden Dreiecke eintragen dv[0] bis dv[5] in Dreiecks- bzw Linienpuffer */

dv[0].color = D3DCOLOR_ARGB (255, 80, 80, 80); // .. Bis dv[5] /* für Eckpunkte der Dreiecke unterschiedliche Farbwerte */ lv[0].pos = dv[0].pos; // 12 Eckpunkte der 6 Randlinien lv[1].pos = dv[1].pos; lv[2].pos = dv[1].pos; lv[3].pos = dv[2].pos; lv[4].pos = dv[2].pos; lv[5].pos = dv[0].pos; lv[6].pos = dv[3].pos; lv[7].pos = dv[4].pos; lv[8].pos = dv[4].pos; lv[9].pos = dv[5].pos; lv[10].pos = dv[5].pos; lv[11].pos = dv[3].pos;

for (i = 0; i<12; i++) lv[i].color=D3DCOLOR_ARGB(255, 0, 0, 0); // Eckpunkte der Randlinien schwarz dreieckbuffer->Unlock(); linienbuffer->Unlock(); } // Vertexbuffer freigeben - Beide Buffer sind jetzt bereit zum rendern

4. Schritt: Rendern der Primitiven im Vertexbuffer Rendern besteht aus wenigen Funktionsaufrufen Zunächst mit Funktion SetFVF richtiges Vertexformat setzen Danach für jeden Buffer Funktionen SetStreamSource und DrawPrimitive aufrufen void trianglelist::render() { device->SetRenderState (D3DRS_AMBIENT, 0xffffff); // Beleuchtung: Ambient Licht einschalten device->SetRenderState (D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_COLOR1); // Farbwerte aus diffusen Farbkomponente des Vertex verwenden device ->SetFVF (MEINVERTEXFORMAT); device->SetStreamSource (0, dreieckbuffer, 0 sizeof(meinvertex)); device->DrawPrimitive (D3DPT_TRIANGLELIST, 0, 2); devide->SetStreamSource(0, linienbuffer, 0 sizeof(meinvertex)); device->DrawPrimitive(D3DPT_LINELIST, 0, 6); } Aufruf der Methoden create und setup sowie regelmäßiges Rendern ergibt gewünschtes Bild

Unterschiedliche Farbtöne an den Ecken der Dreiecke führen zu Farbverläufen Ecken dv[2] und dv[3] haben gleiche Position, unterscheiden sich aber in Farbe -> da Vertices zu unterschiedlichen Dreiecken gehören, hätten wir auf keines von beiden verzichten können Wenn alle Dreiecke in der gleichen Farbe und ohne Farbverläufe dargestellt werden sollen, wäre Farbinfo für jeden Vertex Speicherplatzvergeudung Stattdessen Material in gewünschter Farbe anlegen: D3DMATERIAL9 material; ZeroMemory (&material, sizeof (material)); Material.Ambient = D3DXCOLOR (255, 255, 0, 0); Material in render-Funktion auswählen und durch SetRenderState festlegen, dass Material verwendet werden soll: device->SetMaterial (&material); device->SetRenderState (D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL);

7.3.4 Beispiel mit Punktlisten (Partikelsysteme) Dreiecke oder Linien als am häufigsten genutzte Primitive Punktlisten zur Realisation von Partikelsystemen = 3-dimensionale Punktwolken um etwa Rauch oder Schnee zu implementieren verwenden gleiches Vertexformat wie zuvor define MEINVERTEXFORMAT (D3DFVF_XYZ I D3DFVF_DIFFUSE) struct meinvertex { D3DXVECTOR3 pos; D3DCOLOR color; };

Jeder Punkt /Partikel hat eigene Position, kann eigenständig bewegt werden, hat eigene Farbe Beispiel: Kugeloberfläche als Wolke aus einzelnen Punkten zusammensetzen -> größer werden lassen - > zerstäuben class partikelsystem { private: LPDIRECT3DDEVICE9 device; LPDIRECT3DVERTEXBUFFER9 partikelbuffer; int anzahl; float radius; public: partikelsystem (); ~partikelsystem(); void create( LPDIRECT3DDEVICE9 dev, int anz); void setup(); void blowup(); void render (); void lookatme (…} };

Create – Methode: void partikelsystem::create(LPDIRECT3DDEVICE9 dev, int anz) { device = dev; anzahl = anz; device ->CreateVertexBuffer (anzahl*sizeof(meinvertex), 0, MEINVERTEXFORMAT, D3DPOOL_MANAGED, &partikelbuffer, NULL); } Anwender kann wählen, wie viele Partikel er haben will ->entsprechend wird Vertexbuffer bereit gestellt

Setup – Funktion: Anfangspositionen einzelner Partikel in Buffer eingetragen void partikelsystem::setup() { int i; meinvertex *pv; srand (123); radius = 0.1f; partikelbuffer->Lock (0, 0, (void**)&pv, 0); for (i=0;i < anzahl; i++) pv[i].pos.x = ((float)rand())/RAND_MAX – 0.5f; // für jedes Partikel durch Zufalls- pv[i].pos.y = ((float)rand())/RAND_MAX – 0.5f; // Zufallskoordinaten ein // Punkt in Würfel pv[i].pos.z = ((float)rand())/RAND_MAX – 0.5f; // um den Ursprung bestimmt D3DXVec3Normalize (&pv[i].pos, &pv[i].pos); // Normalisierung des Punktes pv[i].color = D3DCOLOR_ARGB (255, (int) (128*(pv[i].pos.x +1)), (int) (128*(pv[i].pos.y +1)), (int) (128*(pv[i].pos.z +1))); // Auswahl eines Farbwertes für Partikel pv[i].pos = radius * pv[i].pos; // Sphäre auf Anfangsgröße verkleinern } partikelbuffer -> Unlock();

Funktion blowup: Aufblasen der Sphäre, bei jedem Aufruf um 10% bis Maximalwert des Radius 10 Radius 10 erreicht -> Funktion setup setzt Sphäre in Ausgangszustand void partikelsystem::blowup() { int i; meinvertex *pv; radius *= 1.1f; if (radius <10) partikelbuffer ->Lock (0, 0, (void**)&pv, 0); for (i = 0; i < anzahl; i++) pv[i].pos = 1.1f * pv[i].pos; partikelbuffer->Unlock(); } else setup ();

Render – Funktion nimmt Beleuchtungseinstellungen vor und bringt Vertexbuffer zur Darstellung void partikelsystem::render () { device->SetRenderState (D3DRS_AMBIENT, 0xffffff); device->SetRenderState (D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_COLOR1); device->SetFVF (MEINVERTEXFORMAT); device ->SetStreamSource (0, partikelbuffer, 0, sizeof (meinvertex)); device -> DrawPrimitive (D3DPT_POINTLIST, 0, anzahl); } Effekt des Zerbröselns: die dem Auge näher liegenden Partikel größer und weiter entfernt liegende Partikel kleiner -> Pointscaling D3DRS_POINTSCALEENABLE aktivieren

Skalierung für Partikel einschalten und Konstanten A, B und C setzen: inline DWORD FtoDW (FLOAT f) { return *((DWORD*) &f);} void partikelsystem::render () device ->SetRenderState (D3DRS_POINTSCALEENABLE, TRUE); device ->SetRenderState (D3DRS_POINTSIZE, FtoDW(0.1f)); device->SetRenderState (D3DRS_POINTSCALE_A, FtoDW (0.0f)); device->SetRenderState (D3DRS_POINTSCALE_B, FtoDW (0.0f)); device ->SetRenderState (D3DRS_POINTSCALE_C, FtoDW (1.00f)); device ->SetRenderState (D3DRS_AMBIENT, 0xffffff); device ->SetRenderState (D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_COLOR1); device ->SetFVF (MEINVERTEXFORMAT); device ->SetStreamSource (0, partikelbuffer, 0, sizeof (meinvertex)); device -> DrawPrimitive (D3DPT_POINTLIST, 0, anzahl); } Beim Setzen der Konstanten - Hilfsfunktion FtoDW: Funktion SetRenderState akzeptiert nur DWORD, aber eine Gleitkommazahl übergeben -> kopiert float in Speicherbereich, der für DWORD vorgesehen ist