# ArmorPaint Path Tracing vs Forward Rendering Comparison: Engine Architecture and Pipeline Differences

> Discover ArmorPaint path tracing vs forward rendering. Explore their engine architecture and pipeline differences, from scene representation to sample accumulation.

- Repository: [Armory 3D/armorpaint](https://github.com/armory3d/armorpaint)
- Tags: comparison
- Published: 2026-09-11

---

**ArmorPaint uses a dynamic render-path dispatcher to switch between real-time forward rasterization and progressive GPU path tracing, with both pipelines sharing a common compositor but diverging in sample accumulation and scene representation.**

The armory3d/armorpaint repository implements two distinct viewport rendering architectures within its unified engine core. Understanding how these pipelines diverge—starting from the mode selection logic in [`render_path_base.c`](https://github.com/armory3d/armorpaint/blob/main/render_path_base.c) through the final image composition—enables developers to optimize their texturing workflow based on fidelity requirements and interactivity needs.

## How ArmorPaint Selects the Render Path

The engine determines which pipeline to execute through a centralized dispatcher that evaluates the current viewport configuration. The selection logic resides in [`paint/sources/render/render_path_base.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/render/render_path_base.c), specifically within the `render_path_base_commands()` function (lines 31‑40).

When the user selects **Path Trace** from the viewport UI, the engine sets `g_config->viewport_mode` to `VIEWPORT_MODE_PATH_TRACE`. The dispatcher then routes execution accordingly:

```c
if (g_context->viewport_mode == VIEWPORT_MODE_PATH_TRACE && 
    g_context->tool != TOOL_TYPE_COLORID) {
    render_path_raytrace_draw(...);
} else {
    draw_commands();  // Forward render path
}

```

This conditional branch ensures mutual exclusivity between the two pipelines. The forward path invokes `render_path_forward_commands()`, while the path-trace path triggers `render_path_raytrace_draw()`. The UI control that updates this state lives in [`paint/sources/ui/box_preferences.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/ui/box_preferences.c) (lines 24‑26), where a combo box modifies `g_context->viewport_mode` and flags the context dirty to force immediate pipeline reinitialization.

## Forward Rendering Pipeline

The **forward rendering** implementation in [`paint/sources/render/render_path_forward.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/render/render_path_forward.c) provides deterministic, real-time feedback through single-pass rasterization. The entry point `render_path_forward_draw_forward()` (lines 6‑19) executes the following sequence:

1. Renders the G‑buffer (`gbuffer1`) and sky dome.
2. Applies the `compositor_pass` shader for scene composition.
3. Draws UI overlays, compass, and environment sphere.
4. Executes Temporal Anti-Aliasing (TAA) via `render_path_base_draw_taa()`.

Because this pipeline performs all shading in a **single rasterization pass** without frame-to-frame accumulation, each frame represents a final, independent image. This architecture prioritizes **low latency and immediate visual feedback**, making it ideal for interactive painting and mesh editing where cursor responsiveness is critical.

## Path Tracing Pipeline

The **path tracing** pipeline in [`paint/sources/render/render_path_raytrace.c`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/render/render_path_raytrace.c) replaces rasterization with GPU-accelerated compute shaders that simulate physically accurate light transport. Unlike the forward path, this system progressively refines the image across multiple frames.

### Path Trace Configuration Modes

The engine exposes four quality presets defined in [`paint/sources/enums.h`](https://github.com/armory3d/armorpaint/blob/main/paint/sources/enums.h) (lines 11‑15):

```c
typedef enum {
    PATHTRACE_MODE_FAST          = 0,
    PATHTRACE_MODE_QUALITY       = 1,
    PATHTRACE_MODE_MULTI_FAST    = 2,
    PATHTRACE_MODE_MULTI_QUALITY = 3,
} pathtrace_mode_t;

```

These constants control the trade-off between convergence speed and render fidelity. The UI implementation in [`box_preferences.c`](https://github.com/armory3d/armorpaint/blob/main/box_preferences.c) (lines 27‑34) binds these values to `g_config->pathtrace_mode`, allowing runtime switching between fast previews and production-quality accumulation.

### Sample Accumulation and Progressive Refinement

The core accumulation logic checks the frame counter against the user-defined sample limit:

```c
if (render_path_raytrace_frame < g_config->pathtrace_frames) {
    _gpu_raytrace_dispatch_rays(...);
    render_path_raytrace_frame++;
}

```

This loop dispatches compute-shader ray-trace operations until `render_path_raytrace_frame` reaches `g_config->pathtrace_frames`. The resulting image converges toward a noise-free, physically accurate representation as samples accumulate. The UI slider controlling `pathtrace_frames` (lines 43‑50 in [`box_preferences.c`](https://github.com/armory3d/armorpaint/blob/main/box_preferences.c)) determines the maximum sample count per viewport refresh.

### Multi-Object vs Single-Object Rendering

When **Multi** modes are selected (detected via `config_is_raytrace_multi()`), the tracer constructs acceleration structures for **all paint objects** in the scene (lines 62‑70 of [`render_path_raytrace.c`](https://github.com/armory3d/armorpaint/blob/main/render_path_raytrace.c)). In standard mode, the system traces only the active mesh to conserve GPU memory and compute resources. This architectural distinction determines whether the viewport renders isolated material previews or final scene compositions with environmental lighting interactions.

## Architectural Comparison

| Aspect | Forward Rendering | Path Tracing |
|--------|------------------|--------------|
| **Execution Model** | Single rasterization pass per frame | Compute-shader ray dispatch with progressive accumulation |
| **Sample Handling** | Independent frames (no accumulation) | Accumulates up to `g_config->pathtrace_frames` |
| **Quality Control** | Fixed real-time output | Configurable (Fast/Quality/Multi-Fast/Multi-Quality) |
| **Scene Complexity** | G‑buffer handles entire viewport | Acceleration structures built per object(s) |
| **Interaction Latency** | Immediate feedback | Progressive (several frames to converge) |
| **Optimal Use Case** | Interactive painting and editing | High-fidelity previews and final renders |

After the ray-trace pass completes, both pipelines converge on the same compositor (`Scene/compositor_pass/compositor_pass`) to apply bloom, tone-mapping, and color grading, ensuring visual consistency across modes.

## Code Examples

### Switching to Path Tracing Mode

To programmatically enable path tracing with quality settings:

```c
/* Activate path-trace viewport */
g_config->viewport_mode = VIEWPORT_MODE_PATH_TRACE;

/* Configure quality and sample count */
g_config->pathtrace_mode = PATHTRACE_MODE_QUALITY;
g_config->pathtrace_frames = 64;  // Progressive samples

/* Force pipeline reinitialization */
g_context->ddirty = 2;

```

### Configuring Multi-Object Rendering

For scenes requiring inter-object light bouncing:

```c
/* Enable multi-object fast preview */
g_config->pathtrace_mode = PATHTRACE_MODE_MULTI_FAST;
g_config->pathtrace_frames = 32;  // Fewer samples for responsiveness

/* Trigger acceleration structure rebuild */
g_context->ddirty = 2;

```

### Returning to Forward Rendering

When reverting to real-time mode via UI interaction:

```c
/* UI combo updates the mode */
ui_combo(&g_config->viewport_mode, mode_combo, 
         tr("Default Mode"), ...);

if (ui_item_changed()) {
    render_path_raytrace_ready = false;      // Clean ray-trace state
    render_path_raytrace_init_shader = true; // Reset shader bindings
    g_context->ddirty = 2;                   // Trigger forward path init
}

```

## Summary

- **Pipeline Selection**: The engine uses `render_path_base_commands()` to route between forward rasterization and path tracing based on `g_config->viewport_mode`.
- **Forward Rendering**: Implements immediate-mode rasterization in [`render_path_forward.c`](https://github.com/armory3d/armorpaint/blob/main/render_path_forward.c) with single-pass G‑buffer composition and TAA.
- **Path Tracing**: Utilizes [`render_path_raytrace.c`](https://github.com/armory3d/armorpaint/blob/main/render_path_raytrace.c) to dispatch compute shaders that accumulate samples across frames, supporting four quality modes.
- **Multi-Object Support**: Path tracing optionally builds acceleration structures for all scene objects when `PATHTRACE_MODE_MULTI_*` constants are selected.
- **Integration**: Both pipelines share the final compositor, ensuring consistent post-processing regardless of render method.

## Frequently Asked Questions

### How does ArmorPaint switch between forward rendering and path tracing?

The engine stores the active mode in `g_config->viewport_mode`. When this value equals `VIEWPORT_MODE_PATH_TRACE`, the dispatcher in [`render_path_base.c`](https://github.com/armory3d/armorpaint/blob/main/render_path_base.c) invokes `render_path_raytrace_draw()`; otherwise, it executes the standard `draw_commands()` forward path. The UI in [`box_preferences.c`](https://github.com/armory3d/armorpaint/blob/main/box_preferences.c) updates this configuration and sets `g_context->ddirty` to trigger immediate pipeline reinitialization.

### What determines the quality of path-traced renders in ArmorPaint?

Quality is controlled by the `pathtrace_mode_t` enumeration and the `pathtrace_frames` counter. The four presets (Fast, Quality, Multi-Fast, Multi-Quality) adjust acceleration structure scope and sampling strategy, while `pathtrace_frames` caps the maximum number of accumulated samples per viewport session. Higher frame counts reduce noise but increase convergence time.

### Can path tracing handle multiple objects simultaneously?

Yes, when using `PATHTRACE_MODE_MULTI_FAST` or `PATHTRACE_MODE_MULTI_QUALITY`, the function `config_is_raytrace_multi()` returns true, causing the engine to build acceleration structures for all paint objects rather than just the active mesh. This enables accurate inter-object shadowing and reflection at the cost of increased GPU memory and computation.

### Why does path tracing feel slower than forward rendering?

Path tracing relies on **compute-shader ray dispatch** and **progressive sample accumulation** across multiple frames, whereas forward rendering completes in a **single rasterization pass**. According to the source code in [`render_path_raytrace.c`](https://github.com/armory3d/armorpaint/blob/main/render_path_raytrace.c), each frame increments `render_path_raytrace_frame` until reaching `g_config->pathtrace_frames`, meaning the image converges over time rather than displaying instantaneously.