Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

CeDoMain

Alter Hase

  • »CeDoMain« ist der Autor dieses Themas

Beiträge: 587

Wohnort: Ilmenau

Beruf: Student für Mechatronik

  • Private Nachricht senden

1

17.05.2014, 20:33

Geometry Instancing mit D3D9 und der TriBase Engine

Hey Leute,

weil mir hier: std::vector in std::vector einfügen - geht das ohne schleife? von iSmokiieZz empfohlen wurde Geometry Instancing zu verwenden, habe ich mich mal genauer über diese Art des Renderns informiert! Nun habe ich versucht, mir Quellcode auszudenken - dabei hatte ich ein paar Probleme. Ich schildere mal...:

Ich habe ein Spiel programmiert, das eine Welt aus Blöcken rendert, indem es jeden einzelnen Block durchgeht (NICHT jeden Frame - falls wer fragt :D) und überprüft, ob dieser ein "Luft"-Block ist und an einen "nicht-Luft"-Block grenzt. Ist dem so, wird an dieser Grenze ein Quadrat generiert, mit der Textur des "nicht-Luft"-Block, das dann mit absoluten Koordinaten (für Position und Textur - weil ein Texture Atlas verwendet wird) in einen Index- und Vertexbuffer eingefügt wird. Weil die Map fast nur aus Kuben und einigen wenigen "nicht"-Kuben besteht, fand ich die Idee mit dem Geometry Instancing sehr gut!

Weil ich mit David's TriBase-Engine Arbeite, habe ich mir die Sache für D3D9 durchgelesen: http://msdn.microsoft.com/en-us/library/…v=vs.85%29.aspx
Mein Problem ist jetzt, dass in diesem Beispiel die Vertexbuffer anders als in der TriBase-Engine angelegt werden - ist ja auch klar! Kann ich mir auch zwei Vertex Buffer mit diesen Vertextypen erstellen, so dass diese dann mit GI funktionieren?:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
struct GeometryVertex
{
    tbVector3 Position;
    tbVector3 Normal;
    tbVector2 Texture;
    static const DWORD VF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;
};
struct InstanceVertex
{
    tbMatrix Position;
    tbMatrix Texture;
};

Ist das mit dem Instancevertex so richtig? Oder was gebe ich da als Vertexformat an? Kann ich einfach die Matrix für die Normalenvektoren weglassen (weil die brauchen nicht verändert werden)? Und was haben die von MSDN mit den Zahlen 0, 12, 24 ... (0, 16, 32 ...) an zweiter und letzter Position in der Deklaration gemeint???

Ich habe das gesamte Forum durchsucht und leider keine D3D9 betreffenden Ergebnisse gefunden :(. Ich hoffe ich habe nichts übersehen und ihr könnt mir helfen!

PS: Müsste ich 6 verschiedene Geometrybuffer erstellen, mit den jeweils 6 Seiten eines Würfels, wenn ich NUR die sichtbare Seite zeichnen will? Oder kann ich auch mehrere "Geometries" in einen Buffer packen und dann im Instancebuffer angeben welche der Seiten genommen werden soll? Wenn möglich würde ich dabei auf HLSL verzichten - das kann ich nicht ganz so gut :D!
Mit freundlichem Gruß
CeDo
Discord: #6996 | Skype: cedomain

Lass solche persönlichen Angriffe lieber bleiben, meine sind härter.

2

17.05.2014, 21:27

Deine instance vtx struct sollte so funktionieren.

Du musst eben die richtigen vtx declarations festlegen, also ein Array aus D3DVERTEXELEMENT9.
Diese Zahlen, die du angesprochen hast sind der Byte offset, der letztendlich die Größe des vorhergehenden Elements spezifiziert.
Weiterführende Informationen findest du hier: http://msdn.microsoft.com/en-us/library/…0(v=vs.85).aspx

Ich vermute, dass du ohne hlsl nicht sehr weit kommen wirst mit geometry instancing ;)

Du kannst zum Beispiel die 6 Seiten in den eine geometry buffer hauen. Bei den paar vertices dürfte es nicht viel ausmachen, wenn du keine bounds festlegst. Dann nutzt du die d3d9 API Methoden, die auch im msdn tutorial beschrieben wurden. Also setstreamsourcefreq.
Hier hast du ein deutlich besseres tutorial:
http://http.developer.nvidia.com/GPUGems…_chapter03.html
EnvisionGame(); EnableGame(); AchieveGame(); - Visionen kann man viele haben. Sie umzusetzen und auf das Ergebnis stolz zu sein ist die eigentliche Kunst.

