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

Squareys

Frischling

  • »Squareys« ist der Autor dieses Themas

Beiträge: 35

Wohnort: Konstanz

Beruf: VR Development Lead bei Vhite Rabbit.

  • Private Nachricht senden

1

27.09.2012, 10:27

Drawing Order bei Node basierter Graphikengine

Hey Leute,

ich würde gerne in dem Node basierten Graphikteil meiner Gameengine (hobbymäßig und noch nicht sonderlich weit entwickelt) "Drawing Order" implementieren.
Heißt, ich möchte anhand einer z-Koordinate die Reihenfolge festlegen können, in der die Nodes gezeichnet werden, bei meinen Nodes ist das im Augenblick wild durcheinander (Drawing Order eben).

Zeichnen funktioniert also im Augenblick folgendermaßen: Jede Node hat ein Parent und eine Liste Children und zeichnet die Children sobald die Node selbst gezeichnet wird. Die Graphikengine muss somit nur die Zecihenprozedur der Base aufrufen. Der Nachteil ist, dass die Graphikengine keinerlei Bewusstein der Nodes außer der Base besitzt. Die Nodes verwalten sich eigentlich selbst.

Mir selbst sind zwei Möglichkeiten eingefallen:
1.) OpenGL z-Buffer
2.) Vor jedem Frame eine Liste aus der Nodetree erstellen, sortieren, und diese zeichnen.

Nachteil von 1.) ist, dass Allegro 5, das ich praktisch für alles benutze, den z-Buffer nicht selbst unterstützt, ich müsste das also alles selbst implementieren (nicht das Problem), und dabei noch auf Allegros Blitting-Routinen, beispielsweise, verzichten.
Nachteil von 2.) ist ersichtlicherweise die Performance (vermute ich?).

Vielleicht hat irgendwer von euch eine ganz andere Lösung/Lösungsansatz, oder eine Idee,
wie man die Nachteile von 1.) oder 2.) umgehen kann? :S

Danke schonmal für eure Antworten :)

- Squareys

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

2

27.09.2012, 10:43

Z-Buffer funktioniert nicht mit Transparenz, von daher wird das wohl keine Lösung sein.
Es bleibt dir wohl nichts übrig als eine Liste aller zu zeichnenden Objekte anzufertigen und diese zu sortieren.
Solange du nicht zehntausende Objekte hast, wirst du das Sortieren kaum bemerken (was Geschwindigkeit angeht). Wenn du wirklich viele Objekte haben willst oder großen Wert auf Geschwindigkeit legst, kannst du die Liste über das Ende eines Frames hinaus behalten und Objekte neu einsortieren, wenn welche dazukommen oder sich ihr Z-Wert ändert, bzw. aus der Liste entfernen, wenn sie gelöscht werden. Am einfachsten wäre es aber, die Liste jedes Frame neu zu erzeugen und zu sortieren.

Schrompf

Alter Hase

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

3

27.09.2012, 11:54

Ich kenne Leute, die mit komplett statischen Listen von RenderJobs experimentiert haben, die sie dann nur nach Bedarf aktiviert oder deaktiviert haben. Das führt Dich in einem echten Spiel leider nicht sehr weit. Das komplette Gegenteil, nämlich jedes Frame alle RenderJobs neu zu sammeln und zu sortieren, geht dagegen ganz gut. Bei den Splitterwelten haben wir bis zu 5000 DrawCalls pro Frame, und das Sortieren dieser DrawCalls ist dabei unser kleinstes Problem. Du verbringst viel mehr Zeit in den Treiber-DLLs als mit Sortieren.

Eine Mischform könnte aber auch gehen: eine Liste von RenderJobs statisch zusammenstellen und nur bei sortier-relevanten Änderungen neu sortieren. Sichtbarkeit ist nicht sortier-relevant, Material-Änderung, neue oder verschwundene RenderJobs und sowas sind dagegen sortierwürdig. Dafür aber auch seltener.

Architektur: Lasse die Nodes sich nicht selbst zeichnen, sondern nur RenderJobs erzeugen, die sie im Draw()-Aufruf nur in die Liste einsortieren. Das ist unbedingte Voraussetzung für jede beliebige Sortierung. Was Du dann mit "Blitting"-Routinen meinst, weiß ich nicht... aber ich kann Dir sagen, dass die Verwendung des ZBuffers quasi keine Performance kostet, das Sortieren nach Tiefe aber schon. Sortiere lieber so, dass Du so wenig wie möglich Shader- und Textur-Wechsel hast, das bringt viel mehr.
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

4

27.09.2012, 12:02

