Terrain 101: Land in Sicht

Im letzten Artikel dieser Artikelreihe hatte ich ja bereits versprochen, daß wir diesmal die Grundlagen hinter uns lassen und wir das erste Stückchen Terrain sichtbar machen wollen. Dies werde ich nun in die Tat umsetzen und daher steht dieser Artikel unter dem Motto

Land in Sicht

Wir werden in diesem Tutorial das erste Drahtgittermodell einer Landschaft erzeugen. Sicherlich sind wir danach immer noch sehr weit vom Endergebnis entfernt, aber trotzdem wird es ab diesem Teil langsam interessant, denn es werden Ergebnisse sichtbar.
Wichtiges Update: Im Artikel hatte sich ein Fehler eingeschlichen, der dazu geführt hat, dass auf ATI-Grafikkarten nichts gerendert wurde und nur ein blauer Bildschirm erschienen ist. Ich habe den Fehler im Artikel korrigiert.

Auch diesen Artikel werde ich, wie alle bisherigen Artikel auch, mit einem Inhaltsverzeichnis beginnen um die Navigation zu erleichtern. Dieses verlinkt alle bisherigen und zukünftigen Artikel dieser Tutorial-Reihe in chronologischer Reihenfolge. So ist es einfach möglich, bestimmte Themen zu überspringen oder zu diesen zu navigieren. Das Inhaltsverzeichnis in den älteren Artikeln werde ich ebenfalls entsprechend aktualisieren.

Das Inhaltsverzeichnis

Terrain 101: Eine Einführung in dreidimensionale Landschaften in Echtzeit
Terrain 101: Die Entwicklungsumgebung und das XNA 4.0 Projekt
Terrain 101: Das erste Dreieck
Terrain 101: Transformationen
Terrain 101: Vertex- und Index-Buffer
Terrain 101: Land in Sicht
Terrain 101: Technische Rafinessen
Terrain 101: Neue Sichtweisen
Terrain 101: Kamera ab und Action

Einleitung

In diesem Artikel werden wir den ersten, sogenannten Chunk eines Terrains erzeugen. Ein Chunk ist ein rechteckiger Bereich, der das Drahtgittermodell einer Landschaft darstellt. Wie im einleitenden Artikel bereits beschrieben, hat eine große Landschaft viel zu viele Dreiecke um alle gleichzeitig darzustellen. Durch das zerstückeln der Landschaft können wir dem entgegenwirken. Und diese einzelnen Stücke sind eben diese Chunks. Wir werden später – in Abhängigkeit von der Sichtweite – eine Menge Chunks um den Spieler herum anordnen, aber jeweils nur die sichtbaren davon darstellen. Dies nennt man Culling. Die näheren Chunks werden wir mit einem hohen Detailgrad darstellen und die weiter entfernten mit einem niedrigeren. Dies nennt man Level of Detail (LOD).

Heightmaps

Eine Heightmap ist eine Art Landkarte in der Höhenwerte gespeichert sind. Diese Höhenwerte geben für einen bestimmten Punkt in unserer Landschaft an, wie hoch sie an dieser Stelle sein soll. Steht dort z.B. 3.0f für den Punkt 0,0 verzeichnet, so könnte dies – je nach verwendeter Skalierung – bedeuten, daß die Landschaft an dieser Stelle 3m hoch ist. Man kann dafür sehr gut Texturen verwenden, die man am Besten als Graustufen darstellt.

Mit dem kostenlosen Programm Paint.NET ist ein verwendbarer Entwurf schnell erzeugt. Dazu erstellen wir über Datei/Neu ein neues Bild mit der Größe 128×128 Pixel, welches später die Größe unseres Terrain-Chunks darstellt. Im Farbfenster wählen wir – falls nicht bereits geschehen – als Vordergrundfarbe weiß (linke Maustaste) und als Hintergrundfarbe schwarz (rechte Maustaste). Danach rendern wir ein paar Wolken mittels Effekte/Wiedergabe/Wolken. Es erscheint ein kleiner Dialog in dem wir die Wolken beeinflussen können. Diese werden mittels Perlin-Noise erzeugt. In einem späteren Tutorial werden wir sowas selber erzeugen, für den Moment reicht diese kleine Textur aber vollkommen aus. Wir speichern sie nun mit Datei/Speichern unter im Content-Verzeichnis unseres Projektes oder ihr ladet euch einfach folgendes Beispielbild herunter.

Alle dunklen Bereiche dieser Heightmap sind später tiefe Bereiche in unserer Landschaft und die hellen sind hohe Stellen.

