vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#
Top-Preis! AP-Access-Tools-CD Volume 1  
 vb@rchiv Quick-Search: Suche startenErweiterte Suche starten   RSS-Feeds  | Newsletter  | Impressum  | Datenschutz  | vb@rchiv CD Vol.6  | Shop Copyright ©2000-2017
 
zurück
Rubrik: Grafik/DiretxX   |   VB-Versionen: VB5, VB615.12.02
Einführung in Direct3D

Dieses Tutorial richtet sich an erfahrene VisualBasic-Entwickler, die mit der 3D-Grafik-Programmierung interessantes Neuland entdecken wollen. Für das volle Verständnis des Beispielcodes werden jedoch Grundsätze der Vektormathematik benötigt.

Autor:  Nico HeidtkeBewertung:     [ Jetzt bewerten ]Views:  13.654 

Neue Version! sevEingabe 3.0 (für VB6 und VBA)
Das Eingabe-Control der Superlative! Noch besser und noch leistungsfähiger!
Jetzt zum Einführungspreis       - Aktionspreis nur für kurze Zeit gültig -

Dieses Tutorial richtet sich an erfahrene VisualBasic-Entwickler, die mit der 3D-Grafik-Programmierung interessantes Neuland entdecken wollen. Für das volle Verständnis des Beispielcodes werden jedoch Grundsätze der Vektormathematik benötigt.

1. Vorbereitungen

  • Installieren Sie DirectX 8, sofern nicht schon vorhanden.
    (Downloadmöglichkeit: Software >  Grundausstattung)
     
  • Starten Sie die VB-IDE und Erstellen Sie ein neues Standard-Exe Projekt
     
  • Aktivieren Sie im Menü "Projekt - Verweise" den Eintrag DirectX 8 for VisualBasic Type Library
     
  • Fügen Sie dem Projekt noch ein Modul hinzu

Direct3D:
Direct3D ist ein Bestandteil der Multimedia-Schnittstelle DirectX von Microsoft.
Mit Direct3D ist es möglich einen 3-dimensionalen Raum 2-dimensional auf dem Bildschirm zu simulieren.
Den Grossteil der hierzu nötigen Mathematik übernimmt Direct3D. Als Programmierer kann man sich dadurch besser auf das eigentliche Ziel der Programmierung mit Direct3D konzentrieren. Die Darstellung, Interaktion und Simulation von 3-dimesionalen Objekten in einer 3D-Welt, ein ordentliche Portion Mathematik und räumliches Vorstellungsvermögen ist jedoch immer noch vonnöten, um zu verstehen was man eigentlich tut, wenn man Direct3D benutzt.

Das Ziel:
Ziel dieses Workshops ist es, Ihnen einen Einblick in die Welt der 3D-Grafik-Programmierung zu geben und Ihnen Lust auf weiteres Erforschen von Direct3D zum machen.
Wahrscheinlich werden Sie auf den ersten Blick (fast) gar nichst verstehen und auf den zweiten nur die Hälfte. Das macht aber nichts, denn sicherlich geht es den Meisten ebenfalls so. Einige Sachen muss man erst einmal hinnehmen, wie sie einem präsentiert werden. Es wäre einfach auch zuviel verlangt, dass man alles auf einmal versteht. Wenn man jedoch die Grundsätze erst verstanden hat, dann hat man nach und nach eine riesige, benahe unendliche Fülle von Möglichkeiten.

Doch beginnen wir ohne weitere Umschweife...

2. Deklarationen

Zunächst müssen wir ein paar Objeklt-Variablen deklarieren. Die Deklaration erfolgt hierbei in einem Modul. Fügen Sie nachfolgenden Code in das Modul ein, den Sie sich auch sorgfältig durchlesen sollten!

Option Base 0
Option Explicit
 
' Deklaration aller benötigten Objekt-Variablen
 
' Das ist unser globales DirectX8 Objekt
Public myDX8 As New DirectX8
 
' Dies ist unser Direct3D-Objekt
Public myD3D8 As Direct3D8
 
