Die Bestimmung der sichtbaren Flächen in einer Szene erfolgt normalerweise nach dem Z-Buffer Verfahren (auch Depth Buffer, Tiefenpuffer). Dabei ist die Zeichenreihenfolge der Objekte für das Ergebnis unerheblich. Allerdings funktioniert das Z-Buffer Verfahren nur mit opaken Flächen und nicht mit transparenten Flächen. Transparente, sich überdeckende Flächen müssen sortiert von hinten nach vorne gezeichnet werden, da die Zeichenreihenfolge einen entscheidenden Einfluss auf die resultierende Mischfarbe hat.(1)
Daher ist es oft sinnvoll, zuerst die opaken Geometrien mit Z-Buffer zu zeichnen und danach die transparenten Geometrien, wobei aber nur mehr lesend auf den Z-Buffer zugegriffen wird. Auch für Overlays, HUDs und verschiedene Effekte ist die Zeichenreihenfolge wichtig.
Dieses Tutorial beschreibt in welcher Abfolge das Rendering genau passiert und welche Möglichkeiten es gibt, um die Zeichenreihenfolge zu beeinflussen.
- Zu beachten
- (1) Es gibt auch Verfahren um transparente Flächen unabhängig von der Zeichenreihenfolge zu zeichnen - siehe z.B. Weighted Blended Order-Independent Transparency.
Render Hierarchie
In folgenden Ebenen kann Einfluss auf die Zeichenreihenfolge des Renderers genommen werden:
- Framebuffer/Backbuffer
- View
- Camera
- Layer
- Pass
- SortOrder/Distance/DepthOrder
Framebuffer/Backbuffer
Zunächst werden alle Graph::FrameBuffer
in der Reihenfolge, in der sie im Szenegraphen definiert sind, gerendert. Als letztes wird der Backbuffer gerendert.
View
Für jeden Framebuffer/Backbuffer kann es einen oder mehrere Graph::View
-Objekte geben.
Ein View definiert einen Bildausschnitt bzw. eine Region im Fenster (Integer-Koordinaten, Pixel). Der View hat immer die gleiche Größe wie der zugehörige Frambebuffer/Backbuffer. Eine view mask kann dazu verwendet werden, um den Zeichenbereich auf einen kleineren, rechteckigen Bereich einzuschränken ("Scissor Test").
Die Zeichenreihenfolge kann mit dem Attribut depthOrder
festgelegt werden. Bei gleicher depthOrder
bestimmt die Definitionsreihenfolge auch die Zeichenreihenfolge. Eine höherer depthOrder
Wert bewirkt ein späterers rendern d.h. der View wird, innerhalb des Framebuffers, über den anderen Views mit kleineren depthOrder
Werten gezeichnet.
Camera
Für jeden View kann es eine oder mehrere Graph::Camera
-Objekte geben.
Ein Graph::Camera
-Objekt definiert einen sichtbaren Bereich in der virtuellen Welt (Real-Koordinaten).
Die Zeichenreihenfolge kann mit dem Attribut depthOrder
festgelegt werden. Bei gleicher depthOrder
bestimmt die Definitionsreihenfolge auch die Zeichenreihenfolge. Eine höherer depthOrder
Wert bewirkt ein späterers rendern d.h. die Kamera wird, innerhalb des Views, über den anderen Kameras mit kleineren depthOrder
Werten gezeichnet.
Layer
Jede Kamera rendert ihre Objekte (zugeordnet über Kind-Knoten oder über Graph::CameraState
).
Auf Wunsch kann das Rendern schichtweise mit mehreren Layern erfolgen. Standardmäßig wird alles in den Layer 0 gerendert. Mit einem Graph::LayerState
-Knoten und dem Attribut index
kann auf einen anderen Layer umgeschaltet werden.
- Zu beachten
- Achtung! Aus Ressourcengründen sollten Layer immer fortlaufend, beginnend mit 0, benützt werden (also z.B. 0, 1, 2, 3 und nicht 0, 12, 20, 30).
Die Zeichenreihenfolge der Layer wird über den Layer-Index festgelegt, wobei höhere Werte später gerendert werden.
Pass
Das Rendern eines Layers erfolgt in zwei Durchgängen (engl. passes), abhängig vom Graph::Material
und dem Attribut objectSortMode
.
Im ersten Durchgang (Pass 0) werden alle Objekte gerendert, bei denen das Graph::Material
einen objectSortMode
von OBJECT_SORT_MODE_BY_MATERIAL
hat.
Im zweiten Durchgang (Pass 1) werden alle Objekte gerendert, bei denen das Graph::Material
einen objectSortMode
von OBJECT_SORT_MODE_BY_DEPTH
hat.
Wird das Attribut objectSortMode
nicht explizit angegeben, bestimmt der Z-Buffer Mode die Zuordnung:
- Materialien die den Z-Buffer schreiben (
DEPTH_BUFFER_MODE_WRITE_ONLY
oderDEPTH_BUFFER_MODE_READ_AND_WRITE
) verwendenOBJECT_SORT_MODE_BY_MATERIAL
(Pass 0). - Alle anderen Materialien verwenden
OBJECT_SORT_MODE_BY_DEPTH
(Pass 1).
SortOrder, Distanz, DepthOrder
Im Pass 0 bestimmt das Attribut sortOrder
des Materials
die Zeichenreihenfolge.
Im Pass 1 erfolgt die Sortierung nach:
sortOrder
Attribut desMaterials
.- Tiefensortierung (Bei gleicher
sortOder
, Sortierung von hinten nach vorn). depthOrder
Attribut derGeometrie
(bei gleichersortorder
und Distanz, Sortierung überdepthOrder
Attribut).
Über das Graph::Camera
-Attribut depthSortMode
kann die Methode der Tiefensortierung festgelegt werden (z.B. keine Sortierung, Z-Value, Distanz - siehe IEnums::DepthSortMode
).
- Zu beachten
- Das
depthOrder
Attribut des Eltern-Knoten wird an Kind-Knoten weitergegeben. DiedepthOrder
des Kind-Knoten entspricht dann der Summe ausdepthOrder
des Eltern-Knotens und der angegebenendepthOrder
des Kind-Knotens. Soll ein Kind-Knoten hinter einem Eltern-Knoten gerendert werden, so kann beim Kind-Knoten ein negativerdepthOrder
Wert verwendet werden.
Automatische Gruppierung
Objekte, bei denen die Zeichenreihenfolge in einem Pass nicht eindeutig bestimmt ist,
werden vom Renderer nach folgenden Kriterien zusammengefasst:
- Shader-Program-Switch-Minimierung
- Textur-Switch-Minimierung
- Gruppierung bei gleicher Beleuchtung
Das Zusammenfassen erfolgt automatisch, kann von außen nicht beeinflusst werden und ist nicht deterministisch.
Beispiel
Als einfaches Beispiel zeigen wir die Verwendung mehrerer Views und Kameras und zeichnen einige Objekte mit unterschiedlichen Materialien.
Wir verwenden eine background_camera
um den Bildschirm und den Z-Buffer zu löschen, sowie eine main_camera
zum Zeichnen des Hintergrunds.
- Zu beachten
- Die
background_camera
hat keine Kind-Knoten und muss daher mittelsCameraState
aktiviert werden. Anderenfalls würde die Kamera vom Renderer übersprungen werden.
<View id="main_view" leftMaskCoord="0" topMaskCoord="0" rightMaskCoord="0" bottomMaskCoord="0" /> <Camera id="background_camera" viewId="main_view" projectionType="ORTHOGRAPHIC" unitSizeX="1" unitSizeY="1" colorClearValue="000000aah" clearColorBuffer="true" clearDepthBuffer="true" depthOrder="0" /> <CameraState cameraId="background_camera" /> <Camera id="main_camera" viewId="main_view" projectionType="ORTHOGRAPHIC" unitSizeX="1" unitSizeY="1" clearColorBuffer="no" clearDepthBuffer="no" depthOrder="10" /> <CameraTransform cameraId="main_camera" posX="0" posY="0" posZ="512" /> <CameraState cameraId="main_camera" />
Die main_camera
verwenden wir um eine Textur großflächig anzuzeigen.
<Instance graphResourceId="package_main:graph_camera"/> <PlaneGeometry materialSlot="4" textureSlot="1" scaleFactorX="10000" scaleFactorY="1080"/>
Zusätzlich definieren wir einen view_left
und eine perspektivische Kamera camera_left
. Den Graph::View
schränken wir mit dem Attribut rightMaskCoord
auf die linke Bildschirmhälfte ein. Das Zentrum der Graph::Camera
verschieben wir ebenfalls in die Mitte der linken Hälfte.
<?xml version="1.0" ?> <Graph> <View id="view_left" leftMaskCoord="0" topMaskCoord="0" rightMaskCoord="-640" bottomMaskCoord="0" /> <Camera id="camera_left" centerX="-0.5" viewId="view_left" fieldOfViewX="2" clearColorBuffer="no" clearDepthBuffer="no" depthOrder="2" /> <CameraTransform cameraId="camera_left" posX="0" posZ="600" /> <CameraState cameraId="camera_left" /> </Graph>
Für die rechte Bildschirmhälfte definieren wir einen view_right
und eine orthographische Kamera camera_right
. Auch hier schränken wird den Graph::View
ein und verschieben das Zentrum der Graph::Camera
.
<?xml version="1.0" ?> <Graph> <View id="view_right" leftMaskCoord="640" topMaskCoord="0" rightMaskCoord="0" bottomMaskCoord="0" /> <Camera id="camera_right" projectionType="ORTHOGRAPHIC" centerX="0.5" viewId="view_right" unitSizeX="1" unitSizeY="1" clearColorBuffer="no" clearDepthBuffer="no" depthOrder="2" /> <Transform angleX="-90d"> <CameraTransform cameraId="camera_right" posZ="500" /> </Transform> <CameraState cameraId="camera_right" /> </Graph>
Mit den beiden Kameras rendern wir die gleiche Szene in unterschiedlichen Ansichten.
<Instance graphResourceId="package_main:graph_camera_left"/> <Reference targetId="myScene"/> <Instance graphResourceId="package_main:graph_camera_right"/> <Reference targetId="myScene"/>
Als Ergebnis erhalten wir ein Bild, das mit vier Kameras gerendert wurde.
background_camera
löscht den Fensterinhaltmain_camera
füllt den Hintergrund mit einem blauen Bildcamera_left
rendert die Szene in einer perspektivischen Ansichtcamera_right
rendert die Szene orthographisch aus der Vogelperspektive.
Damit die opaken und transparenten Flächen richtig gerendert werden, wird für opakes Material depthBufferMode="READ_AND_WRITE"
und für transparentes Material depthBufferMode="READ_ONLY"
verwendet.