Als Basis verwenden wir nun den Code aus dem letzten Teil Terrain 101: Vertex- und Index-Buffer und fügen zunächst im Bereich der Member-Variablen am Anfang der Game-Klasse eine neue Variable für unsere Heightmap ein.

Texture2D heightmap;

Diese laden wir dann in der LoadContent-Methode mittels folgendem Code, den wir hinzufügen.

heightmap = Content.Load<Texture2D>("ChunkHeightmap");

SetupFirstChunk();

Damit dieser Code funktioniert müssen noch zwei Voraussetzungen geschaffen werden. Zum einen muss die Heightmap-Textur zum Content-Projekt hinzugefügt werden. Dies geschieht durch einen Klick mit der rechten Maustaste auf das Content-Projekt. Im erscheinenden Datei-Auswahldialog wählen wir die Datei, die wir in unserem Zeichenprogramm gespeichert haben.

Der nächste Schritt ist die Implementation der Methode SetupFirstChunk, die ich im nächsten Abschnitt beschreiben werde. Die letzte Aktion in diesem Abschnitt wird also die Erzeugung des Methodenrahmens sein.

private void SetupFirstChunk()
{

}

Die Methoden SetupVertexBuffer und SetupIndexBuffer, sowie deren Aufrufe können wir aus dem Projekt löschen, da wir diese nicht mehr benötigen.

Triangulierung eines Chunk

Ein Chunk besteht natürlich wie jedes andere 3D-Objekt auch, aus Dreiecken, die z.B. wie folgt angeordnet sind.

In der Konzeptzeichnung wird ein Chunk der Größe 4×4 dargestellt. Wir werden hier jedoch einen Chunk der Größe 128×128 erzeugen.

Die komplette Methode SetupFirstChunk sieht wie folgt aus. Ich werde im Anschluss erklären, was dort genau passiert und warum wir so vorgehen, wie es dort niedergeschrieben ist.

        private void SetupFirstChunk()
        {
            Color[] heights = new Color[heightmap.Width * heightmap.Height];
            heightmap.GetData<Color>(heights);

            VertexPositionColor[] vertices = new VertexPositionColor[128 * 128];
            int index = 0;

            for (int z = 0; z < 128; z++)
            {
                for (int x = 0; x < 128; x++)
                {
                    vertices[z * 128 + x] = new VertexPositionColor(new Vector3(x * 10.0f, heights[z * 128 + x].R, z * 10.0f), Color.Green);
                }
            }

            this.vertexBuffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColor), 128 * 128, BufferUsage.WriteOnly);
            this.vertexBuffer.SetData<VertexPositionColor>(vertices);

            int[] indices = new int[127 * 127 * 6];
            index = 0;

            for (int z = 0; z < 127; z++)
            {
                for (int x = 0; x < 127; x++)
                {
                    indices[index + 0] = z * 128 + x;
                    indices[index + 1] = indices[index + 0] + 128 + 1;
                    indices[index + 2] = indices[index + 0] + 128;
                    indices[index + 3] = indices[index + 0];
                    indices[index + 4] = indices[index + 0] + 1;
                    indices[index + 5] = indices[index + 0] + 128 + 1;

                    index += 6;
                }
            }

            this.indexBuffer = new IndexBuffer(GraphicsDevice, IndexElementSize.ThirtyTwoBits, 127 * 127 * 6, BufferUsage.WriteOnly);
            this.indexBuffer.SetData<int>(indices);
        }

Zur Ermittlung der Höhenwerte für unsere Landschaft benötigen wir Zugriff auf die Textur. Dies ist möglich, indem wir die Daten der Textur in ein Color-Array kopieren. Wir kopieren also die Farbe jedes einzelnen Pixel aus der Textur in ein Array. Dies geschieht in den Zeilen 3 und 4. Dabei ist zu beachten, daß GetData tatsächlich dazu führt, daß Daten vom Speicher der Grafikkarte in den Arbeitsspeicher des Computers kopiert werden. Dies ist ein relativ langsamer Vorgang, der möglichst nur zum Vorbereiten von Daten verwendet wird. Dieser Befehl sollte auch möglichst selten verwendet werden. Ich habe bereits einen Grundlagenartikel mit der Erklärung in Vorbereitung. Leider konnte ich diesen nicht mehr fertigstellen, bevor ich diesen Teil des Tutorials veröffentlicht habe.