' Das D3DDevice ist sozusagen das Objekt, 
' das die 3DGrafik zeichnet und berechnet
Public D3DDevice As Direct3DDevice8

Wenn wir Direct3D benutzen, müssen wir immer auch sicherstellen, dass der PC, auf dem das Programm ausgeführt wird, alle benötigten Features unterstützt. Andernfalls kann es zu schweren Fehlern kommen. Deshalb sollte man zunächst ermitteln, was die Grafikkarte des PCs leisten kann und was nicht, was man sich natürlich irgendwo merken (zwischenspeichern) muss.

' Hier legen wir fest, ob die Anwendung z.B.
' Fullscreen benutzen soll
Public D3DPP As D3DPRESENT_PARAMETERS
Public DispMode As D3DDISPLAYMODE
 
' In dieser Variable speichern wir, ob ein 
' 3D-Beschleuniger vorhanden ist
Public D3D_HwOrSw As Long
 
' Das soll unsere Fenstergröße sein
Public Const ScreenWidth As Long = 640
Public Const ScreenHeight As Long = 480
 
' Dies hier sind Matrizen für den 3D-Raum. Man benötigt
' Matrizen, um mathematische Operationen im 3D-Raum
' möglichst effizient umzusetzen.
Public matWorld As D3DMATRIX
Public matView As D3DMATRIX
Public matProj As D3DMATRIX
 
' Mit dieser Matrix können wir bequem die Szene
' rotieren, so dass das Ganze interessanter aussieht
Public matRotation As D3DMATRIX
 
' Die aktuelle Rotation
Public Rotation As Double
 
' Pi brauch man auch oft für Berechnungen
Public Const Pi = 3.14159265358979

Natürlich brauchen wir auch etwas zum Darstellen. Das speichern wir in diesem Typ:

Public Type D3DColorVertex
  x As Single
  y As Single
  z As Single
  color As Long
End Type
 
' Mit dieser Konstanten sagen wir später dem Device wie 
' es unseren selbst definierten Typ zu verstehen hat.
Public Const D3DFVF_COLORVERTEX = D3DFVF_XYZ Or _
  D3DFVF_DIFFUSE
 
' Und davon nehmen wir 12 Stück
Public Triangles(11) As D3DColorVertex
 
' Wird die Anwendung noch ausgeführt?
Public bRunning As Boolean

3. Die Sub Main

Wenn wir es mit Direct3D zu haben, brauchen wir eigentlich keine Forms oder Steuerelemente. Deshalb sollte man auch keine Form als Start-Objekt definieren, sondern stattdesse die Sub Main. In der DirectX-Programmierung hat sich das eben so eingebürgert

Die "Sub Main" sollte gut gegliedert und nicht zu umfangreich werden.

Folgender Code kommt ebenfalls in das Modul:

Public Sub Main()
  ' Anzeigen unserer Form im Vollbild-Modus
  With Form1
    .Show
    .Width = ScreenWidth * Screen.TwipsPerPixelX
    .Height = ScreenHeight * Screen.TwipsPerPixelY
  End With
 
  ' Start der Initialisierung
  ' Wenn die Initialisierung von Direct3D fehlschlägt,
  ' soll mit dem Programm gar nicht erst fortgefahren
  ' werden
  bRunning = InitD3D
 
  ' Hier regeln wir alle sonstigen Dinge die noch zu
  ' erledigen sind, bevor es dann los gehen kann
  If bRunning Then InitSettings
 
  ' Danach initialisieren wir unsere Geometrie, die wir 
  ' rendern (zeichnen) wollen
  If bRunning Then InitTriangles
 
  ' Direct3D Anwendungen müssen in einem Loop ausgeführt
  ' werden, in dem die Szene immer wieder neu gezeichnet
  ' wird. Diesen Loop nennen wir MainLoop
  Mainloop
 
  ' Wenn man mit DirectX arbeitet ist es immer wichtig
  ' sämtliche Objekte wieder freizugeben, da sie einem
  ' onst den Speicher "vollmüllen"
  TerminateD3D
 
  ' Und jetzt noch die Form entladen
  Unload Form1    