Jup, Render-Jobs mit Priorität gibt's bei mir auch. Die werden erst alle gebuffert, sortiert und dann gerendert.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Squareys

Frischling

  • »Squareys« ist der Autor dieses Themas

Beiträge: 35

Wohnort: Konstanz

Beruf: VR Development Lead bei Vhite Rabbit.

  • Private Nachricht senden

5

27.09.2012, 15:59

Okay, danke, das hilft mir sehr!

Wie liessen sich RenderJobs umsetzen? Eine Klasse mit einer spezifischen Render Funktion, nur die Funktion, als Funktionspointer oder sowas?
Und wenn ich eine Liste hab, der ich RenderJobs hinzufügen möchte, wie wäre es am "saubersten", die für die Nodes zugänglich zu machen?
(in C++) Statischer Pointer, einfachen Pointer (im Konstruktor) übergeben oder sowas, global wohl weniger... übergeben in die Draw-Funktion?

@David: Das mit der Tranzparent war mir nicht bewusst, danke :)

@Schrompf: Meine GameEngine ist 2D, blitting ist das zeichnen eines Bitmaps auf den Bildschirm/anderes Bitmap. Shader und Texturwechsel hab ich daher also auch nicht, bzw, vmtl macht das Allegro für mich...

@BlueCobold: Mit Priorität heißt, dass zuerst gezeichnet werden?

Schrompf

Alter Hase

Beiträge: 1 470

Wohnort: Dresden

Beruf: Softwareentwickler

  • Private Nachricht senden

6

27.09.2012, 16:10

Dann kannst Du Dir doch jegliche Sortiererei sparen. Erfinde x Layer, wobei jeder Layer nur eine Liste von Blits ist. Jeder Node fügt jetzt einfach dem Layer seiner Wahl die Blits hinzu, und Du renderst dann die Layer einen nach dem anderen aus.

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
struct RenderJob
{
  size_t theImageToPaint;
  Vector2D position;
  float rotation;
  float scale;
  Color4D color;
  // und was Du sonst so brauchst, um eine einzelne Grafik auf den Schirm zu kriegen

  /// Standard-Konstruktor... notwendig, damit std::vector das Ding aufnehmen kann
  RenderJob() { }
  /// Konstruktor aus Parametern... kannst Du Dir ersparen, wenn Du einen Compiler mit guter C++11-Unterstützung nutzt
  RenderJob( size_t image, const Vector2D& pos, const Color4D& col = 0xffffffff, float rot = 0.0f, float scal = 1.0f);
};

const size_t MaxLayerCount = 8;

struct RenderProcess
{
  std::vector<RenderJob> layer[MaxLayerCount];
};

// und mehr ist es nicht
class MyExample : public Node
{
  // als Beispiel
  void Draw( RenderProcess& process) { process.layer[3].push_back( RenderJob( myImage, myPosition, myCurrentRotation));
}
};


Mehr ist es nicht. Das reicht, um ganz simpel zu bestimmen, dass bestimmte Grafiken immer über andere Grafiken drübergepinselt werden. Und dafür braucht man auch nicht so viele Layer... es reicht im Grunde: BodenTiles, Flecken, Leute, Partikel, WandTiles, Spieler

[edit] Code-Beispiel saubergemacht.

[edit] Es empfiehlt sich wohl, die Layer-Anzahl genauso wie jeden Layer in ein Enum zu verpacken. Damit kannst Du später weitere Layer hinzufügen, ohne die Layer-Indizes in jeder Draw()-Funktion händisch anpassen zu müssen.

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
 enum LayerIndex 
{
  Layer_GroundTiles,
  Layer_GroundAdditional,
  Layer_WallTiles,
  Layer_MaxCount ///< immer das letzte Element: gibt die Anzahl Layer an
};

struct RenderProcess
{
  std::vector<RenderJob> layers[Layer_MaxCount];
};


Bye, Thomas
Häuptling von Dreamworlds. Baut aktuell an nichts konkretem, weil das Vollzeitangestelltenverhältnis ihn fest im Griff hat. Baut daneben nur noch sehr selten an der Open Asset Import Library mit.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Schrompf« (27.09.2012, 16:15)


Squareys

Frischling

  • »Squareys« ist der Autor dieses Themas

Beiträge: 35

Wohnort: Konstanz

Beruf: VR Development Lead bei Vhite Rabbit.

  • Private Nachricht senden

7

27.09.2012, 16:20

Ah, okay, verstehe! Bei mir sind die GraphicNodes ziemlich das selbe wie hier die RenderJobs.
Das mit dem RenderProcess ist allerdings eine klasse Idee!
Danke vielmals! :)

Werbeanzeige