# How Scene Layer Configurations Control Rendering Order and Compositing in Pyrite64

> Discover how Pyrite64 scene layer configurations like depthCompare, depthWrite, blender, and fog control rendering order and compositing for stunning visuals. Optimize your effects now.

- Repository: [Max Bebök/pyrite64](https://github.com/hailtododongo/pyrite64)
- Tags: deep-dive
- Published: 2026-02-19

---

**Scene layer configurations in Pyrite64 determine rendering order through sequential layer processing, where depthCompare enables Z-testing, depthWrite controls occlusion, blender handles color mixing, and fog applies distance-based atmospheric effects.**

Pyrite64 renders scenes using a layer-based architecture defined in `LayerConf` structures. Each scene layer configuration controls GPU pipeline state through specific flags that dictate visibility, depth handling, and color blending. Understanding how these parameters interact is essential for achieving correct transparency, occlusion, and atmospheric effects in Nintendo 64-style rendering.

## Understanding Scene Layer Configuration

In Pyrite64, scenes are composed of ordered layer lists stored in `SceneConf`. The configuration for each layer resides in [`src/project/scene/scene.h`](https://github.com/HailToDodongo/pyrite64/blob/main/src/project/scene/scene.h) as the `LayerConf` struct, which defines the rendering parameters consumed by the runtime.

The key members controlling the rendering pipeline include:

- **depthCompare**: Enables Z-testing (SDL_GPU_COMPAREOP) to discard fragments behind existing geometry
- **depthWrite**: Determines whether passing fragments update the Z-buffer
- **blender**: Index referencing an `RDPQ_BLENDER_*` mode for fragment color mixing
- **fog**: Boolean toggle for distance-based atmospheric calculations
- **fogColorMode**, **fogColor**, **fogMin**, **fogMax**: Parameters defining fog range and color

The vectors `layers3D`, `layersPtx`, and `layers2D` inside `SceneConf` establish the **rendering order**, with layers processing sequentially in array order.

## Translating Configuration to GPU State

During the build process, [`src/build/sceneBuilder.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/src/build/sceneBuilder.cpp) serializes layer flags into a compact binary format for the runtime. The depth flags are packed into bitfields:

```cpp
// src/build/sceneBuilder.cpp
if (layer.depthWrite.value)   flags |= (1 << 0);  // Bit 0: depth write enable
if (layer.depthCompare.value) flags |= (1 << 1);  // Bit 1: depth compare enable
ctx.fileScene.write<uint32_t>(flags);

```

The blending mode index is written directly as a `uint32_t`. At runtime, [`src/renderer/n64/n64Material.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/src/renderer/n64/n64Material.cpp) interprets these packed flags to configure the GPU pipeline state, enabling or disabling depth testing and write operations per draw call.

Shader parameters pass through the `UboMaterial` uniform block defined in `src/shader/ubo.glsl`, making the blender index and fog settings available to the fragment shader.

## Impact on Rendering Order and Compositing

The interaction between depth handling, blending, and fog determines how layers composite into the final frame.

### Depth Compare and Depth Write

When **depthCompare** is enabled, the GPU performs Z-testing against the existing depth buffer. Fragments failing this test are discarded immediately.

**depthWrite** controls occlusion behavior for subsequent layers:

- **Enabled**: Passing fragments write their depth value, blocking later geometry behind them
- **Disabled**: The layer draws without updating the Z-buffer, allowing later layers to render "through" it regardless of actual depth

This mechanism enables proper **back-to-front ordering** for opaque objects while supporting transparent overlays that don't occlude background geometry.

### Blender Modes

The **blender** parameter selects a mixing operation executed in the fragment shader after depth testing. Available modes (prefixed `RDPQ_BLENDER_*`) include alpha blending, additive, and multiplicative operations.

Blending occurs **only after** the depth test passes, meaning invisible fragments never contribute color to the render target. This sequential processing allows later transparent layers to composite over existing pixel data without disturbing the depth buffer when depthWrite remains disabled.

### Fog Integration

When **fog** is enabled, the fragment shader calculates atmospheric attenuation based on the fragment's depth relative to `fogMin` and `fogMax` distances. The calculation applies after depth handling and blending, mixing the fragment color toward `fogColor` according to `fogColorMode`.

Fog operates independently of blend modes but relies on accurate depth values from previous layers to calculate proper distance attenuation.

## Practical Layer Configuration Examples

### Opaque 3D World Layer

For standard solid geometry, enable full depth handling with no blending:

```json
{
  "layers3D": [
    {
      "name": "WorldGeometry",
      "depthCompare": true,
      "depthWrite": true,
      "blender": 0,
      "fog": false
    }
  ]
}

```

This configuration ensures objects automatically occlude based on distance, with later layers respecting the depth buffer written by this pass.

### Transparent UI Overlay

Interface elements typically require blending without depth interference:

```json
{
  "layers2D": [
    {
      "name": "UserInterface",
      "depthCompare": false,
      "depthWrite": false,
      "blender": 2,
      "fog": false
    }
  ]
}

```

Disabling both depth operations ensures UI elements render on top regardless of 3D scene depth, while blender mode 2 (typically alpha blend) composites translucent elements.

### Atmospheric Fog Layer

Background environments often use fog for depth cueing:

```json
{
  "layers3D": [
    {
      "name": "Background",
      "depthCompare": true,
      "depthWrite": true,
      "blender": 0,
      "fog": true,
      "fogColorMode": 0,
      "fogColor": [0.5, 0.5, 0.6, 1.0],
      "fogMin": 20.0,
      "fogMax": 100.0
    }
  ]
}

```

This applies linear fog fading to the specified color between 20 and 100 units distance, while maintaining proper occlusion for overlapping objects.

## Key Source Files

Understanding the implementation requires examining these specific components:

- **[`src/project/scene/scene.h`](https://github.com/HailToDodongo/pyrite64/blob/main/src/project/scene/scene.h)** — Defines the `LayerConf` structure containing depth, blender, and fog parameters
- **[`src/build/sceneBuilder.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/src/build/sceneBuilder.cpp)** — Serializes layer configurations into binary scene files with packed depth flags
- **[`src/renderer/n64/n64Material.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/src/renderer/n64/n64Material.cpp)** — Maps packed flags to GPU pipeline state for depth testing and writing
- **`src/shader/ubo.glsl`** — Declares the `UboMaterial` uniform block transporting blender indices and fog data to shaders
- **`src/shader/n64.frag.glsl`** — Implements blending operations and fog calculations in the fragment pipeline

## Summary

- **Scene layer configurations** in Pyrite64 are processed sequentially through `layers3D`, `layersPtx`, and `layers2D` vectors
- **depthCompare** enables Z-testing to cull occluded fragments before shading
- **depthWrite** determines whether a layer blocks subsequent geometry by updating the depth buffer
- **blender** indices select color mixing operations applied after depth testing but before final output
- **fog** parameters calculate atmospheric attenuation based on fragment depth, applied after blending operations
- Disabling depthWrite while enabling depthCompare creates transparent overlays that test against but don't modify the Z-buffer

## Frequently Asked Questions

### How does Pyrite64 handle transparent objects with scene layer configurations?

Transparent objects should use **depthCompare enabled** with **depthWrite disabled**. This configuration allows the Z-test to discard fragments behind opaque geometry while preventing the transparent layer from blocking subsequent draws. Set the **blender** parameter to an alpha-enabled mode (such as `RDPQ_BLENDER_ALPHA`) to composite translucent colors over the existing frame buffer.

### What happens if depthCompare is disabled for a 3D layer?

Disabling **depthCompare** forces the renderer to draw all fragments regardless of Z-buffer content. In [`src/renderer/n64/n64Material.cpp`](https://github.com/HailToDodongo/pyrite64/blob/main/src/renderer/n64/n64Material.cpp), this disables the GPU depth test, causing later layers to overwrite previous geometry based purely on draw order rather than spatial position. This is useful for full-screen effects or UI elements but generally produces incorrect occlusion for 3D scenes.

### Can fog be applied to specific objects rather than entire layers?

No, according to the `LayerConf` definition in [`src/project/scene/scene.h`](https://github.com/HailToDodongo/pyrite64/blob/main/src/project/scene/scene.h), the **fog** boolean applies to entire layers uniformly. The fog calculation in `src/shader/n64.frag.glsl` processes all fragments in a layer when enabled. To apply fog selectively, you must separate objects into distinct layers with different fog configurations.

### Where does the blender mode index get interpreted in the shader?

The **blender** index passes to the GPU through the `UboMaterial` uniform block defined in `src/shader/ubo.glsl`. The fragment shader in `src/shader/n64.frag.glsl` uses this index to select the mixing operation via `blender_fetch`, which determines how the incoming fragment color combines with the existing render target color.