Tutorial #14: Render Order

Visible surface determination is usually done with the Z-buffer technique (also known as depth buffering). In this case the drawing order of the objects is irrelevant for the render result. However, the Z-buffer technique only works with opaque faces but not with transparent faces. Transparent, overlapping faces have to be drawn in the correct order sorted from back to front. This is because the drawing order has a significant impact to the final compound color.(1)

Thus it is often wise to render the opaque geometries at first with Z-buffering enabled and the transparent geometries afterwards accessing the Z-buffer in read-only mode. Also for overlays, HUDs and various effects the render order often plays a vital role.

This tutorial describes the exact sequence in which the rendering occurs and shows ways to control the render order.

Note
(1) Also other techniques exist which allow to draw transparent faces order independent - e.g. Weighted Blended Order-Independent Transparency.

Render Hierarchy

The drawing order of the renderer can be controlled in several hierarchy levels:

  1. Framebuffer/Backbuffer
  2. View
  3. Camera
  4. Layer
  5. Pass
  6. SortOrder/Distance/DepthOrder

Framebuffer/Backbuffer

At first all Framebuffers are rendered in the order in which they are defined in the scene graph. At last the back buffer is rendered.

View

For each frame buffer / back buffer one or more Graph::View objects can exist.

A view specifies a display window or a region in the window (integer coordinates, pixels). The view has always the same size as the corresponding frame buffer / back buffer. A view mask can be used to restrict the drawing area to a smaller, rectangular region ("scissor test").

The render order can be defined with the attribute depthOrder. If the depthOrder value is the same for two views, the order in the scene graph defines the actual render order. A higher depthOrder value results in the view being rendered later, i.e. on top of all other views in the same frame buffer with lower depthOrder values.

Camera

For each view one or more Graph::Camera objects can exist

A Graph::Camera object specifies a visible area within the virtual world (floating point coordinates).

The render order can be defined with the attribute depthOrder. If the depthOrder value is the same for two cameras, the order in the scene graph defines the actual render order. A higher depthOrder value results in the camera being rendered later, i.e. on top of all other cameras within the same view with lower depthOrder values.

Layer

Each camera renders its objects (assigned by child nodes or with the Graph::CameraState node).

The rendering can be split up into several layers. By default, all geometries are rendered to layer 0. A Graph::LayerState node with the attribute index can be used to switch to a different layer.

Note
Attention! In order to save resources, layers should always be used consecutively, starting with 0 (e.g. 0, 1, 2, 3 and not 0, 12, 20, 30).

The render order is defined by the layer index, higher values are rendered later.

Pass

The rendering of each layer is done in two passes, depending on the Graph::Material and the objectSortMode attribute.

In the first pass (pass 0) all objects are rendered, where the objectSortMode of the Graph::Material is equal to OBJECT_SORT_MODE_BY_MATERIAL.

In the second pass (pass 1) all objects are rendered, where the objectSortMode of the Graph::Material is equal to OBJECT_SORT_MODE_BY_DEPTH.

If the attribute objectSortMode is not explicitly specified, the depthBufferMode defines the mapping:

  • Materials that write the depth buffer (DEPTH_BUFFER_MODE_WRITE_ONLY or DEPTH_BUFFER_MODE_READ_AND_WRITE) use OBJECT_SORT_MODE_BY_MATERIAL (Pass 0).
  • All other materials use OBJECT_SORT_MODE_BY_DEPTH (Pass 1).

SortOrder, Distance, DepthOrder

In pass 0 the attribute sortOrder of the material defines the render order.

In pass 1 the render order is defined by:

  1. sorOrder attribute of the material.
  2. Distance (if the sortOrder is equal, sort from back to front).
  3. depthOrder attribute of the geometry (if the sortOrder and distance is equal, sort by the depthOrder attribute).

The method to determine the distance can be specified with the depthSortMode attribute of the Graph::Camera (e.g. no depth sorting, z-value, distance – see IEnums::DepthSortMode).

Note
The depthOrder attribute of the parent node is passed to the child node. The resulting depthOrder of the child node is the sum of the depthOrder of the parent node and the specified depthOrder of the child node. If a child node should be rendered behind a parent node, a negative depthOrder value can be used for the child node.

Automatic Grouping

Objects, for which the render order is undefined within one pass, are grouped together according to the following criteria:

  • Minimize shader program switches.
  • Minimize texture switches.
  • Group objects with same lighting.

The grouping is done automatically and is not deterministic.

Example

As a simple example we show how to use multiple views and cameras and draw several objects with different materials.

We use a background_camera to clear the display and the depth buffer and a main_camera to draw the background.

Note
The background_camera must be activated using a CameraState node because it does not contain any child node. Otherwise the camera would be skipped by the renderer.
<View id="main_view"
    leftMaskCoord="0"
    topMaskCoord="0"
    rightMaskCoord="0"
    bottomMaskCoord="0"
/>

<OrthographicCamera id="background_camera"
    viewId="main_view"
    unitSizeX="1"
    unitSizeY="1"
    colorClearValue="000000aah"
    clearColorBuffer="true"
    clearDepthBuffer="true"
    depthOrder="0"
/>
<CameraState
    cameraId="background_camera"
/>

<OrthographicCamera id="main_camera"
    viewId="main_view"
    unitSizeX="1"
    unitSizeY="1"
    clearColorBuffer="no"
    clearDepthBuffer="no"
    depthOrder="10"
/>

<CameraTransform
    cameraId="main_camera"
    posX="0" posY="0" posZ="512"
/>

<CameraState
    cameraId="main_camera"
/>

The main_camera is used to draw a texture.

    <Instance graphResourceId="package_main:graph_camera"/>
    <PlaneGeometry materialSlot="4" textureSlot="1" scaleFactorX="10000" scaleFactorY="1080"/>

Additionally we define a view_left and a perspective camera camera_left. The Graph::View is restricted to the left half of the screen with the attribute rightMaskCoord. The center of the Graph::Camera is also moved to the center of the left half.

<?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>

For the right half of the screen we define a view_right and an orthographic camera camera_right. We again restrict the Graph::View and move the center of the Graph::Camera.

<?xml version="1.0" ?>

<Graph>

    <View id="view_right"
        leftMaskCoord="640"
        topMaskCoord="0"
        rightMaskCoord="0"
        bottomMaskCoord="0"
    />

    <OrthographicCamera id="camera_right"
        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>

The two cameras render the same scene in different views.

<Instance graphResourceId="package_main:graph_camera_left"/>
    <Reference targetId="myScene"/>

<Instance graphResourceId="package_main:graph_camera_right"/>
    <Reference targetId="myScene"/>

We get an image that is rendered with four cameras:

  • background_camera clears the window.
  • main_camera uses a blue image to draw the background.
  • camera_left renders the scene with a perspective camera.
  • camera_right renders the scene with an orthographic camera in a bird's eye view.

To get a correct render result for the opaque and transparent faces we use depthBufferMode="READ_AND_WRITE" for the opaque material and depthBufferMode="READ_ONLY" for the transparent material.

tut0114_render_order.png
Render Order


Copyright © 2011-2024 Spraylight GmbH.