End Sub

Nachdem Sie den Code eingefügt haben, müssen Sie noch die Sub Main als Startobjekt definieren. Öffnen Sie hierzu das Projekt-Eigenschaften Dialogfenster (Menü Projekt - Eigenschaften von...), klicken auf das Register "Projekt" und wählen dort als Startobjekt "Sub Main" aus.

Nun wollen wir uns mit den einzelnen Funktionen beschäftigen...

4. Die Initialisierung der benötigten Objekte

Für die Initialisierung erstellen wir eine eigene Funktion: Init3D3. Erfolgt die Initialisierung erfolgreich bekommen wir ein Device. Ein Device kann man mit einer PictureBox vergleichen: es enthalt die Funktionen und Werte, über die man das Zeichnen auf innerhalb des Device steuern kann.

Die Initialisierungs-Funktion kommt ebenfalls wieder ins Modul. Schauen Sie sich den Code genau an, um zu verstehen, was alles wie initialisier werden sollte.

Public Function InitD3D() As Boolean
  ' Wichtig! Fehlerbehandlung aktivieren
  On Error Resume Next
 
  ' Zuerst müssen wir unser Direct3D-Objekt aus dem
  ' DirectX-Objekt erstellen
  Set myD3D8 = myDX8.Direct3DCreate
 
  ' Nun ermitteln wir noch, welchen Bildschirmmodus die
  ' Grafikkarte momentan hat...
   myD3D8.GetAdapterDisplayMode D3DADAPTER_DEFAULT, _
     DispMode
 
  ' ...und setzen anschließend unsere Wunschauflösung
  DispMode.Width = ScreenHeight
  DispMode.Height = ScreenHeight
 
  ' Dann schauen wir, ob die Grafikkarte
  ' 3D-Beschleunigung unterstützt
  If myD3D8.CheckDeviceType(D3DADAPTER_DEFAULT, _
    D3DDEVTYPE_HAL, DispMode.Format, _
    DispMode.Format, 1) = D3D_OK Then
 
    ' Hardwarebeschleunigung wird unterstützt
    D3D_HwOrSw = D3DDEVTYPE_HAL
  Else
    ' Kein Harwarebeschleunigung: Der Prozessor muss
    ' alles selbst machen
    D3D_HwOrSw = D3DDEVTYPE_REF
  End If
 
  With D3DPP
    ' Diesen Teil sollten Sie einfach einmal
    ' schlucken :-)
    .BackBufferCount = 1
    .EnableAutoDepthStencil = 0
    .SwapEffect = D3DSWAPEFFECT_FLIP
    .BackBufferFormat = DispMode.Format
    .AutoDepthStencilFormat = D3DFMT_D16
    .EnableAutoDepthStencil = 1
 
    ' Wir wollen Fenster und nicht Fullscreen
    .Windowed = 1
 
    ' Hiermit legen wir fest, wie groß das Farbspektrum
    ' unseres Fenster sein soll.
    ' Da wir kein Fullscreen möchtenm, wird automatisch
    ' die Farbtiefe des Desktops genommmen
    .BackBufferFormat = DispMode.Format
 
    ' Die Fenstergröße nehmen wir auch für den BackBuffer.
    ' Auf dem BackBuffer kann z.B. Anisotrope filterung
    ' vorgenommen werden, bevor die Szene gerendert wird
    .BackBufferWidth = ScreenWidth
    .BackBufferHeight = ScreenHeight
 
    ' Dann teilen wir dem Device noch mit, in welchem
    ' Fenster es 'Rendern' (=zeichnen) soll.
    ' Hierzu nehmen wir einfach die hWnd-Eigenschaft
    ' unserer Form1
    .hDeviceWindow = Form1.hWnd
  End With
 
  ' Nun erstellen wie unser Device.
  ' Wir benutzen Softwware-Vertex-Processing, weil nur
  ' neuere GeForce-Karten Harware-Vertex-Processing
  ' unterstützen. Das könnte man zwar auch abfragen,
  ' aber für den Anfang reicht das erst mal ;)
  Set D3DDevice = myD3D8.CreateDevice(0, D3D_HwOrSw, _
    Form1.hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, _
    D3DPP)
 
  ' So, dann schauen wir doch mal, ob es
  ' funktioniert hat
  If D3DDevice Is Nothing Then
    ' Das Device ist leer, also haben wir was
    ' falsch gemacht
    InitD3D = False
  Else
    ' Das Device ist da!
    InitD3D = True
  End If