CeDoMain

Alter Hase

  • »CeDoMain« ist der Autor dieses Themas

Beiträge: 587

Wohnort: Ilmenau

Beruf: Student für Mechatronik

  • Private Nachricht senden

3

18.05.2014, 21:20

Deine instance vtx struct sollte so funktionieren.

Du musst eben die richtigen vtx declarations festlegen, also ein Array aus D3DVERTEXELEMENT9.
Das ist für mich jetzt ein bisschen gegensätzlich! Kann ich jetzt mein Struct als Typangabe verwenden oder muss ich mir dafür ein neues aus D3DVERTEXELEMENT9 basteln? Wenn ich das bei deinem ersten Link richtig verstanden habe, dann hat das hier:

C-/C++-Quelltext

1
static const DWORD VF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;
die gleiche Bedeutung wie ein D3DVERTEXELEMENT9-Array!?

Wie bekommt D3D eigentlich mit, welchen Vertextyp die beiden Arrays haben? Bisher habe ich das mit SetFVF() gemacht, das dann D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1 übergeben bekommen hat. Das könnte ich mit dem Geometryarray so machen, aber vom Instancearray habe ich keinen FVF-Bezeichner. Welches Array ist bei GI eigentlich mit SetFVF() gemeint?

Wenn ich meine 6 Seiten aber hintereinander in einen Vertex Buffer packen würde, würden doch für jede Instance alle 6 Seiten gezeichnet. Das sind meistens 5 zuviel! Ich will ja immer nur eine Seite von nem Würfel zeichnen - je nach dem welche an einen "Luft"-Block grenzt. Damit will ich Vertices sparen! Oder ist das nicht nötig?
Mit freundlichem Gruß
CeDo
Discord: #6996 | Skype: cedomain

Lass solche persönlichen Angriffe lieber bleiben, meine sind härter.

4

18.05.2014, 22:18

Zitat

Kann ich jetzt mein Struct als Typangabe verwenden oder muss ich mir dafür ein neues aus D3DVERTEXELEMENT9 basteln?


Du brauchst beides. Du musst einmal deine Daten adressieren können durch eine Strukt und andererseits DX beibringen, wie diese Struktur aussieht.

Zitat

dann hat das hier [...]die gleiche Bedeutung wie ein D3DVERTEXELEMENT9-Array!?

Nein.

Zitat

Wie bekommt D3D eigentlich mit, welchen Vertextyp die beiden Arrays haben?

Fixed Pipeline: SetFVF()
Programmable Pipeline: SetVertexDeclaration()
http://msdn.microsoft.com/en-us/library/…4(v=vs.85).aspx

Wenn du mehrere Vertex Streams verwendest hast du in der D3DVERTEXELEMENT9 Struktur ein Attribut "Stream". Damit kannst du den jeweiligen Stream "anzapfen".

Zitat

Wenn ich meine 6 Seiten aber hintereinander in einen Vertex Buffer packen würde, würden doch für jede Instance alle 6 Seiten gezeichnet.

Für solch penibles Occlusion Culling musst du kompliziertere Techniken einsetzen.
Scene Partitioning kann dir helfen. Außerdem kannst du bei DrawPrimitive Grenzen angeben.

Im DirectX SDK ist übrigens ein sehr ausführliches Beispiel mit unterschiedlichen Methoden. ;)
EnvisionGame(); EnableGame(); AchieveGame(); - Visionen kann man viele haben. Sie umzusetzen und auf das Ergebnis stolz zu sein ist die eigentliche Kunst.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »iSmokiieZz« (19.05.2014, 00:09)


CeDoMain

Alter Hase

  • »CeDoMain« ist der Autor dieses Themas

Beiträge: 587

Wohnort: Ilmenau

Beruf: Student für Mechatronik

  • Private Nachricht senden

5

19.05.2014, 21:32

Danke für die Erklärung! Habe ich das richtig verstanden, dass bei GI keine fixed Pipeline benutzt wird und deshalb nicht SetFVF() benutzt werden kann!? Und deshalb mache ich das mit SetVertexDeclaration() und D3DVERTEXELEMENT9, weil ich die Programmable Pipeline verwende!?

Für solch penibles Occlusion Culling musst du kompliziertere Techniken einsetzen.
Scene Partitioning kann dir helfen. Außerdem kannst du bei DrawPrimitive Grenzen angeben.

