Laden...
H
hauptmann myCSharp.de - Member
Schüler Österreich/Kärnten Dabei seit 19.07.2003 704 Beiträge
Benutzerbeschreibung

Forenbeiträge von hauptmann Ingesamt 704 Beiträge

15.08.2005 - 18:55 Uhr

Ich muss DSL 768/128 kbit verwenden und habe nur 8gb (ab September wenigstens 10gb) Datenvolumen/Monat.
Viel, viel zu wenig und viel zu langsam 😁

Naja, laut den Politikern hier soll ja bald alles auf Breitband ausgebaut werden und dann bekomme ich endlich eine xDSL Flatrate. -.-

14.08.2005 - 16:59 Uhr

Ich bin selbst nur Hobbyprogrammierer (erst 16 -.-) habe aber mal vor später nach der Matura(Abitur) in Richtung IT zu studieren, jedoch eher was in Sachen Elektrotechnik.
Aber derzeit besuche ich bereits eine Schule (HTL) für EDV und Organisation/Software Engeneering sodass man im Besten Fall nach der Matura nicht studieren muss.

23.07.2005 - 16:34 Uhr

"ungesichter Access Point" ?

Kleiner Wardriver 😁

Aber hört sicht schön an das Usertreffen -.-

20.07.2005 - 17:23 Uhr

Auch von mir gratz 👍 🙂

19.07.2005 - 11:41 Uhr

Wieso willst du DirectDraw verwenden?
Es ist obsolet und nicht sicher ob es in neueren DirectX Versionen überhaupt noch drin ist.

Sieh dir mal Microsoft.DirectX.Direct3D.Sprite an. Das ist eine einfache Klasse für Sprites und der Vorteil ist das du dadurch auf Direct3D selbst zugreifen kannst und somit deutlich flexibler und zukunftssicherer bist als mit Direct Draw.

09.07.2005 - 16:44 Uhr

Das Tutorial steht nun als pdf Version zum Download bereit.
Danke an CaptainCider für seine Hilfe beim erstellen des pdfs.

Die Sourcecodes der ganzen Projekte wurden nicht hinzugefügt, stehen aber als Downloads hier im Thread.

21.06.2005 - 17:55 Uhr

Also instance mit Exemplar übersetzen ist imho zwar nach dem Wörterbuch richtig, aber im Kontext zu C# eher falsch, da die C#-Programmiersprachenspezifikation auch immer von Instanzen spricht. Außerdem habe ich in noch keinem Fachbuch Exemplar für Instanz gesehen, deshalb werde ich zumindest weiterhin Instanz verwenden.

09.06.2005 - 19:54 Uhr

Passwörter, Keys usw. solltest du übrigens mit der Stored User Names And Passwords( Credentials Management ) Funktion von Windows verwalten.
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/creduipromptforcredentials.asp

07.06.2005 - 19:31 Uhr

Original von Noodles
Es ist so wahr wie lustig, garnicht. 😉

Ach, dann kann er meine Unterlagen aus dem Programmierunterricht bekommen. Bei uns heißen die Funktionen tatsächlich so ^^

28.05.2005 - 22:52 Uhr

** Alpha Blending **

Alphablending ist ein wichtiger Effekt den man für viele Dinge gebrauchen kann. So kann man damit Transparenz
effekte erzielen.
Sehen wir uns erstmal an was man mit Transparenz überhaupt meint. Jede Farbe besteht zunächst aus 3 Komponenten.
Rot - Grün - Blau. Nun führen wir einfach noch eine Komponente ein, den Alpha Wert. Dieser Wert beschreibt die
Opazität eines Objketes. Die Opazität beschreibt die Lichtundurchlässigkeit eines Objektes. Ein Alpha Wert
von 255 bedeutet das das die Farbe vollkommen opak ist dh man sie vollkommen sieht. Ein Alpha Wert von 0
bedeutet das die Farbe vollkommen durchsichtig ist.
Sobald wir Alphablending einschalten, berechnet Direct3D alle Farbwerte nach der Formel:

final color = source color * source blend factor + destination color * destination blend factor

source color ist jene Farbe, die in den Backbuffer geschrieben werden soll.
destination color ist jene Farbe, die derzeit im Backbuffer steht.
Die beiden blend factor können wir mit Direct3D selbst bestimmen und so verschiedenste Effekte erzielen.
final color ist dann die Farbe, die letztendlich in den Backbuffer geschrieben wird.

Wie aus der Formel ersichtlich, müssen Objekte mit Alphablending müssen immer nach vollkommen opaken Objekten
gerendert werden.
Für unsere Beispielanwendung benötigen wir zwei Texturen. Die erste Textur enthält eine Mauer und die zweite
ein Windowslogo. Der Code ist zunächst simpel. Wir erstellen erstmal ein Meshobjekt und setzen entsprechende
Vertices und Indices. Außerdem laden wir noch die Texturen:


		public void OnCreateDevice(object sender, EventArgs e)
		{
			Device dev = (Device)sender;

			CustomVertex.PositionTextured[] verts = { 			
														new CustomVertex.PositionTextured(-1f,  0f, -1f,0,1),
														new CustomVertex.PositionTextured( 1f,  0f, -1f,1,1),
														new CustomVertex.PositionTextured( 1f,  0f,  1f,1,0),
														new CustomVertex.PositionTextured(-1f,  0f,  1f,0,0),
			};

			short[] indices = { 0,1,2,
								  0,2,3 };

			Object = new Mesh(2,verts.Length,MeshFlags.WriteOnly,CustomVertex.PositionTextured.Format,
				device);

			Object.VertexBuffer.SetData(verts,0,LockFlags.None);
			Object.IndexBuffer.SetData(indices,0,LockFlags.None);

			baseTexture = TextureLoader.FromFile(dev,"base.jpg");
			alphaTexture = TextureLoader.FromFile(dev,"windowslogo.dds");

		}

Und schon können wir in die Renderfunktion springen. Dort rendern wir zuerst das Objekt mit der Mauertextur:


		private void Render()
		{
			if (device == null) 
				return;

			device.Clear(ClearFlags.Target , System.Drawing.Color.Blue, 1.0f, 0);
			
			device.BeginScene();

			device.Transform.View = Matrix.LookAtLH( new Vector3( 0.0f, 3.0f,-5.0f ), new Vector3( 0.0f, 0.0f, 0.0f ), new Vector3( 0.0f, 1.0f, 0.0f ) );
			device.Transform.Projection = Matrix.PerspectiveFovLH( (float)Math.PI / 4, 1.0f, 1.0f, 100.0f );

			Matrix world;
			world = Matrix.Scaling(2.0f,2.0f,2.0f);
			world *= Matrix.RotationX(-1.04f);
			world *= Matrix.Translation(0.0f,0.0f,0.0f);

			device.Transform.World = world;
			device.SetTexture(0,baseTexture);
			Object.DrawSubset(0);

Bis hierher gibt es nichts neues. Nun rendern wir das Objekt nochmal, jedoch mit dem Unterschied, das wir jetzt
mit eingeschaltetem Alphablending rendern.


			device.RenderState.AlphaBlendEnable = true;
			device.RenderState.SourceBlend = Blend.InvSourceAlpha;
			device.RenderState.DestinationBlend = Blend.DestinationAlpha;

			world = Matrix.Identity;
			world *= Matrix.Scaling(1.5f,1.5f,1.5f);
			world *= Matrix.RotationX(-1.04f);
			world *= Matrix.Translation(0.0f,0.0f,0.0f);
			device.Transform.World = world;

			device.SetTexture(0,alphaTexture);
			Object.DrawSubset(0);
			device.RenderState.AlphaBlendEnable = false;

Ganz am Anfang schalten wir Alpha Blending ein. Dadurch sagen wir Direct3D das es die oben genannte Formel
zum Herausfinden der final color verwenden soll.
Die nächsten zwei Zeilen definieren die blend factoren.
Als source blend setzen wir den Faktor ( 1 - As, 1 - As, 1 - As, 1 - As). As ist der Alpha Wert der source
Farbe.
Als destination blend setzen wir den Faktor (Ad, Ad, Ad, Ad). Ad ist hierbei der Alpha Wert der destination
Farbe.
Danach setzen wir noch unsere Textur, die überblendet werden soll und rendern das ganze. Zum Schluss schalten
wir Alpha Blending wieder aus, denn wir wollen ja nicht das die Mauer auch mit Alphablending gerendert werden
soll.

Natürlich gibt es noch mehr Blend Faktoren. Diese kann man unter
http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_m_dec_2004/directx/ref/ns/microsoft.directx.direct3d/e/blend/blend.asp
nachschlagen.

25.05.2005 - 15:15 Uhr

Original von herbivore
Hallo Tankian,

OT: Wo kaufst du deine PCs? Die Zeiten, in denen man eine Windows-Lizenz mitkaufen "musste", sind doch schon längst vorbei.

herbivore

Der Mainstream User wird sich seinen PC aber nicht selbst zusammenbauen sondern wird einfach in den Laden gehen und kauft sich ein Komplettsystem. In meiner Umgebung war das bisher so zu 90% der Falll 😉
Und bei den meisten Komplettsystemen ist Windows eben dabei. Und für solche Mainstream User ist es auch recht egal welches OS jetzt oben ist, denn die wollen ja meist nur spielen, surfen, chatten, Musik hören usw.

19.05.2005 - 21:29 Uhr

Original von Kronos

Original von Dark Matter
He Danke hat geklapt hab nur noch eine Frage was bedeutet Tv und Tu?

Tv und Tu legen fest, wie oft du eine Textur entlang der U (respektive der V) Richtung wiederholen willst ... wenn mich nicht um diese Uhrzeit allen guten Geister verlassen haben 😉

joa, Tu und Tv legen fest welchter Teilbereich der Textur auf den Vertex gemappt wird.

Ja, das hat mich auch gewundert. Hauptmanns Tutorials sind wirklich Klasse, aber das Texturtutorial macht irgendwie den Eindruck nicht ganz richtig zu funktionieren. Sollte es nicht eigentlich ein Würfel werden? Sollte nicht die Textur auf allen Seiten erscheinen? Bei Objekten die nur aus einem Subset bestehen kann man auf zwei Arten vorgehen: Entweder man setzt für alle Flächen die gleichen Texturkoordinaten, dann erscheint die gleiche Textur auf allen Seiten. Oder man biegt die Texturkoordinaten so hin, das eine einzige Textur sich praktisch wie Einwickelpapier um das Objekt legt. Auf diese Weise kann man mit nur einer einzigen Textur mehrere Seiten unterschiedlich texturieren. Allerdings wird die manuelle Berechnung der Texturkoordinaten natürlich mit steigender Vertexanzahl immer komplizierter, so dass man diese Arbeit besser mit einem externen Programm erledigen sollte. Ich habe mir mal erlaubt, Hoffmanns Tutorial etwas abzuändern (ich hoffe Hauptmann verzeiht mir das...), so das es jetzt ein Würfel wird der auf allen Seiten die gleiche Textur zeigt. Einfach nur die Form1.cs auswechseln.

joa, habe nciht dagegen. Ich hasse es nur selber Vertices und Texturdaten zu setzen. In der Theorie/meinen Zeichungen sieht alles perfekt aus, nur am PC nicht mehr -.-
(btw, ich heiße Hauptman, nciht Hoffmann ^^)

18.05.2005 - 15:20 Uhr

Crossposting.

13.05.2005 - 18:25 Uhr

stimmt. Aber man könnte es doch so machen das man zuerst eine DirectoryInfo des zu überwachenden Verzeichnisses holt und dann in der Changed überprüft ob der übergebene Pfad als Verzeichniss im DirectoryInfo Objekt (das sich ja nicht selbstständig ändert wie FileSystemWatcher)) ist und wenn er ist weiß man das ein Ordner war.
Am Ende der Changed noch ein DirectoryInfo Refresh machen.

13.05.2005 - 15:26 Uhr

Direkt im .net Framework fällt mir jetzt nichts ein, aber du kannst auf das Speech API (SAPI) zurückgreifen. Damit kann man mehr oder weniger einfach auf ein Mikrofon o.ä. zugreifen.
Du musst dir halt eine Wrapper Klasse für die ganzen COM Interfaces schreiben.

13.05.2005 - 15:17 Uhr

Du kannst doch noch das Changed Event abbonieren und alle Dateichanges aussortieren:


myWatcher.Changed += new FileSystemEventHandler(myWatcher_Changed);


protected void myWatcher_Changed(object sender, FileSystemEventArgs e)
{
      if(e.Type == WatcherChangeTypes.Created || e.Type == WatcherChangeTypes.Deleted) 
{
   if(Directory.Exists(e.FullPath))
   {
      // Es ist ein Verzeichnis. Verarbeite Daten
   }
   else
   {
      // Kein Verzeichnis
    }
}
}

10.05.2005 - 17:57 Uhr

hmm, ich selbst will eher keine Konsole haben, aber für anderen (= Leute denen der PC zu kompliziert ist und zu teuer ist) ist sowas sicher toll.

Nicht vergessen am Freitag: Microsoft stellt auf MTV die neue Xbox 360 vor.

10.05.2005 - 17:37 Uhr

Es heißt auch DataSet.Tables.


DataSet data = new DataSet();
data.ReadXml("a.xml");

foreach(DataTable myTable in data.Tables)
{
	foreach(DataRow myRow in myTable.Rows)
	{
		if(myTable.TableName == "home")
			Console.WriteLine(myRow["author"]);
	}
}

05.05.2005 - 22:10 Uhr

System.Xml.XmlTextReader

Reading XML Data with XmlTextReader

24.04.2005 - 11:13 Uhr

Lass dich vom Namen nicht täuschen:
http://www.microsoft.com/downloads/details.aspx?FamilyId=AFC15F29-D7C9-4CF7-A8D5-8AB81F14AE1B&displaylang=en

SDK heißt Software Development Kit. Und das DirectX 9 SDK bringt dir halt zunächst mal die DLLs damit du damit programmieren kannst, eine sehr große Dokumentation und viele Beispielprogramme. Die Runtime vom DirectX 9 kommt nur mit den DLLs damit du DirectX 9 Programme ausführen kannst.

Wenn du das alles installiert hast musst du aber auch auf die Microsoft.DirecX.dll und Microsoft.DirectX.Direct3D.dll referenzieren (csc /reference:Microsoft.DirectX.dll;Microsoft.DirectX.Direct3D.dll)

22.04.2005 - 14:45 Uhr

also ich programmiere seit ich 11 bin also rund 4/5 Jahre -.-
Quick Basic, C++, C# und Assembler

Aber dann bitte gleich zusammen mit "funzt" und "noob". Augenzwinkern

Das kann ich nur unterstützen 👍

22.04.2005 - 14:33 Uhr

Mit Clear gehts nicht, da musst du zwei Dreiecke nehmen und die texturieren(also die Dreiecke über den ganzen schirm gehen lassen und dann texturieren)

19.04.2005 - 17:33 Uhr

Manager.Adapters.Default.Information.Description

Gibt bei mir zB die Ausgabe: nVidia Geforce 2 GTS/Geforce 2 Pro (Omega 1.6693)

Diesen String kannst du dann zB mithilfe von Regex oder Substrings zerlegen. Für weitere Infos kannst du dir die AdapterDetails Klasse ansehen ...

18.04.2005 - 14:20 Uhr

Sind Freeshards nicht illegal?

18.04.2005 - 14:15 Uhr

Hi!

Über Manager.Adapters.Count bekomsmt du die Anzahl der installierten Adapter auf dem System. Mit Manager.Adapters.Current greifst du auf den derzeitigen Adapter zu und mit Manager.Adapters.MoveNext verschiebst du den Current Pointer auf den nächsten Adapter.
Alternativ kannst du auch in einer for Schleife über Manager.Adapters iterieren, da diese einen Indexer beinhalten( Manager.Adapters[0].CurrentDisplayMode.Height zB)
( Manager.Adapters.Default spricht den ersten Adapter(=0) an)

Und über die Check... Funktionen der Manager Klasse kannst du schauen ob bestimmte Formate und Device Types in Ordnung sind für einen Adapter.

17.04.2005 - 21:19 Uhr

** Vertikale Synchronisierung**

Wenn wir uns das letzte Beispiel nocheinmal ansehen, dann sehen wir das die Framerate bis zu einem bestimmten Punkt geht und nicht darüber (bei mir 85). Das Ganze hat den Grund das die Anwendung das wir Direct3D implizit sagen, das es für uns die Framerate mit der Bildwiederholfrequenz des Bildschirms synchronisieren soll. Das können wir abstellen indem wir unsere PresentParameters verändern.
Wir müssen den Wert PresentationInterval verändern


presentParams.PresentationInterval = PresentInterval.Immediate;

Das hebt diese Beschränkung auf und man erhält höhere Frameraten.

17.04.2005 - 17:56 Uhr

** Fonts und Textrendering **

So ziemlich jedes Spiel heutzutage hat auch eine Textausgabe. Direct3D bietet uns einen sehr komfortablen Weg
Text auszugeben. Dazu können wir die Klasse Font verwenden die uns die ganze Arbeit des Fontrendering abnimmt.
Zunächst brauchen wir eine Variable der Font Klasse:


Microsoft.DirectX.Direct3D.Font text;

Wir müssen hier den vollen Namespace angeben, da es sonst zur Kollision mit der Font Klasse von System.Windows.
Forms kommt. Nun erstellen wir eine Instanz davon:


text = new Microsoft.DirectX.Direct3D.Font(device,new System.Drawing.Font("Tahoma",12));

Wir verwenden den einfachsten Konstruktor, bei dem wir nur ein Device angeben müssen und den Fontnamen. Als
Fontnamen kann jeder Font im Verzeichnis C:\Windows\Fonts verwendet werden(<WINDOWSROOT>\Fonts). Wir verwenden
erstmal Tahoma. Als Schriftgröße wählen wir 12. Die Schriftgröße wird in Geviert angegeben. Nun springen schon
in die Render Funktion:


text.DrawText(null,"Hello World",100,100,Color.Red);

Der erste Parameter beschreibt ein Sprite Objekt. Es kann null sein wenn die Font Klasse ein eigenes verwenden soll
(wir werden später noch sehen wie wir das selbst angegeben). Wenn DrawText öfters aufgerufen wird, sollte ein
eigenes Spriteobjekt verwendet werden, um die Performance zu erhöhen(die Font Klasse muss ansonsten bei jedem
DrawText Aufruf ein neues Spriteobjekt erstellen). Der zweite Parameter gibt den auszugebenen Text an. Der
dritte und vierte Parameter die x- und y-Koordinaten des Texts. Der letzte Parameter schließlich die Farbe des
Texts. Zu beachten ist, dass die DrawText Funktion unabhängig von unseren Transformationen arbeitet und nur 2D
Text ausgeben kann. Wir sehen später noch eine Möglichkeit 3D Text auszugeben(und zwar über die Mesh Klasse).
Das wars auch schon zu der Font Klasse, wir sehen sie uns aber noch genauer an. Die Font Klasse bietet uns die
Möglichkeit Ressourcen in den Video RAM vorzuladen(Preloading). Dies können wir zB für Menütexte ausnutzen die
ja statisch sind. Das Preloading erhöht normalerweise die Performance des Rendering. Sehen wir uns nun einmal
PreloadText an, womit wir Text bereits vor dem Aufruf von DrawText laden können(was natürlich nur Sinn macht
bei großen Mengen an Text). Zunächst deklarieren wir eine string Variable:


string message = "Preloaded Text";

Nun laden wir diesen Text direkt nach dem Konstruktoraufruf vor:


text.PreloadText("message");

Den DrawText Aufruf müssen wir nur geringfügig umändern:


text.DrawText(null,message,100,100,Color.Red);

DrawText erkennt nun das die Variable message bereits im Video RAM geladen wurde und muss dadurch den string nicht
mehr bei jedem Frame zum Video RAM schicken sondern nimmt einfach die bereits im Video RAM vorhandende Variable.
Was wir uns noch ansehen können ist die Ausgabe der Framerate. Die Framerate ist das wichtigste Mittel zum messen
der Performance einer Anwendung und wird überlicherweise in frames per second(fps) angegeben. fps geben an, wie
viele Bilder pro Sekunde angezeigt werden. Ab rund 25 fps erkennt das menschliche Auge eine Szene als flüssig.
Wie können wir nun die Frames berechnen?
Zunächst brauchen wir einmal die Funktion QueryPerformanceCounter aus der WinAPI. Diese liefert uns den aktuellen Wert
des high-performance Counters.


[DllImport("user32.dll")]
private static extern bool QueryPerformanceCounter(ref System.Int64 counter);

Danach brauchen wir noch QueryPerformanceFrequency


	[DllImport("kernel32.dll")]
		private static extern bool QueryPerformanceFrequency(out System.Int64 freq);

Nun kommen wir zum Berechnen: Wir benötigen die Zeit die zwischen zwei Frames vergangen ist und müssen die Freuquenz des
high-performance Counters durch den Delta Wert der zwischen den zwei Frames vorherrscht dividieren.
Ganz am Anfang der Render Funktion holen wir uns die Frequenz des Counters und den aktuellen Wert:


System.Int64 LastFrame;
System.Int64 Freq;

QueryPerformanceFrequency(out Freq);
QueryPerformanceCounter(out LastFrame);

Nun rendern wir alles. Zum Schluss berechnen wir noch die Frames


System.Int64 CurrentFrame;
QueryPerformanceCounter(out CurrentFrame);

fps = Freq / (CurrentFrame - LastFrame);

Und nun können wir die Framerate ausgeben


text.DrawText(null,"Framerate: " + Convert.ToString(fps),150,150,Color.Red);

Wie immer gibt es den Source inkl. Visual Studio 2003 Projekt als Anhang.

15.04.2005 - 13:25 Uhr

Google mal ein wenig nach der DIN-EN-ISO 9241 Norm. In dieser Norm sind die Kriterien für das Design ergonomischer Benutzerschnittstellen geregelt.
Vielleicht hilft dir das weiter -.-

13.04.2005 - 22:04 Uhr

Original von LordK

Original von hauptmann
Ich hasse eigentlich jetzt schon das 2. und das 3. Jahr(COBOL ^^).

Och, ich kann dazu nur sagen, das COBOL auch sehr viel spaß machen kann 😉

Mach grad ne Ausbildung zum Fachinformatiker-Anwendungsentwicklung und durft fast das komplette erste Jahr mit COBOL arbeiten. Zum schluss hab ich schon angefangen "spiele" auf dem Großrechner mit COBOL zu programmieren 😉

das Problem ist nur das wir dann auch noch CICS(ein Transaktionsserver von IBM) verwenden. Grauenhaft -.-

13.04.2005 - 15:46 Uhr

Hi!

ich bekomme beim IIS 6 beim Aufruf einer ASP.net Site(bzw. wollte ich die Telligent Foren Systems ausprobieren -.-) folgenden Fehler:

Beim Ausführen der aktuellen Webanforderung wurde einen unbehandelte Ausnahme generiert. Informationen über den Ursprung und die Position der Ausnahme können mit der Ausnahmestapelüberwachung angezeigt werden.

Stapelüberwachung:

[HttpException (0x80070005): Der Zugriff auf 'D:\Inetpub\wwwdefault&#39; wurde verweigert. Das Überwachen der Dateiänderungen konnte nicht gestartet werden.]
System.Web.DirMonCompletion..ctor(DirectoryMonitor dirMon, String dir, Boolean watchSubtree, UInt32 notifyFilter) +139
System.Web.DirectoryMonitor.StartMonitoring() +42
System.Web.DirectoryMonitor.StartMonitoringFile(String file, FileChangeEventHandler callback, String alias) +154
System.Web.FileChangesMonitor.StartMonitoringDirectoryRenamesAndBinDirectory(String dir, FileChangeEventHandler callback) +278
System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +331

[HttpException (0x80004005): ASP.NET-Initialisierungsfehler]
System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +982

System.Web.HttpRuntime.ProcessRequestInternal(HttpWorkerRequest wr) +128

Ich habe bereits die ACLs für den ASP.net User und den Anonymen Internetuser habe ich bereits auf Vollzugriff gesetzt? Was muss ich noch tun, ich verzweifle schon leicht

12.04.2005 - 20:21 Uhr

Schüler der HTL Villach, Abteilung für EDV und Organisation - Software Engineering. Das Programmieren dort ist nicht wirklich anstrengend, im ersten Jahr(wo ich jetzt bin). C Grundlagen halt. Ich hasse eigentlich jetzt schon das 2. und das 3. Jahr(COBOL ^^).
Programmieren tue ich meist in meiner Freizeit sehr viel.

12.04.2005 - 17:52 Uhr

Links:

Kommunikation zwischen zwei Forms
MVC (Model-View-Controller Pattern mit ASP.NET)

Der Thread Wie von einer WIndow-Form auf die GUI-Elemente einer anderen Window-Form zugreifen? zeigt sehr schön, was man alles an Fehlern und schlechten Designansätzen vermeiden kann und sollte.

Und in Vorüberlegung zur Form2Form-Kommunikation: Programm-Design überdenken wird die Frage behandelt: "Brauche ich wirklich ein GUI mit mehreren unabhängigen Forms?".

12.04.2005 - 17:41 Uhr

Wenn man eine einfache ConsoleApplication schreibt und diese dann aufruft öffnet sich zwar ein Konsolenfenster, aber es schließt sich sofort wieder. Um das zu verhindern reicht ein einfaches Console.ReadLine(); am Ende des Programmes. Dadurch wartet das Programm auf einen Enterdruck.

Sofern man kein Console.ReadLine(); verwenden kann, will oder soll und das Visual Studio verwendet, ist es auch möglich seine Anwendungen mit der Tastenkombination Strg+F5 auszuführen. Damit adaptiert das Visual Studio das Standardverhalten von VC++ 6.0 und wartet nach der Ausführung der Anwendung auf einen Tastendruck. Der Nachteil dieser Methode ist aber, dass dieser Tastendruck nur beim Debuggen mit Visual Studio gefordert wird, nicht beim kompilierten Programm.

Wenn das Programm fertig ist, sollte man das ReadLine unbedingt wieder entfernen, da es stört und verwirrt, wenn die Anwendung (später) direkt aus der Eingabeaufforderung gestartet wird.

12.04.2005 - 17:38 Uhr

In I/O Ports Uncensored - 1 - Controlling LEDs (Light Emiting Diodes) with Parallel Port findet man einen Artikel über das Ansteuern der parallelen Schnittstelle.

12.04.2005 - 17:35 Uhr

Eine genaue Spezifikation der RS-232 Schnittstelle findet man in RS232 Specifications and standard.

Um auf diese Schnittstelle zugreifen zu können bietet sich die MSComm Komponente von Microsoft an. Eine genau Erklärung zu dieser Komponente findet sich in Serial COM Simply in C#. Einen Download findet man in MSCOMM32.OCX MSComm Control: Visual Basic Programmer's Guide to Serial Communications.

Wenn man .net 2.0 verwendet kann man die Klasse System.IO.Ports.SerialPort verwenden.

Siehe auch Template SerialPort.

11.04.2005 - 17:32 Uhr

Hi!

Mich würd mal interessieren wie lange du schon (C#) proggst und vor allem wie weit du das hier noch durchziehen möchtest. Ist doch ne ganze Menge Arbeit.

So ca. 2.5 Jahre und schreiben will ich das halt solange ich Zeit und Lust habe -.-

Das Tutorial ist jetzt unter Link verfügbar

11.04.2005 - 17:31 Uhr

** Der Indexbuffer - Indizieren von Vertices **

Diesmal sehen wir uns den sogenannten Indexbuffer an. Indexbuffer sind dazu da um Vertices zu reduzieren.
Bei einer Indizierung fallen nämlich doppelte Vertices weg und man kann so einen Vertexbuffer deutlich optimieren.
Nehmen wir an wir wollen zwei Dreiecke zeichnen. Grafisch würde das so aussehen:
1 2|4

0|3 5

Die Nummern geben dabei die einzelnen Vertices an. Für dieses Beispiel bräuchten wir also 6 Vertices in einem
Vertexbuffer. Jedoch fällt uns auf das zwei Vertices mit den selben Koordinaten zweimal im Vertexbuffer stehen.
Ist das nicht sinnloser Verbrauch von Speicherplatz? Deshalb erstellt man nun einen Indexbuffer in dem man
speichert, an welcher Position welcher Vertex ist. (sry, aber ihr solltet den Beitrag quoten oder die im zip beiliegende Tutorial.txt ansehen, dort wird die Tabelle richtig dargestellt -.- )
Index Wert Vertex
0 0 (x0,y0)
1 1 (x1,y1)
2 2 (x2,y2)
3 0 (x0,y0)
4 2 (x2,y2)
5 5 (x5,y5)

Wie wir in der Tabelle sehen müssen wir nur mehr 4 verschiedene Vertices speichern, nicht mehr 6 verschiedene.
In diesem Beispiel mag das nicht als viel erscheinen, aber wenn man große Objekte hat, kann man mit Indexbuffer
viel einsparen und sehr gut optimieren. Zu beachten ist aber, dass man jedoch 6 Indices braucht. In DirectX SDK
Hilfe wird übrigens empfohlen nur mit indizierten Vertices zu arbeiten. Und nun können wir bereits zu Praxis
kommen. Direct3D stellt uns die Klasse IndexBuffer zur Verfügung, um einen IndexBuffer zu erstellen. Wir werden
jedoch nicht direkt mit dem Indexbuffer arbeiten sondern verwenden ein Mesh Objekt. Ein Mesh Objekt ist einfach
eine Klasse die das Arbeiten mit Vertexdaten vereinfacht.


Mesh Object;

Sehen wir uns nun die OnCreateDevice Funktion an. Hier erstellen wir zuerst 4 Vertices, danach 6 Indices und
speichern diese im Mesh Objekt:


		public void OnCreateDevice(object sender, EventArgs e)
		{
			Device dev = (Device)sender;

			CustomVertex.PositionOnly[] verts = { 			
										new CustomVertex.PositionOnly(-1f,  0f, -1f),
										new CustomVertex.PositionOnly( 1f,  0f, -1f),
										new CustomVertex.PositionOnly( 1f,  0f,  1f),
										new CustomVertex.PositionOnly(-1f,  0f,  1f),
			};

			short[] indices = { 0,1,2,		// erstes Dreieck
								  0,2,3 };	// zweites Dreieck

Dieser Code bietet uns keine Probleme. Wir verwenden short Indices aus Speicherplatzgründen, da wird ja nur kleine
Werte brauchen(von 0 bis 3) und so müssen wir auf dem RAM der Grafikkarte pro Index nur 2 Bytes verwenden anstatt
4 Byte wie bei int. Die Zahlen im indices Array weisen auf die Vertices hin, die beim Zeichnen verwendet werden.
Für das erste Dreieck verwenden wir die Vertices 0, 1 und 2. Fürs zweite 0, 2 und 3.


Object = new Mesh(indices.Length / 3,verts.Length,MeshFlags.WriteOnly,CustomVertex.PositionOnly.Format,
							  device);

			Object.VertexBuffer.SetData(verts,0,LockFlags.None);
			Object.IndexBuffer.SetData(indices,0,LockFlags.None);

		}

Hier erstellen wir ein neues Mesh Objekt und setzen die VertexBuffer und IndexBuffer Werte dafür. Die Parameter
des Mesh Konstruktors sind einfach:
Der erste Parameter gibt die Anzahl der Flächen eines Objektes an. Wir brauchen logischerweise 2 Flächen. Einmal
fürs erste Dreieck und einmal fürs Zweite.
Der zweite Paramter gibt dann die Anzahl der Vertices an. Der Dritte gibt sogenannte MeshFlags an. Diese können
dazu verwendet werden der Klasse gewisse Informationen zu geben, wie sie die Daten behandeln soll. Wir wollen nur
Daten in das Mesh Objekt schreiben, jedoch keine herauslesen. Dazu geben wir MeshFalgs.WriteOnly an. Das nächste
ist das Format der Vertexdaten. Der letzte Parameter gibt dann schließlich das Device an.
Danach greifen wir direkt auf den VertexBuffer und den IndexBuffer zu und setzen die Daten mit SetData.
Nun können wir auch schon zur Renderfunktion kommen.
Diese ist diesmal auch ganz einfach:


			device.Clear(ClearFlags.Target , System.Drawing.Color.Blue, 1.0f, 0);
			
			device.BeginScene();

			device.Transform.View = Matrix.LookAtLH( new Vector3( 0.0f, 3.0f,-5.0f ), new Vector3( 0.0f, 0.0f, 0.0f ), new Vector3( 0.0f, 1.0f, 0.0f ) );
			device.Transform.Projection = Matrix.PerspectiveFovLH( (float)Math.PI / 4, 1.0f, 1.0f, 100.0f );

			device.Transform.World = Matrix.Identity;
			
			device.RenderState.CullMode = Cull.Clockwise;
			Object.DrawSubset(0);

			device.EndScene();
			device.Present();

Der Code bietet eigentlich nur vertraute Dinge -.-

Was ist jetzt jedoch wenn wir direkt aus einem VertexBuffer und einem IndexBuffer heraus rendern wollen? Dazu
können wir die Funktion DrawIndexedPrimitives verwenden (oder DrawIndexedUserPrimitives wenn wir ohne Vertexbuffer
arbeiten wollen). Doch um diese Funktion müssen wir uns erstmal nicht kümmern, da uns ja die Mesh Klasse die
meiste Arbeit abnimmt.

Wie immer gibt es das gesamte Visual Studio 2003 .net Projekt als zip zum Download.(achtung, ich verwende immer jeweils das neueste SDK dh für diese Version braucht ihr das April Update des SDKs, ich glaube aber das es mit älteren Ausgaben des SDK auch funktionieren sollte)

11.04.2005 - 17:30 Uhr

** Texture Mapping **

Alle unsere Objekte hatten bisher immer nur eine einzige Farbe und die Oberfläche der Objekte war glatt und durchgehend.
Jedoch hat eine solche Oberfläche so gut wie kein Objekt der realen Welt. Wenn man sich umschaut sind solche regelmäßigen
Anordnungen sehr selten und Oberflächen sind auch nicht sehr oft so glatt und "perfekt" wie wir sie bisher hatten.
Deshalb greifen wir auf das Texture Mapping zurück: Wir weisen unserem Objekt nicht einfach eine Farbe zu, sondern eine
Textur. Was ist jetzt eine Textur? Eine Textur ist ganze einfach ein Bild das wir über das Objekt legen. Dadurch können
wir jetzt zB einen Ziegelstein nicht einfach nur rot färben, sondern ihn auch ein realistisches Aussehen verpassen.
Sehen wir uns nun einmal an wie wir unsere Box texturieren können. Zunächst brauchen wir eine neue Variable vom Typ
Texture. In ihr speichern wir unsere Textur:


Texture textur;

Nun müssen wir unsere Textur laden. Das geht ziemlich einfach, da uns Direct3D die Klasse TextureLoader zur Verfügung
stellt, in der bereits vorgefertigte Funktionen zum Laden von Texturen enthalten sind:


public void OnCreateDevice(object sender, EventArgs e)
{
	Device dev = (Device)sender;

	textur = TextureLoader.FromFile(dev,"iron04.jpg");

Wir verwenden die Funktion FromFile, die als ersten Parameter das verwende Device verlangt und als zweiten das Bild,
das wir in der Textur speichern wollen. Die FromFile Funktion ist ein wahres Multitalent, denn laut SDK unterstützt
es die folgenden Bildformate: .bmp, .dds, .dib, .hdr, .jpg, .pfm, .png, .ppm, und .tga
In echten Spielen sollte man wenn möglich nicht jpg verwenden, so wie ich das hier tue, jedoch ist eine jpg schön klein
und als Beispiel sollte es reichen. Der Nachteil ist halt das ein jpg als Textur nicht wirklich toll aussieht.
Der Rückgabewert der Funktion ist ein Texture Objekt das die geladene Textur enthält.
Als nächstes müssen wir unsere Box anpassen. Bisher haben wir ja die Vertices der Box mit einer Farbkomponente erweitert,
jetzt müssen wir sie mit Texturkoordinaten erweitern.
Texturkoordinaten? Was sind Texturkoordinaten?
Wenn wir eine Textur auf ein Dreieck legen, woher soll Direct3D jetzt wissen wie es die Textur auf das Dreieck legen soll?
Deshalb gibt es die Texturkoordinaten, mit ihnen können wir bestimmen wie eine Textur auf ein Dreieck gelegt werden soll.
Als Anlehnung an Vertex werden diese Koordinaten auch Texel genannt. Ein Texel ist immer zwei dimensional(Bilder sind ja
auch nur zwei dimensional) und hat also 2 Achsen: Die u-Achse und die v-Achse. Die u-Achse ist eigentlich die x-Achse,
während die v-Achse die y-Achse ist. Der Koordinatenursprung einer Textur liegt jedoch in der linken oberen Ecke.
Eine weitere Besonderheit von Texel sind das sie in einem Intervall von [0,1] liegen. Dh das es bei allen Texturen nur
Texel zwischen 0 und 1 gibt unabhängig von der eigentlichen Größe der Textur. Der Texel 0,5 liegt daher immer in der Mitte
einer Achse.
Wir müssen nun also jedem Vertex einen Texel zuweisen, der anzeigt welcher Teil einer Textur auf diesen Vertex gelegt
werden soll.
Ein Beispiel: Nehmen wir an wir haben ein Rechteck mit 4 Vertices und eine Textur. Die Textur ist ebenfalls rechteckig
und wir wollen nun die Textur auf dieses Rechteck legen. Dazu müssen wir jetzt jedem Vertex des Rechteck einen Texel
der Textur zuweisen.
Sehen wir uns es jetzt den 1. Vertex an. Nehmen wir mal an das dieser in der linken oberen Ecke liegt. Wir können diesem
Vertex jetzt den Texel (0.0/0.0) zuweisen. Oder (1.0/1.0). Uns sind hierbei keine Grenzen gesetzt. Wir wollen aber die
Textur normal auf das Rechteck legen, also so wie ein Bildprogramm uns die Textur anzeigt, so wollen wir diese auf dem
Rechteck haben. Also weisem wir dem 1. Vertex den Texel (0/0) zu. Der 2. Vertex liegt nun in der rechten oberen Ecke.
Also weisen wir diesem Vertex den Texel (1/0) zu(wir müssen auf der u-Achse ganz nach rechts). Der 3. Vertex ist nun
die linke untere Ecke. Also brauchen wir hier den Texel (0/1). Und für den letzten Vertex brauchen wir schließlich den
Texel (1/1).
Ich hoffe mit diesem Beispiel versteht man die Texturkoordinaten recht gut. Am Besten ist es hier eine Zeichnung zu machen.
Kommen wir nun zum praktischen:


box = Mesh.Box(dev,2.0f,1.0f,2.0f);
			VertexFormats format = VertexFormats.PositionNormal | VertexFormats.Texture1;
			Mesh tempBox = box.Clone( box.Options.Value, format, dev );
			box.Dispose();
			box = tempBox;

			CustomVertex.PositionNormalTextured[] verts = (CustomVertex.PositionNormalTextured[])box.VertexBuffer.Lock( 0, 
				typeof( CustomVertex.PositionNormalTextured ), 
				LockFlags.None, 
				box.NumberVertices );

Wir wir sehen ist der Code nicht allzu schwer. Das VertexFormat der Box müssen wir auf PositionNormal und Texture1 setzen.
Texture1 beschreibt, das wir pro Vertex 1 Texel haben.
Danach müssen wir noch die neuen Vertices die wir schreiben wollen erstellen. Hierbei müssen wir PositionNormalTextured
verwenden, anstatt PositionNormalColored.
Nun kommen wir zum zuweisen der Texel, was nicht sonderlich schwer ist:


for(int i=0;i < verts.Length;++i)
	{
			verts[i].Tu = verts[i].X * 0.8f;
			verts[i].Tv = verts[i].Y * 0.8f;
	}
box.VertexBuffer.Unlock();
}

Sehen wir uns jetzt das Rendern an. Bevor unsere Box normal rendern können, müssen wir noch die Textur setzen. Dh wir
müssen Direct3D sagen das es jetzt für die nächsten Operationen wo es Texturen braucht die Textur nehmen soll, die wir
gesetz haben.


device.SetTexture(0,textur);

Was hat die 0 hier zu bedeuten? Wie wir später sehen werden gibt es verschiedene Texture Stages. Die untereste dieser
Schichten ist die Stufe 0. Mit diesen Texturstages werden wir später zwei oder mehr Texturen miteinander verbinden können
und auf unser Objekt legen. Dieses Verfahren nennt man dann Multi-Texturing.
Nun können wir unser Objekt mit der Textur rendern:


box.DrawSubset(0);

Und das wars schon. Mit Texturen kann man natürlich noch viel mehr anfangen, doch dazu mehr in späteren Teilen 😉
Anbei ist natürlich wieder das Visual Studio .net 2003 Projekt sowie die Datei iron04.jpg

11.04.2005 - 17:24 Uhr

** Input -> Output **

Nun, in unseren Anwendungen war jetzt zwar bereits etwas Bewegung. In Spielen kommt jedoch noch ein weiterer Faktor
hinzu: Der Userinput. Ohne diesen gäbe es eigentlich keine Spiele. Deshalb sehen wir uns einmal eine vereinfachte Form
an, die wir in unseren Anwendungen nutzen können. Unser Ziel ist einfach: Wir wollen anhand der Leertaste steuern
ob sich unser Objekt dreht oder nicht.
Dazu überschreiben wir einfach die Funktion ProcessCmdKeys der Basisklasse Form. Diese liefert uns im einem Parameter
die gerade gedrückte Taste.


protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
	return base.ProcessCmdKey( ref msg, keyData );
}

Nun können wir unseren bisherigen Eventhandler für Input löschen und fügen hier folgendes ein:


if(keyData == Keys.Escape)
		Application.Exit();

Nun kommen wir zum eigentlichen Teil: Zunächst reduzieren wir den Rendercode auf eine Box(und löschen nebenbei natürlich
überflüssigen Code für das zweite Objekt aus dem letzten Tutorial)


		private void Render()
		{
			if (device == null) 
				return;


			//Clear the backbuffer to a blue color 
			device.Clear(ClearFlags.Target, System.Drawing.Color.Blue, 1.0f, 0);
			//Begin the scene
			device.BeginScene();

			device.Transform.View = Matrix.LookAtLH( new Vector3( 0.0f, 3.0f,-5.0f ), new Vector3( 0.0f, 0.0f, 0.0f ), new Vector3( 0.0f, 1.0f, 0.0f ) );
			device.Transform.Projection = Matrix.PerspectiveFovLH( (float)Math.PI / 4, 1.0f, 1.0f, 100.0f );

			Matrix world;
			world = Matrix.Scaling(0.5f,0.5f,1.5f);
	
			device.Transform.World = world;

			box.DrawSubset(0);

			device.EndScene();
			device.Present();
		}

Nun müssen wir eine Variable vom Typ bool in unsere Klasse aufnehmen, die anzeigt ob das Objekt derzeit rotieren soll
oder nicht:


bool rotate = false;

Und schon kommen wir wieder zur ProcessCmdKey Funktion:


if(keyData == Keys.Space)
		rotate = !rotate;

Wir fragen ab ob die Leertaste(Space) gedrückt wurde, wenn ja dann wird roate invertiert(aus false wird true gemacht,
aus true false).

Nun müssen wir jedoch noch in die Render Funktion etwas ändern.
Vor dem device.Transform.World = world müssen wir noch abfragen ob rotate auf true ist, wenn ja dann müssen wir unser
Objekt drehen lassen.


	Matrix world;
	world = Matrix.Scaling(0.5f,0.5f,1.5f);

	if(rotate)
		world *= Matrix.RotationX(Environment.TickCount * (float)0.0025);
			
	device.Transform.World = world;

	box.DrawSubset(0);

Und damit ist es schon fertig. So einfach kann Benutzereingabe sein ^^
Wie immer ist das fertige Projekt im Visual Studio .net 2003 Format dabei. Außerdem ist in der Zip Datei das Tutorial als Txt enthalten ...

11.04.2005 - 14:26 Uhr

** Mehr als 2 Objekte **

Bisher hatten wir immer nur 1 Objekt auf unserem Bildschirm. Das wird sich nun ändern, da wir nun zwei Objekte rendern
werden. Wie immer baut dieses Tutorial auf den anderen auf, sowohl im Wissen, als auch im Code. Doch nun zum
vergnüglichen Teil ^^
Zuerst brauchen wir natürlich zwei Objekte, dazu nehmen wir einfach die rote Box aus dem vorgen Teil und eine Teekanne.
Lassen wir die Teekanne einfach mal grün sein.
Dazu machen wir eigentlich dasselbe, was wir mit der Box getan haben: Eine Variable vom Typ Mesh erzeugen, in ihr eine
Teekanne speichern und danach die Farbe verändern. Zuerst definieren wir die Membervariable sphere:


Mesh teapot;

Danach weisen lassen wir uns wieder durch eine statische Funktion eine Kugel vorberechnen und speichern diese in der
Variable teapot:


teapot = Mesh.Teapot(dev);

Die Mesh Klasse ist wirklich toll ^^
Nun machen wir dasselbe, was wir für die Box getan haben. Wir verändern die Farbe der Teekanne. Da wir den Code gleich
nach dem erstellen der Box einfügen, müssen wir nicht wieder extra Variablen einfügen und können so das Vertex Format von
oben verwenden:


tempBox = teapot.Clone( teapot.Options.Value, format, dev );
			teapot.Dispose();
			teapot = tempBox;

			verts = (CustomVertex.PositionNormalColored[])teapot.VertexBuffer.Lock( 0, 
				typeof( CustomVertex.PositionNormalColored ), 
				LockFlags.None, 
				teapot.NumberVertices );
		

			for(int i=0;i < verts.Length;++i)
			{
				verts[i].Color = Color.Green.ToArgb();
			}
			teapot.VertexBuffer.Unlock();

Jetzt kommen wir zum interessanten Teil. Und zwar zum Rendern. Zuerst begnügen wir uns mal, einfach beide Objekte zu
rendern, ohne sie zu rotieren o.ä.
Dazu müssen wir für jedes Objet zuerst eine World Matrix setzen und es dann damit zeichnen.
Anders ausgedrückt:
Begin Scene
Projection Matrix setzen
View Matrix setzen

World Matrix für Objekt 1 setzen
Objekt 1 rendern

World Matrix(und evtl. auch Projection und View Matrix) für Objekt 2 setzen
Objekt 2 rendern
World Matrix für Objekt 1 setzen
Objekt 1 rendern

World Matrix(und evtl. auch Projection und View Matrix) für Objekt n setzen
Objekt n rendern
End Scene

Das ist natürlich stark vereinfacht ausgedrückt, da in echten Spielen natürlich noch haufenweise andere Sachen dazukommen(
wie z.B. Kollisionsabfrage, Benutzereingabe etc.). Wir werden später noch sehen, wie man eine einfache Benutzereingabe
verwirklichen kann.
Sehen wir uns nun einmal an, wie wir das rendern verwirklichen. Zuerst setzen wir die Projection und View Matrix, für unser
Beispiel reicht eine Projection und eine View Matrix.


device.Transform.View = Matrix.LookAtLH( new Vector3( 0.0f, 3.0f,-5.0f ), new Vector3( 0.0f, 0.0f, 0.0f ), 
											new Vector3( 0.0f, 1.0f, 0.0f ) );
device.Transform.Projection = Matrix.PerspectiveFovLH( (float)Math.PI / 4, 1.0f, 1.0f, 100.0f );

Danach setzen wir die World Matrix für das erste Objekt und zeichnen es:


Matrix world;
world = Matrix.Scaling(0.5f,0.5f,1.5f);
			
world *= Matrix.Translation(-1.0f,0.0f,0.0f);
device.Transform.World = world;

box.DrawSubset(0);

Zuerst verkleinern wir das Objekt und strecken es auf der z Achse "nach hinten". Danach verschieben wir das Objekt um
eine Einheit nach links(auf der x Achse, negative Koordinaten sind hierbei nach links gerichtet). Schließlich rendern wir
es.
Nun zum zweiten Objekt. Wir setzen unsere Variable world zuerst auf die Identitätsmatrix:


world = Matrix.Identity;

Nun verkleinern wir das Objekt ein wenig und rotieren seitlich. Danach verschieben wir es um eine Einheit nach rechts.


world = Matrix.Scaling(0.5f,0.5f,1.0f);
world *= Matrix.Translation(1.0f,0.0f,0.0f);
world *= Matrix.RotationX(380);

Nun setzen wir die neue World Matrix und rendern die Teekanne:


device.Transform.World = world;
teapot.DrawSubset(0);

Und das wars schon. Was ganz wichtig ist, man darf vor dem rendern nicht vergessen, eine neue World Matrix zu setzen, denn
ansonsten wir weiterhin die Alte verwendet. Das könnt ihr ja ausprobieren. Kommentiert einfach mal den device.Transform.World =
world; vor dem teapit.DrawSubset(0); aus. Ihr werdet sehen, das die Teekanne dann auf der Box liegt. Kein schöner
Anblick(ich weis, die derzeitige Szene ist auch nicht gerade "schön", aber das muss man einfach können. Wenn wir dann
Spiele programmieren wollen, müssen wir uns auf diese Grundlagen zurückerinnern ^^)

Im nächsten Teil sehen wir uns dann eine einfache Benutzereingabe an. Wie immer gibt es den Source zum Download und seit
dem 5. Tutorial lege der zip Datei noch das Tutorial als txt File bei.

11.04.2005 - 14:25 Uhr

** Der Box eine Farbe zuweisen **

Nun, wir haben jetzt zwar eine schöne Box, jedoch ist sie noch komplett weiß. Das bringt uns natürlich nicht viel,
da wir ja mal aufregende Spiele schreiben wollen. Wie bringen wir jetzt jedoch Direct3D dazu, die Box mit einer
Farbe zu rendern? Das ist eigentlich ganz einfach: Wir verändern einfach die Vertices der Box direkt. Und dazu
kommen wir wieder auf unsere Vertex Buffer zu sprechen, aber wir brauchen diesmal keinen selber erstellen, sondern
wir holen uns die Vertex Daten einfach aus der Mesh Klasse(die diese ja speichert).
Und das ist mit ein paar Handgriffen gemacht. Zuerst müssen wir unseren Mesh "klonen" und bei diesem Vorgang das
Vertex Format ändern. Dies ändern wir so ab, dass wir die Box ganz normal transformieren können, jedoch jedem Vertex noch
eine Farbe zuweisen können. Nachdem das getan ist, holen wir uns die Vertex Daten des Mesh und schreiben einfach unsere
gewünschte Farbe hinein und geben die Vertex Daten der Mesh Klasse wieder bekannt. Und hierbei verändern wir nur die
Farbkomponente, nicht die Koordinaten der Vertices, da diese ja bereits vorberechnet wurden.
Sehen wir uns nuneinmal an, wie wir unseren Mesh klonen und ihm ein neues Vertex Format zuweisen:


VertexFormats format = VertexFormats.PositionNormal | VertexFormats.Diffuse;
Mesh tempBox = box.Clone( box.Options.Value, format, dev );
box.Dispose();
box = tempBox;

Zuerst definieren wir eine Variable vom Typ VertexFormat das unser VertexFormat enthält. Das VertexFormat für uns ist
PositionNormal(das standardmäßig verwendet wird) und dazu fügen wir Diffuse hinzu. Diffuse beschreibt die diffuse Farbe
eines Vertex. Die diffuse Farbe können wir uns derzeit einfach als jene Farbe merken, die man direkt sieht. Wir werden
später, wenn wir unsere Szene beleuchten sehen, dass es noch andere Arten von Farben gibt bzw. Lichtfarben.
Danach verwenden wir die Funktion Clone um einen vorläufigen Mesh zu erstellen. Danach löschen wir den eigentlichen Mesh
wieder und weisem unserem Mesh den Vorläufigen zu.
Nun müssen wir an die Vertex Daten kommen. Das geht ebenfalls äußerst einfach: Wie im Kapitel über Vertex Buffer locken
wir ihn einfach und kommen so an seine Daten(Lock sperrt die Daten eines Vertex Buffers für weitere Zugriffe und gibt
uns eine Referenz aus diese zurück. Die Änderungen werden übernommen und der Vertex Buffer entsperrt, wenn wir Unlock
aufrufen)
Gespeichert werden sie natürlich in einem Array vom Typ PositionNormalColored


CustomVertex.PositionNormalColored[] verts = (CustomVertex.PositionNormalColored[])box.VertexBuffer.Lock( 0, 
															typeof( CustomVertex.PositionNormalColored ), 
															LockFlags.None, 
															box.NumberVertices );

Da uns Lock nur ein System.Array zurückgibt, müssen wir dies erst explizit auf CustomVertex.PositionNormalColored casten.
Der Erste Parameter bestimmt die Menge an Daten, die wir sperren wollen. Wenn wir 0 angeben, wird der gesamte Vertex
Buffer gesperrt. Beim zweiten Parameter müssen wir bestimmen, welchen Typ die zurückgegebenen Daten haben sollen.
Der Dritte bestimmt die Lock Flags, die wir hier jedoch vernachlässigen können. Der letzte Parameter bestimmt, wie viele
Vertices zurückgegeben werden sollen. Da wir ja jeden Vertex des Mesh verändern wollen, müssen wir auch alle angeben.
Der nächste Schritt ist, die Vertex Daten zu verändern. Dazu gehen wir jeden Vertex durch und ändern seine Farbe:


for(int i=0;i < verts.Length;++i)
{
	verts[i].Color = Color.Red.ToArgb();
}

Die Variable Color ist uns bereits im Kapitel über Vertex Buffer begegnet, sie bestimmt welche Farbe der Vertex haben soll.
Als Letztes müssen wir den Vertex Buffer wieder entsperren, um die Änderungen zu übernehmen.


box.VertexBuffer.Unlock();

Nun rotieren wir unseren Mesh noch:


Matrix world;
world = Matrix.Scaling(1.0f,2.0f,1.0f);
			
int iTime = Environment.TickCount % 1000;
float fAngle = iTime * (2.0f * (float)Math.PI) / 1000.0f;
			
world *= Matrix.RotationY(fAngle);
world *= Matrix.Translation(0.0f,0.0f,0.0f);

device.Transform.World = world;
box.DrawSubset(0);

Und das wars schon. Direct3D ist einfach, wenn man weis wie's geht ^^

11.04.2005 - 14:24 Uhr

** 3D Objekte **

Bisher waren unsere Programme eher langweilig. Klar gibt es heute noch 2D Spiele und und 2D war mal das Maß aller Dinge, aber die richtige Action gibt's heute mit 3D. Und deshalb werden wir uns jetzt mal ansehen, wie man einfache 3D Objekte mit Direct3D erstellt und diese anzeigt.
Zunächst jedoch ein wenig Theorie, um überhaupt zu verstehen, wie 3D Objekte gespeichert werden: Wie unschwer zu erkennen ist der Monitor vor euch eine Fläche. Dh das er 2 Koordinaten hat. Die x und die y Koordinate(wie man es beim karthesischen Koordinatensystem in der Schule lernt ^^). Durch ein Koordinatenpaar x|y können wir nun jeden beliebigen Punkt auf diesem Monitor angeben. Aber wie kann man dann 3D Objekte, wie man sie in jedem Spiel hat, abbilden? Wie ist es möglich, dass man eine 3D Szene auf ein 2D Koordinatensystem abbildet? Die Antwort ist simpel: Man führt einfach noch eine Koordinate ein(die z Koordinate), die einfach mit den beiden vorhanden Koordinaten verknüpft wird. Nun, das klingt ein wenig schwer verständlich, jedoch ist es im Endeffekt für uns Programmierer sehr einfach: Wir geben einfach bei Koordinaten eine z Koordinate zusätzlich an, die Tiefeninformationen speichert. Eine positive z Koordinate zeigt dabei an, dass wir "in den Bildschirm hinein" wollen, eine negative "aus dem Bildschirm heraus". Mathematisch ausgedrückt müssen wir nun also eine Formel finden, die uns nun aus einem Koordinaten Tripel x|y|z ein Koordinatenpaar x|y macht. Dazu können wir nun auf etwas, das wir jeden Tag erleben: Wenn wir uns von einem Haus entfernen, dann erscheint es mit zunehmender Entfernung kleiner. Nähern wir uns dem Haus, wird es größer. Dazu dividieren wir x und y einfach durch z. Die Koordinaten, die wir dadurch erhalten nennt man projezierte Koordinaten. Die Formel lautet nun:
x' (x projeziert) = x / z
y' (y projeziert) = y / z

Wir müssen uns diese Formel nicht merken und uns um diese Umrechnung auch keine Sorgen machen: Direct3D bzw. die Grafikhardware macht dies automatisch.(dazu stecken wir ja mehrere hundert € in Grafikkarten(zumindest ich ^^))

Jetzt können wir uns wieder Direct3D zuwenden. Normalerweise würde hier nun ein Beispiel kommen, in dem man einen Vertex Buffer und einen Würfel oder eine Pyramide erzeugt, jedoch will ich jetzt mal nicht diesen Weg gehen. Und zwar nutzen wir einfach eine vorgefertige Funktion, die uns bereits eine "Box" aus 3 Parametern berechnet und die wir dann leicht anzeigen können.
Zunächst deklarieren wir eine neue Variable box vom Typ Mesh. Mesh ist eine Klasse, in der man genau solche Vertex Informationen leicht speichern und anzeigen kann(bisher haben wir uns ja um die Erstellung eines Vertex Buffers gekümmert und um dessen anzeigen ...).


Mesh box;

Nun verwenden wir die statische Funktion Box der Klasse Mesh, die uns aus 3 Parametern eine Box berechnet und uns eine Variable vom Typ Mesh zurückgibt.


public void OnCreateDevice(object sender, EventArgs e)
{
			Device dev = (Device)sender;

			box = Mesh.Box(dev,2.0f,1.0f,2.0f);
}

Die Parameter sind simpel: Der Erste ist einfach das device. Der zweite beschreibt die Ausdehnung auf der x Achse. Der dritte auf der y Achse und letzte schließlich, trommelwirbel, auf der z Achse.
Diese Funktion berechnet uns nun aus diesen Parametern eine Box die wir in der der Variable box speichern. Einfach, nicht wahr?

Nun kommen wir zum anzeigen. Das geht ebenfalls entsprechend einfach. Wir springen in unsere Render Funktion und rufen die Funktion DrawSubset auf. Fertig.


box.DrawSubset(0);

Um den Parameter brauchen wir uns erstmal nicht kümmern.(erst später, wenn wir dann zB Vertex Daten von Dateien laden o.ä.)

Die Mesh Klasse bietet uns jedoch nicht nur eine Funktion, für die Erstellung einer Box, sondern wir können mir ihr auch noch Teekannen, Zylinder, Kugeln und Ringe erzeugen. Und natürlich auch beliebige 2D Polygone(ein Polygon ist einfach der Sammelbegriff für Dreiecke, Vierecke, Sechsecke usw.). Einfach mal ein wenig in der Klasse rumstöbern ^^

Einen Schönheitsfehler hat das ganze jedoch. Die Box erscheint weiß. Logisch, wir haben ja auch keine Farbe angegeben. Wie können wir der Box jetzt jedoch eine Farbe zuweisen? Nun, dazu müssen wir einfach die Vertex Daten ändern, doch dazu im nächsten Kapitel mehr.

Wie immer gibt es nun auch den Source als VS.net 2003 Projektfiles als Zip File.

11.04.2005 - 14:22 Uhr

** Die World Matrix **

Sehen wir uns nun die World Matrix einmal genauer an. Im letzten Beispiel haben wir ja das Dreieck rotiert, was ist aber, wenn wir es still stehen lassen wollen. Dann müssen wir die World Matrix auf die Identitätmatrix setzen(was mit 1 oder 0 verglichen wird. Also eine Matrix + der Identitätsmatrix ergibt wieder die Matrix selbst). Das machen wir so:


device.Transform.World = Matrix.Identity;

Damit steht unser Dreieck jetzt still. Kommen wir jetzt zum Rotieren. Wir können das Objekt um die x Achse, die y Achse und um die z Achse rotieren. Die Parameter beschreiben immer den Winkel, um den rotiert werden soll:


int  iTime  = Environment.TickCount % 1000;
float fAngle = iTime * (2.0f * (float)Math.PI) / 1000.0f;
device.Transform.World = Matrix.RotationX(fAngle);

Hier wird unser Objekt jetzt eine Rotation um die X Achse vollziehen. Es gibt noch die Funktionen Matrix.RotationY(für die Y Achse) und Matrix.RotationZ(für die Z Achse)

Nun zum Translieren oder verschieben. Erstmal ein Beisiel:


device.Transform.World = Matrix.Translation(1.0f,0.0f,0.0f);

Wie unschwer zu erkennen, haben wir hier unser Dreieck auf der x-Achse verschoben(Vektorangaben sind immer x,y,z).
Und zum Schluss das skalieren. Das skalieren bedeutet ja vergrößern oder verkleiner.


device.Transform.World = Matrix.Scaling(1.5f,1.0f,1.0f);

Auch hier können wir auf den verschiedenen Achsen skalieren. Wichtig zu wissen ist, dass der Wert 1 bedeutet, das das Objekt seine ursprüngliche Größe behalten soll. Jeder Wert kleiner 1 bedeutet eine Verkleinerung, jeder Wert größer 1(so wie hier), bedeutet eine Vergrößerung.

In einer echten Anwendung wollen wir jedoch nicht einfach nur mal rotieren und mal translieren, sondern diese Operationen auch kombinieren. Das ist eigentlich ganz einfach: Wir müssen einfach eine Matrix bilden und diese dann als World Matrix setzen. Wie wir bereits wissen, müssen wir zuerst skalieren, dann rotieren und dann translieren. Wichtig ist, das man die einzelnen Ergebnisse(Skalieren,rotieren,translieren) miteinander mulitplizieren muss.(Eine Matrix Multiplikation ist jedoch nicht assoziativ d.h. Marix M * Matrix B ist ungleich Matrix B * Matrix M)


Matrix world;
			
// skalieren
world = Matrix.Scaling(1.0f,1.0f,1.0f);
// rotieren
world *= Matrix.RotationY(1.0f);
// translieren
world *= Matrix.Translation(1.0f,0.0f,0.0f);
			
device.Transform.World = world;

11.04.2005 - 14:22 Uhr

**Transformationen **

Bisher waren unsere Programme eher langweilig. Jetzt wird sich das aber ändern, da wir jetzt Bewegung in die ganze Sache bringen. Und dazu brauchen wir jedoch ein wenig Mathematik. Wie wir bereits gesehen haben haben wir 3 Werte um einen Punkt in unserer Szene zu beschreiben. Diese sind die x Koordinate, die y Koordinate und die z Koordinate. Währe es nun nicht gut, wenn wir diese Werte zusammenfassen könnten und mit ihnen rechnen? Nun, das geht natürlich und das ganze nennt sich einen _ Vektor_. Wir verwenden einen 3D Vektor, da wir ja 3 Werte verwenden. Wie man damit rechnet will ich hier mal ausklammern, da es bereits auf Wikipedia einen ausgezeichneten Artikel zu diesem Thema gibt.
Das nächste, was wir wissen müssen, ist der Begriff der Matrix. Eine Matrix ist eine rechteckige Anordnung von Zahlen. Wir werden gleich sehen, wozu wir eine Matrix brauchen.(Für die Rechenregeln verweise ich wieder auf Wikipedia).
Nun kommen wir erstmal zu einer wichtigen Frage: Wie können wir usnere Szene nun animieren bzw. in Bewegung versetzen.
Um das zu programmieren, müssen wir erstmal verstehen, wie unsere Vertices aus unseren VertexBuffern auf der Grafikkarte behandelt werden. Und zwar wird jeder Vertex duch eine sogenannte Fixed Function Geometry Pipeline. Dort werden die Vertices(welche als Vektoren vorliegen) mit 3 verschiedenen Matrizen transformiert. Anschließend werden die Vertices geclippt(= Culling) und ihre Koordinaten werden auf den Monitor skaliert d.h. die Koordinaten werden so angepasst, das sie auf bzw. für den Monitor passen.
Für uns sind nun die ersten 3 Schritte dieser Pipeline wichtig. Diese 3 Transformationen sind: World Transformation, View Transformation und Projection Transformation.
Sehen wir uns diese 3 einmal genauer an:
World Transformation:
Normalerweise sind alle Vertices relativ zum Ursprung des Objekts(also in einem lokalen Koordinatensystem). Jedoch müssen wir, um das Objekt auch anzeigen zu können, dieses lokale Koordinatensystem zu einem sogenannten world space transformieren. Der world space ist nichts anderes, als ein Koordinatensystem, in dem alle Koordinaten relativ zu einem gemeinsamen Ursprung sind. Welche genauen mathematischen Vorgänge das nun sind, dies ist für uns eher uninteressant. Für uns ist es jetzt wichtig zu wissen, das wir mit dieser World Transformation unser Dreieck aus dem vorigen Beispiel bewegen können. Und diese World Transformation wird durch eine Matrix dargestellt, die World Matrix. Wir können nämlich auf die World Matrix 3 verschiedenen Operationen anwenden: Translieren, Rotieren und Skalieren.
Translieren bedeutet einfach ein Objekt zu verschieben. Man kann es nach oben,unten und links,rechts verschieben. Rotieren ist wohl klar. Man rotiert das Objekt entweder um die x-Achse oder um die y-Achse. Und beim Skalieren kann man ein Objekt um einen bestimmten Faktor vergrößern oder verkleinern.
Ich habe jetzt von Objekt gesprochen, nun warum rede ich hier von Objekt und nicht von Matrix? Nun, alle diese Operationen werden auf die World Matrix angewendet und diese World Matrix wird ja wiederrum auf alle Vertices eines Objektes angewendet(wir können die World Matrix während der Laufzeit jederzeit verändern). DirectX bietet uns natürlich mehrere Funktionen, damit wir uns nicht mit den genauen Rechnungen herumschlagen müssen. Wichtig ist außerdem, dass wir skalieren, translieren und rotieren nicht einfach in beliebiger Reiehenfolge durchführen dürfen(bzw, können schon, jedoch kommen dann unlogische Verschiebungen zum Vorschein...) Die richtige Reihenfolge lautet: Skalieren und danach zuerst rotieren und zum Schluss translieren. Man kann auch zuerst translieren und dann rotieren(skalieren kommt immer als erstes.). Der unterschied ist einfach der, das ja um den Koordinatenursprung rotiert wird. Wenn man nun zuerst transliert und dann rotiert, dann wird das Objekt zuerst irgendwohin in der Szene gesetzt und dann nochmal um die Rotierung verschoben. Um diesen Effekt zu vermeiden, rotieren wir zuerst und translieren erst später.
_View Transformation _
Mit der View Transformation können wir eine Kamera oder auch Viewport erzeugen. Damit ist einfach gemeint, wohin der Betrachter einer Szene schaut. Eine View Matrix wird aus 3 Vektoren gebildet. Diese sind der eye point Vektor, der look-at Vektor und der world's up Vektor. Der eye point Vektor beschreibt, wo der Augenpunkt(Also der Ausgangspunkt) des Betrachters ist. Der look-at Vektor gibt an, wohin(also zu welchem Punkt) der Betrachter schaut. Und der world's up Vektor beschreibt die Ausrichtig auf der y Achse.(Dieser Vektor ist normalerweise [ 0,1,0] außer man will die Kamera um ihre eigene z Achse drehen).
Um eine View Matrix zu bilden, bietet uns DirectX wie immer die Möglichkeit dies mit mehreren vorgefertigten Funktionen zu tun.
_Projection Transformation _
Die Projection Transformation(beschrieben durch die Projection Matrix) ist dafür verantwortlich, das wir auf dem 2D Monitor eine 3D Szene darstellen und auch sehen können. Und zwar sorgt sie dafür, dass Objekte, die weiter weg sind, kleiner sind und Objekte, die näher sind, größer sind(also wie, als wenn man aus dem Fenster schaut.)

Nun, jetzt wissen wir die Theorie, jedoch noch nicht, wie man das in der Praxis anwendet. In der Praxis ist das aber äußerst einfach: Man berechnet einfach die jeweiligen Matrizen und gibt sie dann dem Device bekannt. Alle Objekte, die wir danach rendern, werden mit den derzeit gesetzten Matrizen transformiert. Jedes Objekt wird immer nur einmal durch die Pipeline geschleust, außer man render es nochmal im selben Frame.

Aber wir müssen noch etwas bedenken. Unsere Initalisierung wird von nun an länger sein. Deshalb werden wir ab jetzt auch eine neue Struktur des Quellcodes haben(der dem aus dem Tutorial sehr ähnlich ist ^^).

Kommen wir nun endgültig zur Praxis. Jedoch vorher müssen wir noch den Typ unserer Vertices ändern. Bisher hatten wir sie vom Typ TransformedColored, dies müssen wir jetzt ändern. Das Transformed bedeutet nämlich, das wir uns nicht um die einzelnen Transformationen kümmern wollen und diese DirectX überlassen wollen(was bei statischen Objekten ja auch durchaus sinnvoll sein kann). Wir benötigen jetzt jedoch Vertices vom Typ PositionColored.

Die einzelnen Matrizen setzen wir über uns device. D.h.
device.Transform.View = View Matrix
device.Transform.World = World Matrix
device.Transform.Projection = Projection Matrix

Jetzt wird es aber ernst: Die Render Funktion. Hier setzen wir zuerst die Matrizen und rendern dann aus unseren Vertex Buffer ein Dreieck. Die Besonderheit: Es dreht sich um die y Achse. Sehen wir uns zuerst die World Matrix an:


int iTime = Environment.TickCount % 1000;
float fAngle = iTime * (2.0f * (float)Math.PI) / 1000.0f;
device.Transform.World = Matrix.RotationY(fAngle);

Zunächst berechnen wir den Winkel, in dem sich das Dreieck rotieren soll. Danach setzen wir die World Matrix einfach über die Funktion Matrix.RotationY, welche uns eine Roation um die Y Achse mit dem angegbenen Winkel(fAngle) berechnet.

Nun die View Matrix:


device.Transform.View = Matrix.LookAtLH( new Vector3( 0.0f, 3.0f,-5.0f ), new Vector3( 0.0f, 0.0f, 0.0f ), new Vector3( 0.0f, 1.0f, 0.0f ) );

Die View Matrix ist nichts besonderes. Zuerst wird der eye Point definiert, dann der look-at Vektor und dann noch world's up.


device.Transform.Projection = Matrix.PerspectiveFovLH( (float)Math.PI / 4, 1.0f, 1.0f, 100.0f );

Hier die die Projection berechnet. Ebenfalls nichts besonderes.

In den nächsten Programmen werden wie die Projections Matrix und die World Matrix eher in den Schatten stellen und uns dafür ausgiebig mit der World Matrix beschäftigen.

Etwas, das wir noch klären müssen, ist warum wir das Culling ausschalten. Wir rotieren ja das Dreieck. Und beim rotieren sind die Vertices ja einmal gegen den Uhrzeigersinn angeordnet und einmal im Uhrzeigersinn. Wenn wir jetzt das Culling einschalten würden, würden wir je nach gesetzen Wert eine Seite des Dreiecks nicht sehen(einfach mal ausprobieren).
Ansonsten gibt es nicht mehr viel zu sagen. Der Code ist halt vom Microsoft DirectX Tutorial übernommen, aber ich hoffe mal, das das kein Problem darstellt.
Der gesamte Sourcecode ist wie immer als Visual Studio .net 2003 Projekt(gepackt als zip) angehängt.

11.04.2005 - 14:20 Uhr

** Renderstates **

Was ist ein Renderstate? Nun, ein Renderstate beschreibt, wie Direct3D unsere Dreiecke zeichnen soll. Es gibt sehr viele verschiedene Renderstates, daher kann ich jetzt nicht auf alle eingehen(abgesehen davon, könnten wir viele Renderstates mit unserem bisherigen Wissen ohnehin nicht verstehen). Daher will ich mal das grundlegende Konzept der RSs zeigen, damit wir später keine Probleme haben.
Also zuerst mal, beeinflusst ein Renderstate wie ein Dreieck gezeichnet wird. Ein einfacher Renderstate wäre z.B. der FillMode. Der sagt Direct3D, ob unser Dreieck, entweder solid(was der Standard Wert ist und mit dem haben wir auch in den bisherigen Anwendungen gearbeitet), ob nur die Eckpunkte oder nur die Linien gezeichnet werden sollen. Wir können disen RS vor dem Rendern unseres Dreiecks einsetzen:


....
m_Device.RenderState.FillMode = FillMode.WireFrame;

			m_Device.SetStreamSource(0,m_VertexBuffer,0);
			m_Device.DrawPrimitives(PrimitiveType.TriangleStrip,0,1);
....

Mit dem WireFrame Modus werden von unserem Dreieck nur die Linien gezeichnet. Daneben gibt es noch FillMode.Solid, der der Standardwert ist. Und dann gibts noch FillMode.Point, der nur die Eckpunkte zeichnet.(Bemerkung: Wenn wir den DrawPrimitive Aufruf auf m_Device.DrawPrimitives(PrimitiveType.PointList,0,3); abändern, hätte wir den selben Effekt wir mit dem Renderstate FillMode auf Point. )

Natürlich gibt es noch viel mehr RSs als nur FillMode, daher will ich hier noch ein paar vorstellen.

Der nächste ist DitherEnable. Beim Dithering wird da, wo mehrere verschiedene Farben aufeinandertreffen, ein Mittelwert aus diesen berechnet und dieser dann eingesetzt. Auf heutigen Grafikkarten macht es fast keinen Unterschied, ob Dithering eingeschalter ist oder nicht, daher schalten wir es ein:


m_Device.RenderState.DitherEnable = true;

Und noch einer ist das Culling. Das Culling entscheided, welche Objekte sichtbar sind und deshalb gezeichnet werden und welche nicht sichtbar sind und nicht gezeichnet werden.
Direct3D unterstüztt bereits von Haus aus 2 einfache Arten von Culling, die in unseren Beispielanwendungen vollkommen ausreichen werden. Bei Spielen/Grafikdemos würde man natürlich zum Standard Culling noch sein eigenes implementieren.
Direct3D bietet uns das clockwise(CW) culling und das counterclockwise(CCW) culling. Wenn wir unsere Vertices im Uhrzeigersinn anordnen, brauchen wir CCW culling, damit die "unsichtbaren" Vertices wegfallen, wenn unsere Vertices gegen der Uhrzeigersinn angeordnet sind, müssen CW culling verwenden.
Der Standard Wert dieses RS ist CCW. Natürlich können wir das Culling auch komplett abschalten:


m_Device.RenderState.CullMode = Cull.CounterClockwise; // Standard. Unser Dreieck aus dem vorigen Beispiel würde gezeichnet werden
m_Device.RenderState.CullMode = Cull.Clockwise; // CW Culling. Um unser Dreieck jetzt zu zeichnen müssten wir die Elemente 0 und 2 austauschen
m_Device.RenderState.CullMode = Cull.None; // Alles wird gezeichnet. Es ist egal wie wir unsere Vertices anordnen

Gut, das waren mal 3 Renderstate, wir werden jedoch im Laufe des Tutorials noch viele mehr kennen lernen...

11.04.2005 - 14:16 Uhr

** Rendern von Dreiecken **

Gut, weiter gehts. Dieses Kapitel wird viele grundlegende Dinge beinhalten, also gut aufpassen. Zunächst wollen wir einmal sehen, wie ein Dreieck aufgebaut ist: Ein Dreieck besteht aus 3 Eckpunkten,die miteinander verbunden werden. Und für jedes Dreieck, dass wir zeichnen wollen, müssen wir diese 3 Eckpunkte definieren. Aber wie müssen wir diese 3 Eckpunkte definieren, damit DirectX sie zeichnen kann? Nun, dass ist relativ einfach: Für Direct3D ist der Bildschirm nichts weiter als ein Koordinatensystem. Wir müssen nur die Koordinaten für die einzelnen Eckpunkte an Direct3D schicken und Direct3D zeichnet uns dann ein Dreieck. Wie ist dieses Koordinatensystem nun aufgebaut? Ich denke mal, dass jeder weis, was ein (karthesisches)Koordinatensystem ist. Für Direct3D exestieren 3 Achsen. Die x Achse, die y Achse und die z Achse. Die x Achse verläuft auf der horizontalen, die y auf der vertikalen. Die z Achse geht in den Bildschirm hinein. Da wir ja jetzt erstmal nur 2D Objekte rendern wollen, brauchen wir uns darum nicht zu kümmern. Zu sagen bleibt hierzu eigentlich nur noch, dass die englische Bezeichnung für Eckpunkt Vertex lautet, im Plural Vertices.
Um solche Vertices zu speichern, bietet uns Direct3D eine ganze Reihe von Strukturen. Diese sind alle im Namespace Microsoft.DirectX.Direct3D.CustomVertex zusammengefasst. In diesem Namespace sind ziemlich viele unterschiedliche Strukturen zum Speichern von Vertices, für uns ist jedoch nur die Struktur CustomVertex.TransformedColored (erstmal) interessant. Das Transformed bedeutet das wir 2D Koordinaten angeben wollen und das Color das wir jeden Eckpunkt eine Farbe zuweisen wollen. Wenn wir jetzt jeden Eckpunkt eine eigene Farbe zuweisen, wird der unmittelbare Bereich zu diesem Eckpunkt mit der Farbe gefärbt. An den Übergängen zwischen den einzelnen Farben, werden die Farben interpoliert. Beim interpolieren wird einfach ein Mittelwert zwischen den einzelnen Farben berechnet und dadurch kommt ein Farbverlauf zwischen den Farben zustande.
Jetzt müssen wir noch wissen, wie wir die Vertices speichern. Dazu müssen wir zunächst mal ein Array von CustomVertex.TransformedColored erstellen und mit unseren Daten befüllen. Dazu fügen wir unserer Klasse aus dem 1. Kapitel eine weitere private Variable verts hinzu:


private CustomVertex.TransformedColored[] verts;

Nun, diese Vertices liegen jetzt aber im Hauptspeicher(dem RAM). Wenn wir nun unser Dreieck rendern wollen, muss Direct3D diese erst über den AG Port(bzw. PCI Port) zur Grafikkarte transportieren. Bei ein paar Vertices, wird sich das sicher nicht bemerkbar machen, aber wenn wir jetzt mehrere tausend Vertices haben? Deshalb wäre es ja von Vorteil, wenn die Vertices direkt auf dem Speicher der Grafikkarte liegen würden.(Video RAM) Der Vorteil des Video RAM ist, dass er eine extrem kurze Zugriffszeit hat und das wir nicht bei jedem Rendervorgang, die Daten über den AG Port transportieren müssen. Ein Nachteil ist jedoch, dass der Video RAM von der Größe her relativ begrenzt ist und teuer ist. Aber darum müssen wir uns nicht kümmern. Also wir speichern wir jetzt unsere Vertices im Video RAM? Ganz einfach: Mit einem VertexBuffer. Ein VertexBuffer ist nichts anderes als ein Speicherbereich im Video RAM, der unsere Vertices hält. Ein VertexBuffer wird durch die Klasse Direct3D.VertexBuffer repräsentiert.


private VertexBuffer m_VertexBuffer;

Nun müssen wir unserer Vertices noch definieren und nebenbei den VertexBuffer mit diesen Daten befüllen. Zuerst definieren wir die Vertices:


verts = new CustomVertex.TransformedColored[3];

			verts[0].X=150;verts[0].Y=50;verts[0].Z=0.5f; verts[0].Rhw=1;verts[0].Color = Color.Yellow.ToArgb();
			verts[1].X=250;verts[1].Y=250;verts[1].Z=0.5f; verts[1].Rhw=1;verts[1].Color = Color.Bisque.ToArgb();
			verts[2].X=50;verts[2].Y=250;verts[2].Z=0.5f; verts[2].Rhw=1; ;verts[2].Color = Color.White.ToArgb();

Wir sehen also, dass wir jedem Vertex eine X Koordinate, eine Y Koordinate, eine Z Koordinate und eine Farbe zuweisen. Wie gesagt, müssen wir uns eigentlich nicht um die Z Koordinate kümmern, jedoch habe ich sie auf 0.5 gesetzt, da ein 0.0 bei manchen(älteren) Grafikkarten zu Problemen führen könnte.
Nun erstellen wir den VertexBuffer und zwar mit den folgenden Konstruktor:

public VertexBuffer(
Type typeVertexType,
int numVerts,
Device device,
Usage usage,
VertexFormats vertexFormat,
Pool pool
);

Der erste Parameter bestimmt, welchen VertexType wir für unseren VertexBuffer verwenden wollen. Hier setzen wir einfach typeof(CustomVertex.TransformedColored) ein.
Der zweite Parameter bestimmt die Anzahl der Vertices, die wir in unserem VertexBuffer speichern wollen. Da wir nur ein Dreieck speichern wollen, setzen wir hier eine 3 ein.
Der vierte Parameter bestimmt das Device, in welchem Kontext der VertexBuffer erstellt werden soll: m_Device.
Der fünfte Parameter bietet uns einen Satz von Konstanten an, die bestimmen, wie der VertexBuffer intern aufgebaut ist. Hier setzen wir Usage.WriteOnly ein, das besagt, das wir nur in den VertexBuffer schreiben wollen, ihn jedoch nicht auslesen(das kann zu kleineren Performancegewinnen führen)
Der vorletze Parameter ist das VertexFormat: CustomVertex.TransformedColored.Format
Der letzte Parameter bestimmt schließlich, wo wir unseren VertexBuffer speichern wollen. Hier können wir Pool.Default einsetzen, was besagt, dass Direct3D die Vertices einfach in den Video RAM plaziert. Pool.Managed übergibt die Kontrolle des VertexBuffers an den Grafikkartentreiber. Der kann dann z.B., wenn der Platz auf dem Video RAM knapp wird, unsere Vertices wieder in den RAM verschieben. Pool.SystemMemory besagt schlieslich, dass wir unsere Vertices im System RAM plazieren wollen, was die Vorteile eines VertexBuffers jedoch wieder zunichte macht.
In der Praxis sieht das nun so aus:


m_VertexBuffer = new VertexBuffer(typeof(CustomVertex.TransformedColored), 3, m_Device, Usage.WriteOnly, CustomVertex.TransformedColored.Format, Pool.Default);

Nun müssen wir unsere Vertices noch in den VertexBuffer hineinschreiben. Dazu müssen wir ihn zuerst sperren(locken), um sicherzustellen, dass während unseres Schreibvorgangs niemand anders auf den Buffer zugreift. Dann schreiben wir unsere Daten in den VertexBuffer und schlieslich entsperren wir ihne wieder(unlocken).Das ganze sieht so aus:


GraphicsStream stream = m_VertexBuffer.Lock(0,0,0);
			
			stream.Write(verts);
			m_VertexBuffer.Unlock();

Der Code bedarf eigentlich keiner großartigen Erklärungen, außer das die Parameter von Lock angeben, dass wir den ganzen VertexBuffer sperren wollen.
Nun kommen wir zum rendern.
Bevor wir unser Dreieck renden können, müssen wir unserem Device noch sagen, in welchem Format(CustomVertex.xxx) unsere Vertices gespeichert sind:


m_Device.VertexFormat = CustomVertex.TransformedColored.Format;

Jetzt haben wir zwei Möglichkeiten. Entweder wir rendern direkt aus dem SystemMemory heraus(unser Array verts liegt ja immer noch da drinnen) oder wir rendern vom VertexBuffer heraus. Ich zeig zuerst mal die Variante aus dem Systemmory, dazu nutzen wir die Funktion Device.


public void DrawUserPrimitives(
    PrimitiveType primitiveType,
    int primitiveCount,
    object vertexStreamZeroData
);

Der erste Parameter bestimmt den Primitive Type. Hier können wir entweder Punkte, Linien oder Dreiecke einsetzen.(Ein Primitiv ist nichts anderes als ein Punkt,Linie oder Dreieck) Wir setzen natürlich PrimitiveType.TriangleStrip ein. Es gibt mehrere Varianten von jedem Primitiv, TriangleSrip besagt, das wir, falls wir evtl. mehrere Dreiecke haben, die Dreiecke in einem Band miteinander verbunden gerendert werden.
Der nächste Parameter bestimmt die Anzahl der Primitive, die gezeichnet werden soll. Wir können unser verts Array z.B. mit 6 Elementen befüllen, um dann zwei Dreiecke zu zeichnen. Da wir aber nur ein Dreieck zeichnen wollen, setzen wir hier eine 1 ein.
Der letze Parameter bestimmt nun das Array, in dem unsere Vertices liegen: verts
Also hier der Code:


m_Device.DrawUserPrimitives(PrimitiveType.TriangleStrip,1,verts);

Nun kommen wir zur Methode mit den VertexBuffer. Bevor wir aus einem VertexBuffer heraus rendern können, müssen wir unserem Device sagen, aus welchem wir überhaupt rendern wollen(ja, wir können mehrere VertexBuffer haben...). Dazu nutzen wir die Funktion Device.SetStreamSource


m_Device.SetStreamSource(0,m_VertexBuffer,0);

Der erste Parameter bestimmt welchen Kanal(Vertex Stream) wir nutzen wollen. Einfach 0 einsetzen, denn das ist der 1. Vertex Kanal, auf dem die Vertices fließen.
Der zweite Parameter bestimmt jenden VertexBuffer, von dem die Daten kommen sollen.
Der letzte Parameter gibt den Offset vom Beginn des Streams bis zu den ersten Vertex Daten an. Hier setzen wir einfach wieder 0 ein.
Nun steht uns nichts mehr im Wege, aus dem VertexBuffer zu rendern. Und das machen wir via Device.DrawPrimitives.


m_Device.DrawPrimitives(PrimitiveType.TriangleStrip,0,1);

Die Parameter unterscheiden sich nicht allzu sehr von den Parametern von DrawUserPrimitives, bis auf 2., der den Start Vertex angibt, also den Vertex, ab dem gerendert werden soll(einfach 0). Der Letzte gibt wieder an, wie viele Primitive wir zeichnen wollen.

Dieses Kapitel war jetzt schon länger als das vorangehende. Zum Schluss nochmal der gesamte Code, wie immer gibts auch die Visual Studio .net 2003 Projektdateien zum Download:


using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace MDXTutorial02
{
	
	public class MDXSampleApp : System.Windows.Forms.Form
	{
		private Device m_Device;
		private VertexBuffer m_VertexBuffer;
		private CustomVertex.TransformedColored[] verts;
		

		// ctor
		// do nothing...
		public MDXSampleApp()
		{
		}

		// dtor
		~MDXSampleApp()
		{
		}

		public void InitGfx()
		{
			try
			{
				this.ClientSize = new Size(800,600);
				this.Text = "Direct3D Tutorial 2: Rendering colored Vertices";

				this.KeyPress += new KeyPressEventHandler(OnKeyPress);

				PresentParameters pp = new PresentParameters();

				pp.Windowed = true;
				pp.SwapEffect = SwapEffect.Copy;
				
				
				m_Device = new Device(Manager.Adapters.Default.Adapter,DeviceType.Hardware,this,CreateFlags.	HardwareVertexProcessing,
					pp);
				CreateVertexBuffer();

				
				
			
			}
			catch(DirectXException e)
			{
				MessageBox.Show(e.Message);
				
			}
		}

		private void CreateVertexBuffer()
		{
		
			verts = new CustomVertex.TransformedColored[3];

			verts[0].X=150;verts[0].Y=50;verts[0].Z=0.5f; verts[0].Rhw=1;verts[0].Color = Color.Yellow.ToArgb();
			verts[1].X=250;verts[1].Y=250;verts[1].Z=0.5f; verts[1].Rhw=1;verts[1].Color = Color.Bisque.ToArgb();
			verts[2].X=50;verts[2].Y=250;verts[2].Z=0.5f; verts[2].Rhw=1; ;verts[2].Color = Color.White.ToArgb();

			m_VertexBuffer = new VertexBuffer(typeof(CustomVertex.TransformedColored), 3, m_Device, Usage.WriteOnly, CustomVertex.TransformedColored.Format, Pool.Default);

			GraphicsStream stream = m_VertexBuffer.Lock(0,0,0);
			
			stream.Write(verts);
			m_VertexBuffer.Unlock();
		}

	
		public void Render()
		{
			

			m_Device.Clear(ClearFlags.Target ,Color.Blue,0.0f,0);
			
			m_Device.BeginScene();
			
			m_Device.VertexFormat = CustomVertex.TransformedColored.Format;

			m_Device.SetStreamSource(0,m_VertexBuffer,0);
			m_Device.DrawPrimitives(PrimitiveType.TriangleStrip,0,1);
		    //m_Device.DrawUserPrimitives(PrimitiveType.TriangleStrip,1,verts);
			
			m_Device.EndScene();
			m_Device.Present();

		}

		public void Shutdown()
		{
			m_Device.Dispose();
		}

		

	
		/// <summary>
		/// Der Haupteinstiegspunkt für die Anwendung.
		/// </summary>
		[STAThread]
		static void Main() 
		{
			MDXSampleApp example = new MDXSampleApp();
			example.InitGfx();
			example.Show();

			while(example.Created)
			{
				example.Render();
				Application.DoEvents();
			}
			example.Shutdown();

		}

		private void OnKeyPress(object sender, KeyPressEventArgs e)
		{
			if((int)e.KeyChar == (int)Keys.Escape)	
				this.Close();
			
		}
	}
}

11.04.2005 - 14:15 Uhr

Ab sofort ist die neueste Version des Tutorials hier zu erreichen.

Hi!

weil hier immer wieder mal nach DirectX Tutorials gefragt wird, hab ich einfach mich mal dazu entschlossen eins zu schreiben. Um das hier zu verstehen braucht man keine besondere Erfahrung mit C#. Aber man sollte schon sicher mit C# umgehen können und auch mit WindowsForms sollte man schon ein wenig gearbeitet haben. Feedback etc. sind natürlich erwünscht. Der Sourcecode dieses Tutorials ist am Schluss als Zip angehängt.

** DirectX: Eine Einführung **

DirectX wurde 1995 erstmals von Microsoft unter dem Namen The Games SDK(oder so...) vorgestellt. Davor war(zu DOS Zeiten) musste man umständlich auf Assembler zurückgreifen, wenn man irgendwas mit Grafik machen wollte. DirectX vereinfachte dies extrem und wurde von Version zu Version immer weiter entwickelt und auch immer besser. Derzeit stehen wir bei Version DirectX 9. DirectX ist eigentlich eine Sammlung von mehreren APIs. Diese sind DirectInput um Daten von Tastaturen,Mäuse,Joysticks etc. zu empfangen und verarbeiten, DirectSound/Music um Sounds/Musik zu spielen, DirectPlay um Netzwerkprogramme wie Multiplayer Spiele zu schreiben, DirectShow um Videos abzuspielen und zu guter Letzt Direct3D um auf dem Bildschirm zu zeichnen. Daneben gibt es noch DirectDraw, mit dem man ausschließlich 2D Objekte zeichnen kann(mit Direct3D kann man sowohl 3D als auch 2D Objekte zeichnen) und DirectSetup, ein Setup Generationsprogramm spez. für DirectX Applikationen.
Dieses Tutorial befasst sich also nicht mit dem gesamten DirectX Multimedia Packet, sondern "nur" mit Direct3D. Es muss natürlich gesagt werden, dass jeder Unterbereich von DirectX schon allein ziemlich groß ist. So, nun wollen wir mal sehen, welche Möglichkeiten uns Direct3D bietet. Ebenso werden die nächsten Kapitel ziemlich Theorie beinhalten, mit nur relativ wenig Code. Aber durchhalten. Wir werden bald zu unseren ersten Zeichenoperationen kommen...

** Direct3D: A closer look **

Voll DirectX 9 kompatibel.
Damit wird seit längerer Zeit von vielen Grafikkartenherstellern geworben. Die Grafikkartenhersteller meinen damit natürlich Direct3D.(DirectInput auf der Grafikkarte? ^^) Aber was macht Direct3D eigentlich genau? Direct3D bietet uns die Möglichkeit auf dem Bildschirm Objekte zu zeichnen. Denkt einfach mal an ein Spiel wie Far Cry. Der ganze tolle Dschungel dort wird mit Direct3D gezeichnet.(achja, wir haben noch einen langen Weg vor uns, bis wir sowas wie Far Cry machen können). Aber wie sind diese Objekte jetzt aufgebaut? Die Antwort ist einfach: Aus Dreiecken. Denn aus Dreiecken kann man jedes beliebige Objekt zusammenstellen. Nehmt einfach einen Zettel und malt ein Rechteck drauf. Wie ihr vielleicht sehen könnt, besteht ein Rechteck aus zwei sich gegenüberliegenen rechtwinkeligen Dreiecken. Eine solche Aufteilung in Dreiecken lässt sich praktisch bei jedem Objekt machen. D.h. wir können also Dreiecke mit Direct3D zeichen.(man kann aber auch Punkte und Linien zeichnen, aber am öftesten verwendet man Dreieckte). Aber Direct3D kann noch mehr. Es kann diese Dreiecke nun texturieren. Normalerweise kann man einem Dreieck nur eine Farbe zuweisen(blau,gelb,rot....), aber das ist natürlich langweilig. Deshalb erfand man eine Technik namens Texturieren. D.h. das man einfach ein Bild über das Dreieck legt. Damit sehen Objekte viel realitätsnäher aus. Desweiteren kann man mit Direct3D auch Licht erzeugen.(Lights) Das ist allerdings ziemlich komplex, daher werden wir das erst später besprechen. Zum Schluss kann Direct3D Dreiecke noch verschieben,vergrößern und verkleinern. Aber das werden wir auch noch später genauer kennen lernen.
so, jetzt haben wir uns mal einen groben Überblick über Direct3D verschafft. Jetzt können wir näher auf die einzelnen Bereiche eingehen. Zuerst sehen wir mal, wie wir Direct3D dazu bringen, dass es uns erlaubt irgendwas zu zeichnen.

** Direct3D: Erste Schritte **

Überlegen wir uns erstmal, was wir in diesem Kapitel erreichen wollen. Wir wollen ganz einfach ein Fenster erzeugen, das Blau(oder in irgendeiner anderen Farbe) gefärbt ist. Das ist sozusagen das "Hello World" von DirectX.
Was braucht man dazu? Ich sage mal ganz einfach, dass man mit einer IDE arbeiten sollte. Warum? Weil es das Leben vereinfacht. Ich selbst nutze VS.net 2003. Als nächstes braucht man halt die Standardtools, um mit C# zu programmieren. Zu guter Letzt braucht man noch das so genannte DirectX SDK. Das beinhaltet alles, was man braucht um mit DirectX zu programmieren. Nachdem das SDK installiert wurde, können wir weitermachen.(irgendwie logisch...).
Zunächst referenzieren wir auf folgende DLLs: System.dll,System.Drawing.dll,System.Windows.Forms.dll,Microsoft.DirectX.dll,Microsoft.DirectX.Direct3D.dll Microsoft.DirectX.Direct3DX.dll.
Nun binden wir folgende Namespaces ein, die eigentlich selbsterklärend sind:


using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

Für unsere erste DirectX Anwendung brauchen wir nur eine einzige Membervariable. Das DirectX Device(Microsoft.DirectX.Direct3D.Device m_Device). Ein Device ist die elementare Grundverbindung zwischen unserer Anwendung und Direct3D. Mit einem Device können wir alle oben genannten Dinge machen. Nun implementieren wir eine Funktion InitGfx(), die uns das Device erstellt.
Die ersten Zeilen dieser Funktion sollten kein Problem darstellen:


public void InitGfx()
{
	try
	{
		this.ClientSize = new Size(1024,768);
		this.Text = "Direct3D Tutorial 1";

		this.KeyPress += new KeyPressEventHandler(OnKeyPress);

Nun kommen wir zum interessanten Teil: Die PresentParameter. Diese werden wir später zum erstellen des Devices benötige. Zuerst mal der Code:


PresentParameters pp = new PresentParameters();

pp.Windowed = true;
pp.SwapEffect = SwapEffect.Copy;

Die PresentParamter(Präsentations Parameter) bestimmen also wie sich das Device grundlegend verhalten soll.(es gibt noch mehr, aber in unserem Fall reichen diese aus). Für unsere Applikation brauchen wir zunächst nur diese beiden Parameter. Der erste Paramter zeigt an, das wir eine Fensteranwendung haben wollen. Der Zweite gibt an, wie wir die Bilder auf den Bildschirm bringen wollen. Copy bedeutet einfach, dass wir immer den gesamten Bildschirm neuzeichnen wollen.
Nun geht daran, das Device zu erstellen:


m_Device = new Device(Manager.Adapters.Default.Adapter,DeviceType.Hardware,this,CreateFlags.HardwareVertexProcessing,
										pp);

ok. Gehen wir Parameter für Parameter durch:
Manager.Adapters.Default.Adapter: Gibt an, auf welchem Adapter(=Bildschirm) wir zeichnen wollen. Normalerweise haben die meisten Systeme einen Bildschirm. Und diesen Bildschirm repräsentiert Manager.Adapters.Default.Adapter.
DeviceType.Hardware: Das bedeutet, das für alle möglichen Operationen von Direct3D die Grafikhardware verwendet wird. Man kann auch bestimmen, das das die CPU(Software) machen soll, was aber ziemlich langsam ist.
this: Zeigt einfach an, das das Devie einfach dieses Fenster zum zeichnen verwenden soll.
CreateFlags.HardwareVertexProcessing: Besagt ebenfalls grundsätzlich das selbe wie DeviceType.Hardware, aber ist trotzdem ein wenig anders, außerdem gibts da noch ein paar andere Paramter, die aber für uns(derzeit) nicht interessant sind. Ich werde später noch genauer darauf eingehen.
pp: Zeigt auf die ausgefüllte PresentParameters Struktur.

Und jetzt noch der Abschluss der Funktion:


}
			catch(DirectXException e)
			{
				MessageBox.Show(e.Message);
			}
		}

Gut. Nun kommen wir zum Zeichnen. Das wird als Rendern bezeichnet. Deshalb implementieren wir einfach mal eine Funktion namens Render.


public void Render()
		{
			m_Device.Clear(ClearFlags.Target ,Color.Blue,0.0f,0);
			m_Device.BeginScene();
                             // rendern
			m_Device.EndScene();
			m_Device.Present();

		}

Der erste Funktionsaufruf ist nicht sonderlich interessant(derzeit...) und besagt, dass das Fenster(Target) mit einem blauen Hintergrund gerendert werden soll.
Danach kommen wir zum eigentlichen rendern. Das rendern geschieht immer zwischen einem BeginScene und einem EndScene. Doch das werden wir später auch noch genauer besprechen. Das Present veranlsst Direct3D nun, alles auch wirkich zu rendern.
Nun brauchen wir noch eine Funktion, die Direct3D herunterfährt und noch die Main Funktion. Schließlich implementieren wir noch die OnKeyPress Funktion...


public void Shutdown()
		{
			m_Device.Dispose();
		}

		

	
		/// <summary>
		/// Der Haupteinstiegspunkt für die Anwendung.
		/// </summary>
		[STAThread]
		static void Main() 
		{
			MDXSampleApp example = new MDXSampleApp();
			example.InitGfx();
			example.Show();

			while(example.Created)
			{
				example.Render();
				Application.DoEvents();
			}
			example.Shutdown();

		}

		private void OnKeyPress(object sender, KeyPressEventArgs e)
		{
			if((int)e.KeyChar == (int)Keys.Escape)	
				this.Close();
		}

Hier jetzt der gesamte Source: (insgesamt rund 100 Zeilen)


using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace MDXTutorial01
{
	/// <summary>
	/// Zusammenfassung für Form1.
	/// </summary>
	public class MDXSampleApp : System.Windows.Forms.Form
	{
		private Device m_Device;


		// ctor
		// do nothing...
		public MDXSampleApp()
		{
			
		}

		// dtor
		// shutdown D3D
		 ~MDXSampleApp()
		{
			
			 
		}

		public void InitGfx()
		{
			try
			{
				this.ClientSize = new Size(1024,768);
				this.Text = "Direct3D Tutorial 1";

				this.KeyPress += new KeyPressEventHandler(OnKeyPress);

				PresentParameters pp = new PresentParameters();

				pp.Windowed = true;
				pp.SwapEffect = SwapEffect.Copy;
				
				m_Device = new Device(Manager.Adapters.Default.Adapter,DeviceType.Hardware,this,CreateFlags.HardwareVertexProcessing,
										pp);

			}
			catch(DirectXException e)
			{
				MessageBox.Show(e.Message);
			}
		}
		public void Render()
		{
			m_Device.Clear(ClearFlags.Target ,Color.Blue,0.0f,0);
			m_Device.BeginScene();
			m_Device.EndScene();
			m_Device.Present();

		}

		public void Shutdown()
		{
			m_Device.Dispose();
		}

		

	
		/// <summary>
		/// Der Haupteinstiegspunkt für die Anwendung.
		/// </summary>
		[STAThread]
		static void Main() 
		{
			MDXSampleApp example = new MDXSampleApp();
			example.InitGfx();
			example.Show();

			while(example.Created)
			{
				example.Render();
				Application.DoEvents();
			}
			example.Shutdown();

		}

		private void OnKeyPress(object sender, KeyPressEventArgs e)
		{
			if((int)e.KeyChar == (int)Keys.Escape)	
				this.Close();
		}
	}
}

So, das wars. Im nächsten Teil werden wir genauer aufs Rendern und auch auf den Fullscreen Modus zum sprechen kommen.