Danach erzeugen wir die einzelnen Vertices unserer Landschaft. Der Code dazu ist uns bereits aus dem letzten Teil (und den darin enthaltenen Übungen) bekannt, daher gehe ich nur auf den interessanten Teil ein, der Berechnung der Position des Vertex. Auf der X-Achse (horizontale Achse der obigen Konzeptzeichnung des Chunks) multiplizieren wir einfach die aktuelle Vertex-Position mit 10.0f. Die vertikale Achse in der Konzeptzeichnung entspricht der Z-Achse in unserer 3D-Welt. Diese multipliziere ich ebenfalls mit 10.0f. Dies führt dazu, daß wir später viele Rechtecke haben, die aus jeweils zwei Dreiecken bestehen. Die Rechtecke haben dabei eine Kantenlänge von 10.0f * 10.0f. Dies könnte man z.B. mit 1m * 1m gleichsetzen. Es gibt hier keine feste Maßeinheit, da 3D-Koordinaten immer relativ sind. Die Höhe der Landschaft definieren wir durch die Y-Achse und fügen dort einfach den Farbwert des roten Kanals aus unserem Farbarray ein. Wer aufmerksam war erinnert sich: dunkle Farben = niedriger Rotwert = niedrige Höhe; helle Farben = hoher Rotwert = größere Höhe. In einer Graustufengrafik wie wir sie hier vorliegen haben sind übrigens alle Farbkanäle mit dem gleichen Wert gefüllt.

Interessant ist noch die Berechnung der Position in unserem Array. Dieses hat ja nur eine Dimension und daher müssen wir die Position berechnen. Dazu multiplizieren wir einfach die aktuelle Zeile z (wobei z hier nicht für Zeile steht, sondern für die Z-Koordinate) mit der Breite des Chunks (128) und addieren die aktuelle X-Koordinate. Wer dies nachvollziehen möchte, sollte sich das mal auf ein Stück Papier aufmalen (am besten mit 4×4 oder 5×5) und nachrechnen. Dann wird das Konzept dahinter schnell klar.

Nun folgt der Index-Buffer. In diesem müssen wir aus den vorliegenden Vertices Dreiecke erzeugen. Dazu nehmen wir jeweils 4 Vertices (also ein Rechteck) und erzeugen daraus genau zwei Dreiecke. Dafür benötigen wir insgesamt 6 Indices, weshalb wir in den Zeilen 20 und 38 auch die Größe auf 128 * 128 * 6 festlegen. Also 128 * 128 Vertices mit jeweils 6 Indices.

Um den Rumpf der Schleife zu verstehen empfehle ich wieder die Verwendung eines Stück Papiers auf dem ihr in einem kleinen Chunk von z.B. 5×5 Größe einfach nachvollzieht was dort passiert. Es werden einfach Dreiecke generiert. Und leider ist dies mit Text ein wenig schwer zu erklären.

Um das Ganze jetzt darzustellen benötigen wir noch eine neue virtuelle Kamera. Dazu verändern wir die View-Matrix. Und da unsere Landschaft mittlerweile schon eine Kantenlänge von 1280.0f mal 1280.0f hat, müssen wir auch unsere Far-Plane vergrößern, damit nichts abgeschnitten wird. Was dies exakt bedeutet werde ich im Tutorial-Teil über das Culling noch genauer erklären. Der Code dazu sieht wie folgt aus und gehört in die Initialize-Methode.

projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio, 1.0f, 2000.0f);
viewMatrix = Matrix.CreateLookAt(new Vector3(640.0f, 640.0f, 1280.0f), new Vector3(640.0f, 0.0f, 640.0f), Vector3.Up);

Selbstverständlich müssen wir in der Draw-Methode unserem eigentlichen Draw-Call noch die neue Anzahl der Vertices und die Anzahl der Dreiecke übergeben. Dies geschieht wie folgt.

GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 128 * 128 * 6, 0, (127 * 127 * 6) / 3);

Dabei sind die beiden Werte natürlich einfach vorzuberechnen, was ich auch dringend empfehle. Auch können diese einfacher berechnet werden, auf die Division kann z.B. durch verkleinerung des Faktors auf 2 vollkommen verzichtet werden. Ich habe dies nur so gemacht, damit dies plastischer ist und damit einfacher zu verstehen ist.

Wichtig an dieser Stelle ist, dass die Anzahl der Primitive die durch den letzten Parameter angegeben werden nicht zu groß wird. Wenn dort mehr Primitive stehen, als vorhanden sind, dann rendern ATI-Grafikkarten einfach nichts. Wie man solche Fehler prüfen kann, beschreibe ich in einem Artikel der Reihe Facepalm, da ich auch exakt von diesem Fehler erwischt wurde.