Im DirectX SDK ist übrigens ein sehr ausführliches Beispiel mit unterschiedlichen Methoden. ;)
Du meinst, ich soll erst mal die kompletten Würfel zeichnen (natürlich mit normalen Culling - das funktioniert doch hoffentlich mit der Programmable Pipeline :D ) Über die DrawPimitive-Methode werde ich mich mal bei MSDN informieren... ^^

Habe vielen Dank für diese ausgezeichnete Hilfe!!!
Mit freundlichem Gruß
CeDo
Discord: #6996 | Skype: cedomain

Lass solche persönlichen Angriffe lieber bleiben, meine sind härter.

6

21.05.2014, 22:31

Zitat

Du meinst, ich soll erst mal die kompletten Würfel zeichnen (natürlich mit normalen Culling - das funktioniert doch hoffentlich mit der Programmable Pipeline

Ja, ob Du shaders benutzt oder nicht spielt keine Rolle.

Und ja, iSmokiieZz hat recht, Du kommst nicht um die vertex declarations und einen speziellen vertex shader rum. Für erstere ist die Funktion D3DXDeclaratorFromFVF hilfreich, die Dir aus FVF eine declaration bastelt.

Ein Kommentar hierzu:

Quellcode

1
2
3
4
5
struct InstanceVertex
{
    tbMatrix Position;
    tbMatrix Texture;
};

Das sind komplette 4x4 Matrizen, oder ? Überleg Dir gut, ob das wirklich nötig ist. Erstens brauchts für Instanzen meist nur affine Abbildungen (keine perspektivische Projektion, sondern nur eine sogenannte World-Transformation, dazu reicht eine 3x4 Matrix). Beispielsweise, wenn die Würfel alle gleich gross sind und nicht rotiert werden reicht schon ein einzelner Offset-Vektor (eine Translation, 3 floats). Das gleiche gilt für den Atlas. Sonst gehen Dir nämlich die semantics (*) aus (Kombination aus Usage und UsageIndex aus D3DVERTEXELEMENT9. Meist muss man da TEXCOORD benutzen und je nach shader model gehen die nur von 0 bis 7 oder 8, falls ich mich richtig erinnere)

(*) Achtung, diese MSDN-Seite ist ein Wust aus D3D9, 10 und 11. Sehr verwirrend.

Zusätzliche Warnung: Da Hardware Instancing für HLSL-Neulinge nicht gerade zu empfehlen ist, wäre vielleicht Dein anderer Ansatz doch nicht so schlecht: Setze die Würfelseiten manuell zusammen, (Chunk-weise, d.h. z.B ein Abschnitt aus 16x16x16 Würfeln oder so). Dann kannst Du auch dieses fein-Culling vornehmen. Ich glaube Minecraft und ähnliche Spiele/Engines machen das auch so.

CeDoMain

Alter Hase

  • »CeDoMain« ist der Autor dieses Themas

Beiträge: 587

Wohnort: Ilmenau

Beruf: Student für Mechatronik

  • Private Nachricht senden

7

22.05.2014, 16:46

speziellen vertex shader

Muss ich einen selber programmieren? Oder macht D3D9 schon für mich?

Bin gerade dabei das Programm umzuschreiben ... wie kann ich das machen, wenn ich später andere Geometries einbaue und die dann auch mit Geometry Instancing gerendert werden sollen? Kann ich im Instancevertex auch angeben, welches Modell er nehmen soll? Oder eine Position im Geometrybuffer, wo das Modell zu finden ist?
Mit freundlichem Gruß
CeDo
Discord: #6996 | Skype: cedomain

Lass solche persönlichen Angriffe lieber bleiben, meine sind härter.

8

22.05.2014, 18:35

Zitat

Muss ich einen selber programmieren? Oder macht D3D9 schon für mich?

Nein, musst Du selber schreiben. Vielleicht ist die Engine, die Du benutzt hilfreich, die kenn ich aber nicht. Kuck Dir das Beispiel aus dem SDK an (Instancing), es geht um die Routine OnRenderHWInstancing (in Instancing.cpp) und den vertex shader (in Instancing.fx) VS_HWInstancing. Der Wert vBoxInstance bestimmt Position und Rotation um die Y-Achse.

Hmmmm, bin zwar nicht so glücklich wird dort die COLOR-semantic benutzt (die hat nämlich wenig Auflösung und nur den Wertebereich 0..1). Wie gesagt, üblicherweise benutzt man eine TEXCOORD.

Zitat

Bin gerade dabei das Programm umzuschreiben ... wie kann ich das machen, wenn ich später andere Geometries einbaue und die dann auch mit Geometry Instancing gerendert werden sollen? Kann ich im Instancevertex auch angeben, welches Modell er nehmen soll? Oder eine Position im Geometrybuffer, wo das Modell zu finden ist?

Nein, sowas geht (leider) nicht. Du setzt einen anderen Geometrybuffer, oder aber benutzt sogenannte subsets, d.h. man spielt mit den Parametern von DrawIndexedPrimitive um nur gewisse Teile des Geometrybuffers auszuwählen. Letzteres ist aber noch kniffliger (s. hier, Szenario 3 oder 4).

In beiden Fällen sind das dann separate DrawIndexedPrimitive-Aufrufe (und vermutlich willst Du dazwischen auch den Instanzpuffer ändern, willst ja nicht verschiedene Objekte am gleichen Platz haben ;) )

CeDoMain

Alter Hase

  • »CeDoMain« ist der Autor dieses Themas

Beiträge: 587

Wohnort: Ilmenau

Beruf: Student für Mechatronik

  • Private Nachricht senden

9

22.05.2014, 21:41

Könnte man sich nicht einen Shader programmieren, der aus dem Instance Vertex die Position im Geometrybuffer herausfiltert und dann das "richtige" Modell zeichnet? Ist mir erstamal egal, wie schwierig das würde... ;)

Macht GI eigentlich noch Sinn, wenn es später noch andere Blocktypen gibt? Das würde nämlich einen großen Aufwand bedeuten, die Treppen oder Gräser etc... auch noch mit GI zu rendern - ist's da nicht einfacher bei meiner Methode einfache Vertex- / Indexbuffer zu füllen zu bleiben? Ist zwar ein sch... Aufwand das jetzt alles wieder zu ändern (ja ich weiß ich hätte 'ne Sicherung machen sollen :D ) ... was meint ihr? Oder könntet ihr empfehlen für normale Blöcke und Wasser GI zu verwenden und den Rest wie vorher zu rendern!?
Mit freundlichem Gruß
CeDo
Discord: #6996 | Skype: cedomain

Lass solche persönlichen Angriffe lieber bleiben, meine sind härter.

10

22.05.2014, 22:01

Zitat

Könnte man sich nicht einen Shader programmieren, der aus dem Instance Vertex die Position im Geometrybuffer herausfiltert und dann das "richtige" Modell zeichnet? Ist mir erstamal egal, wie schwierig das würde... ;)
Nein, das geht leider nicht. Die GPU muss beim Ausführen des kompilierten Shader Codes bereits dessen Input kennen und beschreiben können. Somit ist es unmöglich, innerhalb des Shaders noch auf veränderliche Buffer zuzugreifen. ;)


Wenn du noch keine Lust hast, das zu implementieren, empfehle ich dir, dass du erstmal deine Blöcke mit der "Standard"-Methode renderst und dich eher auf die anderen Grundfunktionalitäten deines Spiels (Entities, Kampf, Input, Animationen, Sound, ...) konzentrierst, damit du schnell zu einem Prototypen kommst und dementsprechend auch schnell ein Ergebnis deiner Arbeit siehst.

Wenn du dann auf Geometry Instancing umsteigen willst und bei ersterem Schritt ein gutes Framework gebaut hast, sollte sich der Update-Aufwand auch in Grenzen halten.
Um das Geometry Instancing dann noch zu verfeinern, also Blockdistributionen mit einzuplanen, um die Performance noch weiter zu steigern, fallen mir jetzt verschiedene Möglichkeiten spontan ein:
  • von einer festen Blockdistribution von Anfang an ausgehen und dann entsprechend nur häufig vorkommende Blöcke rendern. (z.B.: Gras- und Steinblöcke kommen in der Welt vermutlich häufiger vor, als Fenster oder Treppen)
  • die Blockstribution an bestimmten zeitlichen Stellen (also nicht jeden Frame) neu berechnen und entsprechend nur häufig vorkommende Blöcke rendern (Stehst du z.B. in einem Holzhaus, wirst du deutlich mehr Holz und Treppen sehen, als Gras und Stein)
  • für Wasser und andere dynamische Blöcke definitiv kein Geometry Instancing nutzen, da sich deren Geometrie ändert und die Geometry Buffer möglichst statisch sein sollten, um CPU Zugriffe darauf zu vermeiden oder zumindest minimieren.
EnvisionGame(); EnableGame(); AchieveGame(); - Visionen kann man viele haben. Sie umzusetzen und auf das Ergebnis stolz zu sein ist die eigentliche Kunst.

Werbeanzeige