End Function

5. Geometrie und Matrizen

Damit es was zu sehen gibt, brauchen wir Objekte. Objekte beliebiger Form kann man aus Dreicken (=Triangles) zusammenbauen. Ein Dreieck (auch 'Polygon') kann man wiederum aus 3 Punkten im Raum definieren. Genau das tun wir jetzt: vier Dreicke aus 12 Punkten basteln. Wenn man diesen Punkten mehr als nur Koordinaten mitgibt z.B. Farbe etc., nennt man sie VERTEX.

Diesen Code bitte auch in das Modul kopieren:

Public Sub InitTriangles()
  Triangles(0) = MakeColorVertex(10, 10, 10, _
    D3DColorARGB(0, 255, 0, 0))
  Triangles(1) = MakeColorVertex(10, -10, 0, _
    D3DColorARGB(0, 0, 255, 0))
  Triangles(2) = MakeColorVertex(10, 10, 0, _
    D3DColorARGB(0, 255, 255, 0))
  Triangles(3) = MakeColorVertex(-10, -10, 10, _
    D3DColorARGB(0, 255, 0, 0))
  Triangles(4) = MakeColorVertex(-10, 10, 0, _
    D3DColorARGB(0, 0, 0, 255))
  Triangles(5) = MakeColorVertex(-10, 10, 10, _
    D3DColorARGB(0, 0, 255, 255))
  Triangles(6) = MakeColorVertex(10, 15, 10, _
    D3DColorARGB(0, 255, 255, 0))
  Triangles(7) = MakeColorVertex(0, 15, 0, _
    D3DColorARGB(0, 255, 0, 255))
  Triangles(8) = MakeColorVertex(-10, 15, 10, _
    D3DColorARGB(0, 255, 0, 255))
  Triangles(9) = MakeColorVertex(-5, -15, 10, _
    D3DColorARGB(0, 0, 255, 255))
  Triangles(10) = MakeColorVertex(0, -15, 0, _
    D3DColorARGB(0, 0, 255, 0))
  Triangles(11) = MakeColorVertex(10, -15, 10, _
    D3DColorARGB(0, 255, 0, 255))
End Sub
' eine kleine Hilfs-Funktion
 
Public Function MakeVector(ByVal x As Single, _
  ByVal y As Single, _
  ByVal z As Single) As D3DVECTOR
 
  With MakeVector
    .x = x
    .y = y
    .z = z
  End With
End Function
' noch eine kleine Hilfsfunktion
Public Function MakeColorVertex(ByVal x As Single, _
  ByVal y As Single, _
  ByVal z As Single, _
  ByVal c As Long) As D3DColorVertex
 
  With MakeColorVertex
    .x = x
    .y = y
    .z = z
    .color = c
  End With
End Function

Somit habe wir 4 Triangles erstellt. Aus den ganzen Zahlen dort müssen Sie nicht schlau werden. Es genügt, wenn Sie verstanden haben, wie man aus 3 Eckpunkten ein Dreieck bastelt.

Damit alles einwandfrei funktioniert müssen wir aber noch definieren, wie von wo aus wir uns diese 3D-Welt anschauen. Dazu benutzt man verschiedene Matrizen, die Ort des "Auges", Blickwinkel, Verzerrungen und Rotation festlegen. Außerdem muss man dem Device auch noch mitteilen, wie es unsere Geomtrie-Daten zu verstehen hat.

Kopieren Sie folgenden Code wiederum in das Modul:

Public Sub InitSettings()
  ' Das hier sollte Sie erst einmal auch nicht kümmern.
  ' Hier wird mit Hilfe einer Matrix festgelegt, wie
  ' die einzelnen Objekte zu rendern sind.
  D3DXMatrixIdentity matWorld
  D3DDevice.SetTransform D3DTS_WORLD, matWorld
 
  ' Diese Matrix kann man als Kamera betrachten, mit
  ' der sich das Sichtfeld modifizieren lässt.
  D3DXMatrixLookAtLH matView, MakeVector(40, 40, 40), _
    MakeVector(0, 0, 0), MakeVector(0, 1, 0)
  D3DDevice.SetTransform D3DTS_VIEW, matView
 
  ' Mit der Projektion-Matrix legen wir Sichtweite und
  ' Sichtwinkel fest.
  D3DXMatrixPerspectiveFovLH matProj, Pi / 4, 1, 0.01, 200
  D3DDevice.SetTransform D3DTS_PROJECTION, matProj
 
  ' Jetzt teilen wir dem Device noch mit, welches
  ' Art Vertex wir haben
  D3DDevice.SetVertexShader D3DFVF_COLORVERTEX
 
  ' Und noch ein paar Einstellungen
  D3DDevice.SetRenderState D3DRS_ZENABLE, 1
  D3DDevice.SetRenderState D3DRS_LIGHTING, 0
  D3DDevice.SetRenderState D3DRS_FILLMODE, D3DFILL_SOLID
  D3DDevice.SetRenderState D3DRS_CULLMODE, D3DCULL_NONE
End Sub

6. Die Haupt-Schleife

Die Hauptschleife enthält alle sich ständig wiederholenden Vorgänge. Dazu gehört:

  • Der "Mal-Vorgang" im 3D-Bereich oder besser als "Rendern" bezeichnet. Der Render-Vorgang muss ständig wiederholt werden, am besten so schnell wie möglich. Je öfter die Hauptschleife pro Sekunde durchlaufen werden kann, desto besser. Die Wiederholungs-Rate nennt man auch "Framerate" oder "fps", was englisch für "frames per second" steht. Ist die "Framerate" zu niedrig, dann ruckelt das Bild. 30 fps sollten das Minimum sein, um eine fürs Auge angenehme Darstellung zu Erreichen.
     
  • Um das ganze interessanter zu machen haben wir noch eine kleine Roation der 3DWelt eingebaut. Diese Rotation muss natürlich auch berechnet werden.
     
  • Wir müssen auch regelmäßig die Steuerung an das Betriebssystem mit dem "DoEvents"-Befehl zurückgeben, damit das Betriebssystem weiterhin unsere Eingaben von Maus und Tastatur entgegennimmt und auswertet.

Nachfolgenden Code bitte auch in das Modul einfügen:

Public Sub Mainloop()
  ' Der MainLoop läuft solange wie wir die
  ' bRunning-Variable auf 'true' lassen
  Do While bRunning
    ' Das vorher Gezeichnete löschen, indem wir es mit
    ' grauer Farbe übermalen
    D3DDevice.Clear 0, ByVal 0, D3DCLEAR_TARGET Or _
      D3DCLEAR_ZBUFFER, &H333333, 1#, 0
 
    ' Dem Device sagen, dass das "rendern gleich
    ' beginnt"
    D3DDevice.BeginScene
 
    ' Und nun lassen wir das ganze noch rotieren
    Rotation = Rotation + 0.05
 
    D3DXMatrixIdentity matWorld
 
    D3DXMatrixRotationY matRotation, Rotation * Pi / 180
    D3DXMatrixMultiply matWorld, matWorld, matRotation
    D3DXMatrixRotationX matRotation, Rotation * 1.5 * Pi / 180
    D3DXMatrixMultiply matWorld, matWorld, matRotation
    D3DXMatrixRotationZ matRotation, Rotation * 2 * Pi / 180
    D3DXMatrixMultiply matWorld, matWorld, matRotation
 
    D3DDevice.SetTransform D3DTS_WORLD, matWorld
 
    ' Hier "rendern" wir unsere Szene
    D3DDevice.DrawPrimitiveUP D3DPT_TRIANGLELIST, 4, _
      Triangles(0), Len(Triangles(0))
 
    ' Rendern beendet
    D3DDevice.EndScene
 
    ' Und das Ganze "präsentieren"
    D3DDevice.Present ByVal 0, ByVal 0, 0, ByVal 0
 
    ' Jeder, der schon mal einen Loop ohne Beendingung
    ' Beding gebastelt hat, weiß, dass man ein
    ' Doevents braucht, um noch Eingaben in seinen
    ' PC machen zu können
    DoEvents
 
  Loop
End Sub

7. Beenden einer Direct3D Applikation

Wie bereits erwähnt sollten alle erstellten Objekte bei Direct3D-Anwendungen zerstört werden, wenn die Aplikation beendet wird. Dies wird mit wachsender Geometrie auch immer wichtiger!

Erledigt wird das auf folgende Weise. Bitte in das Modul einfügen:

Public Sub TerminateD3D()
  ' Alle Objekte zerstören
  Set D3DDevice = Nothing
  Set myD3D8 = Nothing
  Set myDX8 = Nothing
End Sub

Nun haben wir ja einen Endlos-Loop namens MainLoop gebastelt. Da es aber im normalerweise gewünscht ist die Anwednung zu einem beliebigen Zeitpunkt beenden zu können, halten wir uns diese Möglcihkeit offen, indem wir einfach auf ein Mausklick oder Tastendruck-Ereignis die "bRunning"-Variable auf "False" setzen.

Fügen sie bitte folgenden Code in die Form1 ein:

Private Sub Form_Click()
  ' bei Mausklick soll die Anwendung beendet werden
  bRunning = False
End Sub
Private Sub Form_KeyDown(KeyCode As Integer, _
  Shift As Integer)
 
  ' Auf Tastendruck soll man auch beenden können
  bRunning = False
End Sub
Private Sub Form_Unload(Cancel As Integer)
  ' Sicherzustellen, dass TerminateD3D aufgerufen
  ' wird BEVOR wir die Anwendung beenden
  If bRunning Then
    bRunning = False
    ' Entladen abbrechen
    Cancel = 1
  End If
End Sub

Wenn Sie dieses Projekt jetzt ausführen, dann sollten sie 4 bunt-rotierende Dreiecke in einem dunkelgrauem Fenster sehen.

Wenn alles geklappt hat, dann können Sie sich jetzt daran machen Schritt für Schritt die Welt von Direct3D zu erforschen.

Bei Fragen zu diesem Workshop oder auch allgemeinen Fragen zur DirectX-Programmierung wenden Sie sich bitte an direkt an unser  DirectX-Forum im vb@rchiv.

Dieser Workshop wurde bereits 13.654 mal aufgerufen.

Über diesen Workshop im Forum diskutieren
Haben Sie Fragen oder Anregungen zu diesem Workshop, können Sie gerne mit anderen darüber in unserem Forum diskutieren.

Aktuelle Diskussion anzeigen (3 Beiträge)

nach obenzurück


Anzeige

Kauftipp Unser Dauerbrenner!Diesen und auch alle anderen Workshops finden Sie auch auf unserer aktuellen vb@rchiv  Vol.6

Ein absolutes Muss - Geballtes Wissen aus mehr als 8 Jahren vb@rchiv!
- nahezu alle Tipps & Tricks und Workshops mit Beispielprojekten
- Symbol-Galerie mit mehr als 3.200 Icons im modernen Look
Weitere Infos - 4 Entwickler-Vollversionen (u.a. sevFTP für .NET), Online-Update-Funktion u.v.m.
 
   

Druckansicht Druckansicht Copyright ©2000-2017 vb@rchiv Dieter Otter
Alle Rechte vorbehalten.
Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein.

Diese Seiten wurden optimiert für eine Bildschirmauflösung von mind. 1280x1024 Pixel