Bevor wir nun unseren Code mit F5 starten müssen wir noch – da noch keine Texturen und Beleuchtung vorhanden ist – einen eigenen RasterizerState erzeugen, damit wir unseren Chunk als Drahtgittermodell sehen. Würden wir einfach die Dreiecke wie bisher rendern, so würden wir eine ziemlich grüne Fläche sehen, aber die Details nicht erkennen. Diese werden erst durch Licht und Schatten sichtbar.

Wir erzeugen also eine Member-Variable für den RasterizerState.

RasterizerState rs;

Danach erzeugen wir unseren eigenen RasterizerState in der Initialize-Methode.

rs = new RasterizerState();
rs.FillMode = FillMode.WireFrame;

Dabei erzeugen wir in Zeile eins eine neue Instanz des RasterizerState und legen in der zweiten Zeile fest, daß der Füllmodus WireFrame sein soll.

In der Draw-Methode setzen wir nun unseren RasterizerState direkt nach dem löschen des Backbuffers.

GraphicsDevice.RasterizerState = rs;

Nun können wir aber endlich mit F5 starten und sehen unseren ersten Chunk als Drahtgittermodell.

Wie versprochen ist dies nun das erste Stück Land. Da wir damit das Ziel dieses Teils erreicht haben, möchte ich diesen Teil nun auch langsam beenden. Wir haben nun einen großen Schritt in Richtung virtuelle Landschaften gemacht.

Abschluss und Ausblick

Ich habe in diesem Artikel gezeigt, wie man eine einfache Heightmap erzeugt und diese zur Verwendung einer Landschaft benutzt. Ich habe mit euch aufbauend auf die letzten Teile einen Index- und Vertex-Buffer erzeugt, der die Daten unseres ersten Terrain-Chunks enthält. Dabei habe ich bewusst ein paar Erklärungen und Grundlagen ausgelassen, damit wir erstmal ein wenig „Eye Candy“ sehen. Im nächsten Artikel dieser Reihe werde ich einige Punkte aus diesem Teil nochmal aufgreifen und diese als Beispiel nehmen um ein paar Grundlagen zu erklären. Dabei möchte ich z.B. auf das Backface-Culling eingehen, aber auch auf ein paar technische Aspekte wie z.B. Render-States, Near- und Far-Planes und Vertex-Cache. Dieser Artikel wird leider ein wenig trockener werden und vermutlich keinen neuen Code enthalten. Trotzdem wird dieser Artikel hoffentlich interessante Informationen enthalten, die zumindest immens wichtig sind.

Wie immer freue ich mich über Fragen und Anregungen in den Kommentaren dieses Artikels und hoffe, daß diese knapp 2200 Worte hilfreich waren.

Aufgaben für den Leser

Diesmal habe ich keine speziellen Aufgaben für euch, sondern möchte euch nur bitten mit der Heightmap, den unterschiedlichen Faktoren und der Größe des Chunks zu spielen, um ein besseres Gefühl für das Verhalten zu erlangen. Wechselt einfach mal die Heightmap aus, versucht vielleicht mal mit einem weißen Pinsel auf schwarzem Grund zu zeichnen etc. und schaut euch an, wie die später aussieht.

Der gesamte Sourcecode dieses Artikels

Damit die Übersicht gewahrt bleibt ist hier nun wie immer der gesamte Source-Code dieses Artikels als zusammenhängender Block vorhanden.

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Terrain_101
{
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Effect triangleEffect;

        VertexBuffer vertexBuffer;
        IndexBuffer indexBuffer;

        Matrix projectionMatrix;
        Matrix viewMatrix;

        Texture2D heightmap;

        RasterizerState rs;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

            this.Window.Title = "http://www.MitOhneHaare.de - Terrain 101 - Land in Sicht (Teil 6)";
        }

        protected override void Initialize()
        {
            rs = new RasterizerState();
            rs.FillMode = FillMode.WireFrame;

            projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, GraphicsDevice.Viewport.AspectRatio, 1.0f, 2000.0f);
            viewMatrix = Matrix.CreateLookAt(new Vector3(640.0f, 640.0f, 1280.0f), new Vector3(640.0f, 0.0f, 640.0f), Vector3.Up);

            base.Initialize();
        }

        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            triangleEffect = Content.Load<Effect>("Triangle");

            heightmap = Content.Load<Texture2D>("ChunkHeightmap");

            SetupFirstChunk();
        }

        protected override void UnloadContent()
        {
            if (indexBuffer != null)
            {
                indexBuffer.Dispose();
                indexBuffer = null;
            }

            if (vertexBuffer != null)
            {
                vertexBuffer.Dispose();
                vertexBuffer = null;
            }

            base.UnloadContent();
        }

        private void SetupFirstChunk()
        {
            Color[] heights = new Color[heightmap.Width * heightmap.Height];
            heightmap.GetData<Color>(heights);

            VertexPositionColor[] vertices = new VertexPositionColor[128 * 128];
            int index = 0;

            for (int z = 0; z < 128; z++)
            {
                for (int x = 0; x < 128; x++)
                {
                    vertices[z * 128 + x] = new VertexPositionColor(new Vector3(x * 10.0f, heights[z * 128 + x].R, z * 10.0f), Color.Green);
                }
            }

            this.vertexBuffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColor), 128 * 128, BufferUsage.WriteOnly);
            this.vertexBuffer.SetData<VertexPositionColor>(vertices);

            int[] indices = new int[127 * 127 * 6];
            index = 0;

            for (int z = 0; z < 127; z++)
            {
                for (int x = 0; x < 127; x++)
                {
                    indices[index + 0] = z * 128 + x;
                    indices[index + 1] = indices[index + 0] + 128 + 1;
                    indices[index + 2] = indices[index + 0] + 128;
                    indices[index + 3] = indices[index + 0];
                    indices[index + 4] = indices[index + 0] + 1;
                    indices[index + 5] = indices[index + 0] + 128 + 1;

                    index += 6;
                }
            }

            this.indexBuffer = new IndexBuffer(GraphicsDevice, IndexElementSize.ThirtyTwoBits, 127 * 127 * 6, BufferUsage.WriteOnly);
            this.indexBuffer.SetData<int>(indices);
        }

        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // TODO: Add your update logic here

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            GraphicsDevice.RasterizerState = rs;

            triangleEffect.Parameters["World"].SetValue(Matrix.Identity);
            triangleEffect.Parameters["View"].SetValue(viewMatrix);
            triangleEffect.Parameters["Projection"].SetValue(projectionMatrix);

            triangleEffect.CurrentTechnique.Passes[0].Apply();

            GraphicsDevice.SetVertexBuffer(this.vertexBuffer);
            GraphicsDevice.Indices = indexBuffer;

            GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 128 * 128 * 6, 0, (127 * 127 * 6) / 3);

            base.Draw(gameTime);
        }
    }
}
Advertisements

Veröffentlicht am 07.09.2011 in 3D Terrain, Tutorial, XNA, XNA 4.0 und mit , , , , , , , , , , getaggt. Setze ein Lesezeichen auf den Permalink. 57 Kommentare.

  1. Erstmal, ein sehr gutes Tutorial, wie immer. Ich hätte da noch ne frage bei der initialiesierung des VertexBuffers.

    vertices[z * 128 + x] = new VertexPositionColor(new Vector3(x * 10.0f, heights[z * 128 + x].R, z * 10.0f), Color.Green);

    würde nicht vertices[x * z] = … reichen, ich verstehe den Algorythmus dahinter leider nicht.

    • Nein, x*z geht nicht, weil:

      x gibt die Position innerhalb der Zeile an, läuft also für jede Spalte in der Zeile von 0 bis 127. Wir müssen nun noch die Zeile berücksichtigen, also den Zeilenanfang. Die Zeile wird mit der Variable z durchlaufen. Die erste Zeile fängt bei 0 an, die Zweite bei 128, die Dritte bei 256 etc. Um den Zeilenanfang im Array zu erhalten muss man also z mit 128 multiplizieren.

      Nimm mal ein 5×5 Array mit 25 Elementen und stelle das auf Papier nach, dann kommst du echt schnell dahinter, falls die Beschreibung noch nicht ausreichen sollte.

    • Jeha ! Das ist das erste tuorial bei dem ich das wirklich gut verstehe …
      Danke ! Mach weiter so !

    • Hier : “ triangleEffect.Parameters[„World“].SetValue(Matrix.Identity); “
      in der Draw-Methode macht er es beim Ausführen gelb und sagt :
      Der Objektveweis wurde nicht auf eine Objektinstanz festgelegt .
      Woran liegt das ?

      • Das kann eigentlich nur zwei Gründe haben:

        1. triangleEffect wurde nicht geladen
        oder
        2. im TriangleEffect wurde der Parameter World nicht definiert

        Was sagt denn der Debugger: Ist triangleEffect gesetzt und wie sieht die Parameters-Auflistung aus?

      • Ähm also nein das ist kein World definiert… hab ich da was überlesen ?
        ich habe wie du gesagt hast den ganzen Code weggemacht und den von dir reingeschrieben …

      • Achso : lass mich raten : die Variablen am Anfang sollten dableiben ?

        Aber jetzt habe ich das Problem das er mir nichts anzeigt …
        Das Bild ist einfach blau.
        Hab jetzt zur Sicherheit den ganzen Quellcode kopiert aber er zeigt mir immernoch nichts an …

  2. ok, ich glaub ich hab kapiert warum x * z falsch ist.
    Angenommen ich hab bei den for Schleifen 2 verschiedene maximal Werte, welchen
    muss ich dann mit welcher Variable multiplizieren?

  3. Minecraftsuchti

    Kanns kaum erwarten bis der nächste Teil kommt xD

  4. stegameplays

    wann kommt der nächste TEIL???????
    BITTE HEUTE NOCH, ICH MACH EINEN MAP Editor

  5. Gavin Barnes

    Hey,

    super Tutorials, die du hier hast 🙂 Hab mich immer ein wenig vor 3D Programmierung gedrückt aber durch die Tutorials habe ich dann doch mal angefangen.

    Mach weiter so ich hoffe der nächste Teil erscheint bald 🙂

  6. derwarteraufneueterrain101folge

    Sehr gutes Tutorial

    siehe meinen
    Namen!!!! xD

  7. Er zeigt mir nichts an ! Woran liegt das ? Ich hab sogar nochmal zur Sicherheit den ganzen Code kopiert aber er zeigt mir nichts an ! Einfach das blaue Bild …

    • Schick mir bitte mal dein gesamtes Projekt als ZIP per EMail. Dann schaue ich mir das an und sag dir, was das Problem ist.

    • Das Problem ist schlicht und einfach die fehlende Transformation im Shader. Bitte lies dir nochmal den Teil Terrain 101: Transformationen durch. Dort sind die Erklärungen für die World-, View- und Projection-Matrix wichtig. Wenn du mit diesem Shader-Code den Code in deinem Projekt ersetzt, dann funktioniert es problemlos.

      • Wenn ich den Sourcecode von dieser Seite und den Sourcecode des Effectfiles der Transformationen-Seite nehme kommt bei mir auch nur ein blaues leeres Bild.

      • Ich habe gerade extra nochmal mit folgenden Schritten getestet:

        1. Neues Projekt in Visual Studio erzeugt
        2. Projekt auf HiDef umgestellt (wegen der 32Bit Indices)
        3. Source-Code aus diesem Teil kopiert und damit den gesamten Inhalt der Game-Klasse ersetzt
        4. Neues Effekt-File „Triangle.fx“ im Content-Projekt angelegt und den Inhalt aus dem Transformations-Teil eingefügt
        5. ChunkHeightmap.png in das Content-Projekt gepackt
        6. mit F5 gestartet

        und alles funktioniert wie erwartet.

        Kommen bei dir irgendwelche Fehlermeldungen oder ähnliches? Führe die Schritte am besten noch mal durch, vermutlich ist beim kopieren irgendein Fehler aufgetreten.

      • Habs nun nochmal wie Du von vorne ausgeführt. Gleiches Ergebnis, wenn ich den Sourcecode von der Transformation nutze sehe ich das Dreieck. Aber mit dem Source von dieser Seite nichts. Keine Fehlermeldung, einfach nur ein leeres blaues Fenster.
        Lass ich auch nur einen Schritt von Deinen beschriebenen weg erfolgt ja auch eine Fehlermeldung. Ich habs genauso gemacht wie Du beschrieben hast.
        Wie kann das sein daß sich das hier anders verhält ? Spricht ja nicht für die Programmiersprache. Da reiß ich mir doch auch gleich alle Haare raus, echt suckig wenn man eh schon kaum Plan von 3D hat und dann ein Anfänger Tut nicht funzt.

      • Wirklich eigenartig… Kopiere mal bitte den Sourcecode der Game-Klasse aus der Zusammenfassung am Ende des Artikels und ersetze damit den kompletten Code deiner Game-Klasse. Ist das Ergebnis dann immer noch das gleiche?

      • Auch wenn ich aus „Terrain 101: Neue Sichtweisen“ die Sourcecodes einsetze sehe ich nur ein leeres blaues Fenster. Kannst Du mir Dein Projekt mal als Email schicken ? Ich denke mal dabei kommt das gleiche raus. Ne Idee was das sein könnte wenn auch dann alles leer ist ?

      • Hiho,
        kann ich noch mit einer Antwort rechnen ?

      • Schick mir bitte einfach dein Projekt per EMail und ich schaue mal wo das Problem liegen könnte. Das Projekt das ich verwende ist mittlerweile bereits ein Stückchen weiter.

  8. hi, wie lange dauert es noch bis zur nächsten Folge? 😀

  9. Hi,
    ich wollte die Kamera auf den Punkt 0|0|0 setzten, aber wenn ich das tue sehe ich rein gar nichts mehr von der Map. ich muss viel höhere Werte eingeben bis ich was von der Map sehe. Wie kann ich die Mitte der Map auf den 0|0|0 Punkt legen. (Y- Achse halt an die Heightmap angepasst)

    • Wenn du die Kamera mit

      viewMatrix = Matrix.CreateLookAt(new Vector3(0.0f, 100.0f, 0.0f), new Vector3(640.0f, 0.0f, 640.0f), Vector3.Up);

      Positionierst, dann befindest du dich genau an einer Ecke der Landschaft und blickst zur Mitte des Chunks. Der Y-Parameter der Position (erster Vector3) gibt dabei die Höhe an. Bei meiner Heightmap führt dieser Paramter dazu, dass du knapp über dem Boden bist.

      • oh misst, ich habe die Argumente von Vector3 falsch angeordnet 😀 . Ich hatte x, z, y und nicht x, y, z .

  10. ist die richtige Größe des IndexBuffer dann nicht auch 127*127*6 ?

    • Vollkommen richtig, da hat sich ein kleiner Fehler eingeschlichen, gut aufgepasst.

      Ich werde das sofort korrigieren. Danke für die Meldung, dass zeigt mir, dass jemand aufmerksam meine Tutorials durchgeht 🙂

  11. Hey,
    vielen Dank erst mal für das tolle Tutorial ich hab gerade erst mit 3D angefangen und bin dadurch auf diese Seite gestoßen. Hab aber leider genau das gleiche Problem wie oben beschrieben wurde:

    Hab ein neues Projekt angelegt alles richtig Kopiert und hab trotzdem nur ein blaues Fenster. Das mit dem Dreieck anzeigen funktioniert aber Problemlos. Echt zum Verzweifeln…
    Gibts den schon ne Lösung für das Problem?

    • Freut mich, dass dir das Tutorial gefällt…

      Komisch, dass das Tutorial bei dir auch nicht funktioniert. Der andere, der dieses Problem hatte, hat mir sein Projekt geschickt und bei mir lief dies einwandfrei. Nach einigem hin und her ist aufgefallen, dass er wohl zunächst das Windows XP XNA-Paket unter Windows 7 installiert hat, was zu solchen Fehlern führen kann. Leider hat er sich irgendwann nicht mehr für weitere Tests gemeldet, so dass das Problem nicht abschliessen geklärt werden konnte.

      Magst du mir dein Projekt (ZIP vom kompletten Arbeitsmappen-Ordner) mal per EMail zusenden? Also das Projekt, dass bei dir lediglich einen blauen Bildschirm anzeigt. Ich würde das gerne bei mir testen und vielleicht finden wir beide ja die Lösung für das Problem.

      • Email ist versendet 🙂 Hoffe das Problem wird gelöst.

      • Kleine Zwischenmeldung: Bei mir funktioniert das Projekt ohne Änderungen. Lad doch bitte mal das „June 2010“ DirectX SDK herunter und installiere das. Im nächsten Schritt debuggen wir dann mit PIX mal und finden dann hoffentlich heraus, woran es liegen könnte.

      • DirectX neu installieren hilft auch nichts leider.
        Das mit dem PIX debuggen müsstest du mir bitte erklären da kenn ich mich nicht aus.

  12. Es scheint so, als hätte sich in diesem Artikel ein böser, kleiner Fehler eingeschlichen, der aber nur auf ATI-Grafikkarten zum tragen kommt. Da ich eine NVidia-Karte verwende, konnte ich diesen Fehler leider nicht sehen.

    Ich warte noch auf das Feedback von ExUs, ob dies tatsächlich die Problemlösung ist und wenn ich dies bestätigt bekomme, dann werde ich diesen Artikel natürlich sofort aktualisieren. Zusätzlich werde ich dann noch einen Artikel schreiben, wie man solche Fehler vermeiden kann und vor allem, wie man solche Fehler finden kann, ohne eine entsprechende Grafikkarte zu haben.

    • Der Fehler wurde in der Zwischenzeit identifiziert und behoben. Ich habe die entsprechenden Artikel korrigiert.

      Vielen Dank an ExUs für die Hilfe bei der Analyse dieses Fehlers.

  13. Code funktioniert bei mir leider nicht 😦

    Bekomme bei deinem Code immer:

    „Der Objektverweis wurde nicht auf eine Objektinstanz festgelegt“
    bei:


    triangleEffect.Parameters[„World“].SetValue(Matrix.Identity);
    triangleEffect.Parameters[„View“].SetValue(viewMatrix);
    triangleEffect.Parameters[„Projection“].SetValue(projectionMatrix);

    Zudem musste ich erst „graphics.GraphicsProfile = GraphicsProfile.HiDef;“
    benutzen, damit er nicht schon bei der 32Bit-Indices meckert.

    Trotzdem toll geschrieben 🙂

    • Danke sehr, freut micht, dass dir der Artikel gefällt 🙂

      Zum ersten Fehler: Das kann eigentlich nur daran liegen, dass entweder der triangleEffect nicht geladen wurde oder dass es ein Problem mit den Parametern World, View und/oder Projection gibt. Diese müssen im Effect-File in der exakten Schreibweise vorhanden sein, sonst können diese nicht gesetzt werden.

      Der Hinweis auf das HiDef-Profil ist im Artikel „Terrain 101: Vertex- und Index-Buffer“ versteckt. Ist eine Aufgabe für den Leser 😉

  14. Ach verdammt, das kommt davon wenn man denkt „Kapitel Transformation überspring‘ ich, kenn‘ ich ja schon“, und damit die Hälfte verpasst.

    Jetzt läuft alles. Top, danke dir 🙂

    Kurze Frage noch: Gibts irgendwo ne Liste welche Karten 32Bit Indizes unterstützen? Bzw. ab welchen baureihen Nv/ATi/Intel diese integriert sind?

    • Oh, irgendwie hatte ich die Frage überlesen, vermutlich weil ich vom Handy aus freigeschaltet hatte, sorry.

      Ich würde sagen ab Feature-Level 10.0 sollten 32-Bit Indices vorhanden sein. Näheres auch hier.

  15. Hallo,

    erstmal ein ganz großes Lob an diese Serie (und auch an den Blog) von Tutorials. Diese Reihe sucht wohl seines Gleichen im Internet 🙂

    Eine Frage habe ich jedoch.

    Zeile:
    GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 128 * 128 * 6, 0, (127 * 127 * 6) / 3);

    Parameter 4 (128 * 128 * 6) kann man willkürlich ändern, er muss nur größer 1 sein. Das Ergebnis verändert sich jedoch nicht. Ist mir ehrlich gesagt ein schleierhaftes Verhalten.

    Und warum genau (128 * 128 * 6)? und nicht 128*128 ? Heißt es nicht die Anzahl der Vertices? Oder nimmt man die Anzahl der Vertices vom Index?

    Eine kleine Erläuterung wäre sehr schön 🙂

    Vielen Dank
    Domi

    • Hallo und danke für das Lob.

      Zur Beantwortung deiner Frage möchte ich dich gerne ins Forum http://www.indiedev.de einladen. Das ist die neue Heimat für meine Tutorials und Artikel und dort gibt es neben mir noch viele weitere, kompetente Benutzer, die dir bei der Beantwortung deiner Fragen behilflich sein können.

  16. Eigentlich könnte man doch auch gleich die normalen in die heightmap packen? z.B der Alpha Kanal für die Höhe verwenden und RGB würde dann die Normale codieren. Gleich mal ausprobieren:-)

    • Ja, man könnte die Normalen direkt in die Heightmap kodieren. Dies macht aber oft keinen Sinn, weil man diese ja in unterschiedlichen LOD-Stufen neu berechnen muss. Ein weiterer Nachteil ist, daß die Heightmap mehr Speicher benötigt.

  1. Pingback: Terrain 101: Vertex- und Index-Buffer « "Mit ohne Haare"

  2. Pingback: Terrain 101: Transformationen « "Mit ohne Haare"

  3. Pingback: Terrain 101: Das erste Dreieck « "Mit ohne Haare"

  4. Pingback: Terrain 101: Technische Rafinessen « "Mit ohne Haare"

  5. Pingback: Terrain 101: Die Entwicklungsumgebung und das XNA 4.0 Projekt « "Mit ohne Haare"

  6. Pingback: Terrain 101: Eine Einführung in dreidimensionale Landschaften in Echtzeit « "Mit ohne Haare"

  7. Pingback: Terrain 101: Neue Sichtweisen « "Mit ohne Haare"

  8. Pingback: Terrain 101: Kamera ab und Action « "Mit ohne Haare"

  9. Pingback: Terrain 101: Chunked Heightmaps | cworx

  10. Pingback: Heightmap Hawaii 128 | cworx

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: