Startseite > Archiv > Auf dem Weg in die dritte Dimension

Auf dem Weg in die dritte Dimension

 // DRILLDOWN
Auf dem Weg in die dritte Dimension
Craig Andera
» Bei realistischen dreidimensionalen Ausgaben kommt es nicht nur auf die richtigen Lichtquellen an. Auch die Struktur und das Material der darzustellenden Gegenstände muss berücksichtigt werden. Direct3D bietet für beides Abstraktionen, mit denen visuelle Effekte der realen Welt berechnet werden können.

| Im zweiten Teil dieses Artikels wurde eine beleuchtete Szene mit einem dreidimensionalen Dreieck dargestellt, das sich um die eigene Achse gedreht hat. Die Farbe der einzelnen Pixel dieses Dreiecks wurde ausgehend von den Eckpunkten über die Pixel interpoliert. Für viele Grafiken sind solche Farbverläufe nur sehr selten relevant. Wesentlich häufiger ist es sinnvoll, Bilder auf den Flächen der Dreiecke abzubilden.

Dadurch können Dreiecke erst wirklich als die Oberfläche eines realen Gegenstandes dargestellt werden. Ziegelsteine einer Mauer sind ein gutes Beispiel dafür. In Direct3D können Grafiken durch ein spezielles Feature mit der Oberfläche eines Dreiecks verknüpft werden. Dieses Feature wird Textur genannt.

Der englische Begriff Texture lässt sich mit Beschaffenheit, Konsistenz oder Struktur übersetzen. Texturen sind Bilder, die an ein oder mehrere Dreiecke geheftet werden. Richtig eingesetzt, können viele Objekte mithilfe von Texturen durch sehr wenige Dreiecke dargestellt werden. Das wirkt sich in den meisten Fällen sehr positiv auf die Geschwindigkeit der Bilddarstellung aus.

Das Textur-Koordinatensystem

Um zu verstehen, wie Texturen einem oder mehreren Dreiecken zugeordnet werden können, muss ein weiteres Koordinatensystem eingeführt werden. Es ist zweidimensional und heißt Textur-Koordinatensystem. Um seine Achsen nicht mit denen der anderen Koordinatensysteme zu verwechseln, werden diese mit den Buchstaben u und v benannt. Abbildung 1 zeigt ein Porträt. In Abbildung 2 sehen Sie, wie dieses Bild auf ein Dreieck abgebildet werden kann. Beachten Sie bitte, dass das Bild um 90 Grad gedreht ist. Da es auf eine dreieckige Fläche projiziert wird, wurde eine Hälfte des Bildes nicht dargestellt.

Um die Ausgabe wie in Abbildung 2 zu realisieren, ist ein weiteres Vertexformat notwendig. Es heißt PositionedNormalTextured. Ähnlich wie bei dem im vorangegangenen Teil dieses Artikels erklärten Format PositionedNormal sind zur Definition eines PositionedNormalTextured- Vertex eine dreidimensionale Position und ein Normalenvektor notwendig. Darüber hinaus müssen auch noch u- und v-Werte angegeben werden, die das Textur-Koordinatensystem definieren.

Textur-Koordinaten beziehen sich auf eine Position im Originalbild. Die Koordinaten (0,0) beziehen sich auf die obere linke Ecke des Bildes. Das Koordinatensystem ist automatisch so skaliert, dass der Wert u = 1 der Breite und v = 1 der Höhe des Bildes entspricht.

Die Koordinaten (0,1) beschreiben also den Punkt in der rechten oberen Ecke, während mit den Koordinaten (1,0) die linke untere Ecke des Bildes gemeint ist. Damit das Bild in Abbildung 1 um 90 Grad gedreht im Dreieck dargestellt wird, müssen die Koordinaten u und v also so angegeben werden, wie es in Abbildung 3 zu sehen ist.

Die in dem Bild angegebenen Koordinaten der einzelnen Vertizes sind Textur- Koordinaten. Durch die Textur-Koordinaten (1,0) im rechten unteren Vertex des Dreiecks wurde diesem Vertex die rechte obere Ecke des Bildes zugeordnet. Der linke untere Vertex des Dreiecks hat die Koordinaten (1,1).

Daher wird in der linken unteren Ecke des Dreiecks die rechte untere Ecke des Bildes dargestellt. Die Textur-Koordinaten im oberen Vertex des Dreiecks sind (0,1). An dieser Ecke wird also die linke untere Ecke des Bildes dargestellt. Durch diese Koordinaten wird die Drehung des Bildes um 90 Grad bewirkt.

Ähnlich wie Farbwerte und Normalenvektoren werden auch Textur-Koordinaten gleichmäßig über das Dreieck hinweg interpoliert. Der Bildpunkt in der Mitte der unteren Linie des Dreiecks entspricht daher dem Bildpunkt in der Mitte des rechten Bildrandes.

Texturen im Einsatz

Die bisher besprochene Theorie genügt, um die Beispielanwendung so zu implementieren, dass das Dreieck nun mit dem Urlaubsfoto unseres Protagonisten als Textur dargestellt wird. Dazu wird erst einmal die Methode InitializeGraphics angepasst. Diese Methode wurde schon in früheren Versionen der Beispielanwendung eingeführt.

private Texture texture;

protected bool InitializeGraphics()
{
 PresentParameters pres = new PresentParameters();
 pres.Windowed = true;
 pres.SwapEffect = SwapEffect.Discard;
 device = new Device(0,
     DeviceType.Hardware, this,
     CreateFlags.SoftwareVertexProcessing,
     pres);
 texture = CreateOverlayTexture(device);
 vertices = CreateVertexBuffer(device);
 return true;
}

   Die in InitializeGraphics aufgerufene Methode CreateOverlayTexture ist wie folgt eingearbeitet:

protected Texture CreateOverlayTexture( Device device) {
 Texture t = TextureLoader.FromFile(device, “texture.bmp”);
 return t;
}


Der Aufruf TextureLoader.FromFile erzeugt aus einer Datei ein Objekt vom Typ Microsoft.DirectX.Direct3D.Texture und gibt dieses zurück. Um den Typ TextureLoader verwenden zu können, muss zusätzlich die Assembly Microsoft. Direct3.Direct3DX referenziert werden. In dieser Assembly befinden sich zahlreiche Typen, die für die Entwicklung von Direct3D hilfreich sind.

Für die Ausgabe des Dreiecks muss zusätzlich zu dem Texture-Objekt ein VertexBuffer erzeugt werden.

protected VertexBuffer CreateVertexBuffer( Device device) {
 VertexBuffer buf = new VertexBuffer(
     typeof(CustomVertex.TransformedTextured), 3,
     device, 0,
     CustomVertex.TransformedTextured.Format,
      Pool.Default);
 PopulateVertexBuffer(buf);  return buf;
}

protected void PopulateVertexBuffer( VertexBuffer vertices) {
 CustomVertex.TransformedTextured[] verts =
     (CustomVertex.TransformedTextured[])  vertices.Lock(0, 0);
 int i = 0;
 verts[i++] = new CustomVertex.TransformedTextured(
     // Vertex position
     Width / 2, Height / 4, 0.5F,
     // rhw (advanced)
     1,
     // texture coordinates
     0, 1);
 verts[i++] =
     new CustomVertex.TransformedTextured(
      Width * 3 / 4, Height * 3 / 4, 0.5F,
      1,
      1, 0);
     verts[i++] = new CustomVertex.TransformedTextured(
      Width / 4 , Height * 3 / 4, 0.5F,
      1,
      1, 1);
 vertices.Unlock();
}

Wenn Sie die ersten beiden Teile dieser Serie noch in Erinnerung haben, wird Ihnen vieles von dem hier gezeigten Code bekannt vorkommen. Einzig der Typ der Vertizes hat sich verändert. Um das Beispiel so einfach wie möglich zu machen werden hier transformierte Koordinaten (zweidimensionale Fensterkoordinaten) für die Position des Dreiecks verwendet. Daher ist es auch nicht notwendig, mit Welt- und Ansichtskoordinaten und deren Transformation zu arbeiten.

Der Konstruktor des TransformedTextured- Vertexes erhält neben den üblichen Angaben wie den x- und y-Koordinaten für die Position auch die u- und v-Koordinaten des Textur-Koordinatensystems. Mit diesen Vertizes kann das Dreieck so angezeigt werden, wie es in Abbildung 4 zu sehen ist.

Dazu ist es noch notwendig, Direct3D mitzuteilen, dass das Porträtfoto als Bild für Textur-Ausgaben verwendet werden soll. Der folgende Code zeigt, wie das umgesetzt werden kann.

protected void Render() {
 device.Clear(ClearFlags.Target, Color.Black, 1.0F, 0);
 device.BeginScene();
 device.SetTexture(0, texture);
 device.VertexFormat = CustomVertex.TransformedTextured.Format;
 device.SetStreamSource(0, vertices, 0);
 device.DrawPrimitives( PrimitiveType.TriangleList, 0, 1);
 device.EndScene();
 device.Present();
}


Der Aufruf der Methode SetTexture legt hier fest, welches Texture-Objekt für Ausgaben über Vertizes mit Textur-Information verwendet werden soll.

Als zweites Argument wird das in CreateOverlayTexture erzeugte Texture- Objekt übergeben. Es kann mehr als ein aktives Textur-Objekt gleichzeitig geben. Diese können durch den ersten Parameter der Methode SetTexture unterschieden werden. Da die Beispielanwendung nur mit einem Textur-Objekt arbeitet, wird hier der Wert 0 übergeben. Listing 1 enthält den vollständigen Code der Beispielanwendung.

Materialien

Bisher wurden zwei Möglichkeiten aufgezeigt, um Objekte und deren Dreiecke farbig anzuzeigen. Es kann ein Vertex-Format verwendet werden, das Farbinformationen hat, oder es kann mithilfe eines Textur- Objektes eine Bitmap auf einem Dreieck abgelegt werden.

Es gibt jedoch noch eine weitere wichtige Möglichkeit. Diese wird Material genannt. Ein Material ist eine Sammlung von Einstellungen eines Device-Objektes, mit denen die Ausgabe und damit die Darstellung von Dreiecken beeinflusst werden kann. Diese Einstellungen werden in einem Wertetyp namens Material zusammengefasst. In einem Device-Objekt gibt es eine Eigenschaft von diesem Wertetyp, die ebenfalls Material heißt. Der folgende Code zeigt, wie Sie mit dem Material-Objekt arbeiten können.

protected void SetupMaterials() {
 Material mat = new Material();
 // Set the properties of the material
 // (Code omitted for the moment)
 device.Material = mat;
}


Der Wertetyp Material hat nur einige wenige Einstellungen. Zu diesen gehören vier unterschiedliche Farbwerte: Diffuse, Ambient, Emissive und Specular. Ähnlich wie bei Lichtquellen wird mit der Eigenschaft Diffuse die ursprüngliche Farbe des Materials angegeben. Die Eigenschaft Ambient hängt mit dem Umgebungslicht von Direct3D zusammen. Wie unlängst besprochen, gibt es unterschiedliche Arten von Lichtquellen in Direct3D. Während die drei Lichtquellen Point Light, Spot Light und Directional Light das Licht in bestimmte Richtungen ausstrahlen, ist das Ambient Light überall in gleicher Weise vorhanden. Mit der Eigenschaft Ambient des Materials kann die Farbe festgelegt werden, die ein dargestelltes Objekt annimmt, wenn es mit Ambient Light interagiert. Meist ist es sinnvoll, diese Eigenschaft auf den gleichen Wert zu setzen wie die Eigenschaft Diffuse.

Die Eigenschaft Emissive gibt die Farbe an, die ein Objekt von Natur aus emittiert, unabhängig von dem Licht, das auf das Objekt fällt. Diese Eigenschaft ist auch dann wichtig, wenn es kein Licht gibt. Sie lässt ein Objekt glühend erscheinen. Ein Objekt strahlt durch diese Eigenschaft jedoch selbst kein Licht aus. Benachbarte Objekte werden dadurch also nicht heller dargestellt.

Deshalb wird die Eigenschaft Emissive auch nur in einigen Spezialfällen – zum Beispiel zur Darstellung der Sonne oder einer Glühbirne – verwendet. In den meisten Fällen sollte diese Eigenschaft auf schwarz gesetzt werden.

Specular und SpecularSharpness

Mit der Eigenschaft Specular kann ein Effekt berücksichtigt werden, der in der Realität so oft vorkommt, dass er kaum noch wahrgenommen wird.

Dieser Effekt entsteht durch Unregelmäßigkeiten in der Oberflächenstruktur eines Materials und ist als heller Fleck auf der Oberfläche eines Objektes sichtbar. Denken Sie an ein Bild mit einem Apfel. Der Apfel selbst ist grün, aber an der Stelle, an der besonders viel Licht auf den Apfel fällt, erscheint ein heller Fleck.

Wenn die Eigenschaft Specular auf die Farbe weiß gesetzt wird, erscheint dieser Fleck weiß – vorausgesetzt, das einfallende Licht ist auch weiß. Glänzende Materialien wie zum Beispiel viele Kunststoffe können auf diese Weise gut dargestellt werden.

Wenn stattdessen eine Farbe verwendet wird, die näher an der Farbe des dargestellten Objektes liegt, erscheint der Fleck weniger hell. Solche Farbwerte werden häufiger zur Darstellung von metallischen Gegenständen verwendet.

Abbildung 5 zeigt einen Zylinder, bei dem die Eigenschaft Specular auf weiß gesetzt wurde, während die Eigenschaft in Abbildung 6 auf einen Wert gesetzt wurde, der näher an der Objektfarbe liegt. Dieser Effekt kann durch Reflexion – ein weiteres Feature von Direct3D, das hier allerdings nicht besprochen wird – noch mehr verstärkt werden.

Im Zusammenhang mit der Specular- Farbe ist auch die Eigenschaft Specular- Sharpness des Wertetyps Material von Bedeutung. Mit dieser kann die Ausdehnung des hellen Flecks beeinflusst werden. Je kleiner der Wert, desto größer wird der helle Fleck.

In Abbildung 7 wird der Zylinder mit einem SpecularSharpness-Wert von 50 dargestellt, während der Wert in Abbildung 8 auf 10 gesetzt wurde. Wenn ein niedriger SpecularSharpness-Wert zusammen mit einer dunklen Specular-Farbe verwendet wird, erscheint die Oberfläche des dargestellten Objektes matt. Wird stattdessen ein hoher SpecularSharpness- Wert und eine helle Specular-Farbe verwendet, erscheint die Oberfläche des Objektes glänzend.

Genauso wie bei der Berechnung der auf ein Objekt fallenden Lichtmenge werden auch bei der Berechnung dieses hellen Flecks die so genannten Oberflächennormalen verwendet, also die Vektoren, die im rechten Winkel zur Oberfläche stehen. Demzufolge sind auch die Beschränkungen gleich.

Je mehr Dreiecke verwendet werden, desto gleichmäßiger erscheint der helle Fleck, aber desto aufwändiger und langsamer wird die Darstellung. Um die Berechnung des hellen Flecks nur dann durchzuführen, wenn sie tatsächlich notwendig ist, gibt es die Eigenschaft Device.RenderState.SpecularEnable. Hat diese Eigenschaft den Wert false, werden die Eigenschaften Material.Specular und Material.SpecularSharpness nicht berücksichtigt und der helle Fleck wird nicht berechnet.

