ArmorPaint Path Tracing vs Forward Rendering Comparison: Engine Architecture and Pipeline Differences
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 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, 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:
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 (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 provides deterministic, real-time feedback through single-pass rasterization. The entry point render_path_forward_draw_forward() (lines 6‑19) executes the following sequence:
- Renders the G‑buffer (
gbuffer1) and sky dome. - Applies the
compositor_passshader for scene composition. - Draws UI overlays, compass, and environment sphere.
- 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 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 (lines 11‑15):
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 (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:
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) 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). 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:
/* 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:
/* 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:
/* 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 ong_config->viewport_mode. - Forward Rendering: Implements immediate-mode rasterization in
render_path_forward.cwith single-pass G‑buffer composition and TAA. - Path Tracing: Utilizes
render_path_raytrace.cto 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 invokes render_path_raytrace_draw(); otherwise, it executes the standard draw_commands() forward path. The UI in 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, each frame increments render_path_raytrace_frame until reaching g_config->pathtrace_frames, meaning the image converges over time rather than displaying instantaneously.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →