www.jammni.de

Logo - Kleiner Drache

Logo - Drache4. Matrizen und Rotation

Home >> Tutorials >> DirectX 9−Tutorial
  Diesmal schauen wir uns das Ganze mal in 3D an. Das heißt, es werden nicht bereits transformierte Koordinaten verwendet, sondern ganz normale Koordinaten, die Direct3D für die Darstellung erst umrechnen muss. Außerdem wird ein zweites Viereck erstellt, welches das erste Viereck senkrecht schneidet. Und damit man auch wirklich sieht, dass es sich nicht mehr um eine "flache" Welt handelt, wird die ganze Welt noch permanent um die y-Achse gedreht.
Im Folgenden wird der Quelltext wieder angezeigt - allerdings nur noch die relevanten und geänderten Abschnitte. Den vollständigen Quelltext gibt’s wie immer als Download.

Bild von der Ausgabe des DirectX-Programms mit den beiden sich überlappenden Vierecken
Listing 6 - Rotation.cs:
12:
 
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
 
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
  bool play = true;
  ...
  public bool InitializeGraphics()
  {
    try
    {
      // Die Hardware initialisieren und ein paar Parameter setzen
      PresentParameters presentParams = new PresentParameters();
      presentParams.Windowed = true;
      presentParams.SwapEffect = SwapEffect.Discard;
      device = new Device(0, DeviceType.Hardware, this,
               CreateFlags.SoftwareVertexProcessing, presentParams);
      device.DeviceReset += new System.EventHandler(this.OnResetDevice);
      // Beide Seiten der Dreiecke anzeigen
      device.RenderState.CullMode = Cull.None;
      // Direct3D-Beleuchtung deaktivieren
      device.RenderState.Lighting = false;
      vertexBuffer = new VertexBuffer(typeof(CustomVertex.PositionColored), 12, device,
                     0, CustomVertex.PositionColored.Format, Pool.Default);
      vertexBuffer.Created += new System.EventHandler(this.OnCreateVertexBuffer);
      this.OnCreateVertexBuffer(vertexBuffer, null);
      play = true;
      return true;
    }
    catch (DirectXException)
    {
      return false;
    }
  }
 
  public void OnResetDevice(object sender, EventArgs e)
  {
    // damit werden die Vierecke auch noch angezeigt, wenn das Fenster vergrößert wird
    Device device = (Device)sender;
    // Beide Seiten der Dreiecke anzeigen
    device.RenderState.CullMode = Cull.None;
    // Direct3D-Beleuchtung deaktivieren
    device.RenderState.Lighting = false;
  }
 
  public void OnCreateVertexBuffer(object sender, EventArgs e)
  {
    VertexBuffer vertexBuffer = (VertexBuffer)sender;
    GraphicsStream stm = vertexBuffer.Lock(0, 0, 0);
    CustomVertex.PositionColored[] verts = new CustomVertex.PositionColored[12];
 
    verts[0].X = -1.0f;
    verts[0].Y = 1;
    verts[0].Z = 0;
    verts[0].Color = Color.Blue.ToArgb();
    verts[1].X = 1;
    verts[1].Y = 1;
    verts[1].Z = 0;
    verts[1].Color = Color.Red.ToArgb();
    verts[2].X = -1f;
    verts[2].Y = -1f;
    verts[2].Z = 0;
    verts[2].Color = Color.Lime.ToArgb();
    verts[3].X = 1;
    verts[3].Y = 1;
    verts[3].Z = 0;
    verts[3].Color = Color.Red.ToArgb();
    verts[4].X = 1;
    verts[4].Y = -1f;
    verts[4].Z = 0;
    verts[4].Color = Color.Yellow.ToArgb();
    verts[5].X = -1f;
    verts[5].Y = -1f;
    verts[5].Z = 0f;
    verts[5].Color = Color.Lime.ToArgb();
 
    verts[6].X = 0f;
    verts[6].Y = 1;
    verts[6].Z = -1f;
    verts[6].Color = Color.Blue.ToArgb();
    verts[7].X = 0;
    verts[7].Y = 1;
    verts[7].Z = 1;
    verts[7].Color = Color.Red.ToArgb();
    verts[8].X = 0;
    verts[8].Y = -1f;
    verts[8].Z = -1f;
    verts[8].Color = Color.Lime.ToArgb();
    verts[9].X = 0;
    verts[9].Y = 1;
    verts[9].Z = 1;
    verts[9].Color = Color.Red.ToArgb();
    verts[10].X = 0;
    verts[10].Y = -1f;
    verts[10].Z = 1;
    verts[10].Color = Color.Yellow.ToArgb();
    verts[11].X = 0;
    verts[11].Y = -1f;
    verts[11].Z = -1f;
    verts[11].Color = Color.Lime.ToArgb();
    stm.Write(verts);
    vertexBuffer.Unlock();
  }
 
  private void Render()
  {
    if ((device == null) || (!play))
      return;
 
    // Backbuffer löschen und gleichzeitig die Zeichenfläche schwarz einfärben
    device.Clear(ClearFlags.Target, Color.Black, 1.0f, 0);
    // Anfang der Szene
    device.BeginScene();
    // Matrizen aufsetzen
    SetupMatrices();
 
    device.SetStreamSource(0, vertexBuffer, 0);
    device.VertexFormat = CustomVertex.PositionColored.Format;
    device.DrawPrimitives(PrimitiveType.TriangleList, 0, 4);
 
    // Ende der Szene
    device.EndScene();
    device.Present();
  }
 
  private void SetupMatrices()
  {
    // Quadrat um y-Achse alle 1000 Ticks rotieren (eine volle Umdrehung)
    // Zuerst die vergangene Zeit bestimmen
    int iTime = Environment.TickCount % 1000;
    // Anschließend diesen Wert auf 2*PI (360°) umrechnen
    float fAngle = iTime * (2.0f * (float) Math.PI) / 1000.0f;
    // Und nun die Welt um die Y-Achse um fAngle drehen
    // und gibt die entsprechende Matrix zurück
    device.Transform.World = Matrix.RotationY(fAngle);
 
    // Nun den Benutzerblickwinkel aufsetzen
    // Linke-Hand-orientiertes System aufbauen
    // Parameter 1: Kamera-Position
    // Parameter 2: Punkt, auf den geschaut wird
    // Parameter 3: Vektor, der angibt, wo oben ist (normal [0|1|0])
    device.Transform.View = Matrix.LookAtLH(new Vector3(0.0f, 2.0f, -5.0f),
                            new Vector3(0.0f, 0.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f));
 
    // Und die Ansichtsmatrix aufsetzen
    // Linke-Hand-orientiertes System aufbauen
    // Parameter 1: Sichtbereich (Blickwinkel)
    // Parameter 2: Seitenverhältnis
    // Parameter 3: Abstand zum nächsten sichtbaren Punkt (nahe Ebene)
    // Parameter 4: Abstand zum letzten sichtbaren Punkt (ferne Ebene)
    device.Transform.Projection = Matrix.PerspectiveFovLH((float)Math.PI / 4,
                                  ClientSize.Width / (float)ClientSize.Height, 1.0f, 100.0f);
  }
  ...
  protected override void OnKeyPress(System.Windows.Forms.KeyPressEventArgs e)
  {
    if ((int)(byte)e.KeyChar == (int)System.Windows.Forms.Keys.Escape)
      this.Close(); // Esc wurde gedrückt
    if ((e.KeyChar.Equals('p')) ||(e.KeyChar.Equals('P')))
      play = !play;
  }
 
  protected override void OnResize(System.EventArgs e)
  {
    play = Visible;
  }
Download des Beispiels (rar-Datei, 6.228 Bytes) | Download als Visual C#-Projekt (rar-Datei, 22.639 Bytes)

  Auf den folgenden Seiten dieses Kapitels werden alle Neuerungen des Quelltextes besprochen und es gibt einen kleinen Exkurs in Koordinatensysteme.
Wer das Programm bereits ausgeführt hat, wird feststellen, dass die beiden Vierecke nicht wirklich korrekt angezeigt werden. Das liegt daran, dass noch kein Clipping durchgeführt wird und die Dreiecke immer in derselben Reihenfolge gezeichnet werden. Dazu gibts aber später noch mehr Informationen.
Falls ihr diesen Fehler nicht gleich erkennen solltet, so könnt ihr auch einfach P drücken, um das Bild einzufrieren. Mit einem weiteren Druck auf diese Taste rotieren die beiden Vierecke weiter.