Mit diesen Informationen kann die Methode SetupMaterials wie folgt vervollständigt werden:

protected void SetupMaterials() {
 Material mat = new Material();

 // Set the properties of the material
 // The object itself will be blue
 mat.Diffuse = Color.Blue;

 // We want it to look slightly dull,
 // so maybe a grey wide
 highlight mat.Specular = Color.LightGray;
 mat.SpecularSharpness = 15.0F;

 device.Material = mat;

 // Very important –
 // without this there is no specularity
 device.RenderState.SpecularEnable = true;
}

Nach dem Aufruf dieser Methode werden alle Dreiecke blau und etwas glänzend dargestellt.

Wie viele andere Einstellungen auch hat die Eigenschaft Material des Device- Objektes globalen Charakter. Wenn in einer Szene also einige Objekte mit blauem Material und einige mit gelbem Material dargestellt werden sollen, dann muss die Material-Einstellung des Device-Objektes zunächst passend für die blauen Dreiecke eingestellt und es müssen die blauen Dreiecke gezeichnet werden.

Danach muss die Material-Einstellung dem gelben Material entsprechend verändert und es müssen die gelben Dreiecke gezeichnet werden. Um solche Probleme zu vermeiden, arbeitet die Beispielanwendung in Listing 2 nur mit einem Objekt und einem Material. Das dargestellte Objekt ist hier ein Zylinder, weil die Auswirkung der Eigenschaften Specular und SpecularSharpness bei runden Objekten besser zu erkennen ist.

Fazit

Mit Texturen und Materialien bietet die Bibliothek Managed Direct3D zwei Techniken, mit denen Objekte in dreidimensionalen Grafiken wirklichkeitsnah dargestellt werden können. Sinnvoll eingesetzt, ermöglichen es diese beiden Techniken, die Qualität der dargestellten Szenen wesentlich zu verbessern, ohne dass die Zahl der zur Darstellung verwendeten Dreiecke massiv erhöht werden muss.

Das kann sich in vielen Fällen auch positiv auf die Geschwindigkeit der Ausgabe auswirken, denn die Berechnung vieler einzelner Dreiecke ist häufig wesentlich aufwändiger als das Interpolieren von Texturkoordinaten und Normalenvektoren über eine geringere Zahl von Dreiecken.

Ich habe versucht, Ihnen in diesem dreiteiligen Workshop die Verwendung von Direct3D zusammen mit Windows Forms näher zu bringen. Der Einsatz dieser Grafikschnittstelle zusammen mit .NET ist möglich.

Allerdings ist die Vorgehensweise anders, als man sie von GDI+ her kennt. Ungewohnt ist zum Beispiel sicher die so genannte Game Loop, die schon im ersten Teil vorgestellt worden ist.

Gespannt darf man auf die weitere Entwicklung von Direct3D in Bezug auf NET sein. Schließlich dürfte eine komplette Integration der Grafikbibliothek in NET nicht nur im Sinne der Spieleentwickler sein.

 

»
Ressourcen
[1]  Craig Andera, Direct3D – auf dem Weg in die dritte Dimension, dotnetpro 6/2003, Seite 70 ff.
[2]  Craig Andera, Direct3D – auf dem Weg in die dritte Dimension, dotnetpro 7/8/2003, Seite 66 ff.
» Über den Autor
Craig Andera ist Trainer bei DevelopMentor und unabhängiger Consultant für .NET. Dabei ist er auf die Themen Skalierbarkeit und Sicherheit spezialisiert. Sie erreichen ihn über seine Homepage http://staff.develop.com/candera/weblog2.
Login
Sie sind nicht eingeloggt.

Login & Registrierung
Abo bestellen





Anzeige





Newsletter
Tragen Sie Ihre E-Mailadresse für den kostenlosen Newsletter von dotnetpro ein.


Umfrage
Haben Sie die Preview von Windows 8 schon ausprobiert?




Ergebnis anzeigen