WBOIT is an order-independent transparency technique based on the paper Weighted Blended Order-Independent Transparency by McGuire and Bavoil (2013). It provides a single-pass, approximate transparency rendering method that does not require per-pixel sorting or linked lists.
This document describes the extension that adds WBOIT as an alternative to Hydra's default linked-list OIT: theory, usage, unit tests, limitations, and a comparison with the existing approach.
Traditional alpha blending requires rendering transparent objects in strict back-to-front order. OIT techniques remove this requirement. WBOIT approximates the correct blended result using weighted averages.
The WBOIT algorithm works in two passes:
Pass 1 -- Accumulation (WbOitRenderTask)
For each transparent fragment with color C_i, alpha a_i, and a depth-dependent weight
w_i:
- Accumulate into a vec4 buffer:
(C_i * w_i, a_i)using additive blending(One, One). - Accumulate into a float buffer:
a_i * w_iusing additive blending. - Alpha channel uses
(Zero, OneMinusSrcAlpha)to compute total transmittance.
The weight function is:
w(z, a) = a * max(0.01, 3000 * (1 - z)^3)
This is Equation 10 from the original paper. The depth term (1 - z)^3 gives higher weight
to fragments closer to the camera, biasing the result towards a roughly correct ordering.
Pass 2 -- Resolve (WbOitResolveTask)
A fullscreen pass reads both buffers and computes the final color:
opacity = 1 - accumColor.a
finalColor = vec4(accumColor.rgb / clamp(accumWeight, 1e-4, 5e4), opacity)
The resolved color is then alpha-blended over the opaque scene using:
- Color:
SrcAlpha, OneMinusSrcAlpha, Add - Alpha:
One, OneMinusSrcAlpha, Add
| File | Purpose |
|---|---|
include/hvt/tasks/wboitRenderTask.h |
Public header for WbOitRenderTask |
source/tasks/wboitRenderTask.cpp |
Accumulation pass implementation |
include/hvt/tasks/wboitResolveTask.h |
Public header for WbOitResolveTask |
source/tasks/wboitResolveTask.cpp |
Fullscreen resolve pass implementation |
include/hvt/resources/shaders/wboit.glslfx |
Render pass shader (accumulation) |
include/hvt/resources/shaders/wboitResolve.glslfx |
Resolve fragment shader |
-
WbOitRenderTask extends
PXR_NS::HdxRenderTask- Overrides
_Syncto configure blend state and disable MSAA. - Overrides
Prepareto create WBOIT-specific render buffers (Float16Vec4 for color accumulation, Float16 for weight accumulation) and set theoitRequestFlag. - Overrides
Executeto skip rendering when no translucent draw items exist. - Uses
HdStRenderPassShaderwith thewboit.glslfxto inject theRenderOutputfunction that computes weighted accumulation.
- Overrides
-
WbOitResolveTask extends
PXR_NS::HdxTask- Uses
HdxFullscreenShaderwithwboitResolve.glslfx. - Only executes when the
oitRequestFlagis present in the task context, ensuring no unnecessary GPU work when there are no translucent fragments.
- Uses
The TaskCreationOptions struct controls which OIT variant is used:
hvt::FramePassDescriptor passDesc;
passDesc.renderIndex = renderIndex;
passDesc.uid = SdfPath("/MyFramePass");
passDesc.taskCreationOptions.useWbOit = true; // Enable WBOIT
// CreateFramePass calls CreatePresetTasks internally.
auto framePass = hvt::ViewportEngine::CreateFramePass(passDesc);When useWbOit is true:
- The translucent render task uses
WbOitRenderTaskinstead ofHdxOitRenderTask. - The resolve task uses
WbOitResolveTaskinstead ofHdxOitResolveTask. - The volume render task falls back to a standard
HdxRenderTaskbecause volume rendering is not supported with WBOIT.
When useWbOit is false (default), the existing linked-list OIT pipeline is used unchanged.
The render pass shader (wboit.glslfx) is self-contained: it inlines the
HvtRenderPass.RenderWbOit layout and GLSL function, plus imports standard USD shaders for
camera, clip planes, and selection via $TOOLS/ prefixed paths.
The resolve shader (wboitResolve.glslfx) is a minimal fullscreen fragment shader that reads
the two accumulation buffers and computes the final weighted-average color.
Validation lives in test/tests/ — expect many tests per feature. Usage demonstration is
covered separately by the How-to below (one per feature).
Tests are located in test/tests/testWboitTask.cpp.
- construction: Verifies that the default
TaskCreationOptionsproduces linked-list OIT, and thatuseWbOit=trueproduces WBOIT tasks instead. - resolveTaskParamsVtValue: Validates VtValue requirements for
WbOitResolveTaskParams.
End-to-end integration example (not a substitute for the unit tests above):
test/howTos/howTo19_UseWBOITRenderTask.cpp demonstrates the complete workflow to enable WBOIT
in a frame pass. It renders the translucent_cube.usda test asset (overlapping opaque and
translucent rectangles) with WBOIT enabled, validated via image comparison.
-
Volume rendering: Not supported with WBOIT. When
useWbOitis enabled, the volume OIT task is replaced by a standard render task. Volume prims will render but without proper transparency compositing. -
MSAA (Multi-Sample Anti-Aliasing): WBOIT buffers are not multi-sampled. The render task explicitly disables MSAA (
SetMultiSampleEnabled(false)) for the accumulation pass. This means edges of transparent geometry will not benefit from hardware anti-aliasing. -
Accuracy: WBOIT is an approximation. The depth-based weight function can produce visible artifacts when:
- Multiple transparent surfaces are very close in depth.
- Transparency values vary widely across overlapping surfaces.
- Colored transparent surfaces overlap with similar depth values.
-
High alpha values: The weight function may lose precision for fragments with very high alpha values combined with near-equal depth, leading to color bleeding.
-
Shader path resolution: The render pass shader relies on USD's
$TOOLS/path resolution for standard imports (hdSt/shaders/renderPass.glslfx,hdx/shaders/renderPass.glslfx,hdx/shaders/selection.glslfx). This requires the USD resource paths to be properly configured at runtime.
| Aspect | Linked-List OIT | WBOIT |
|---|---|---|
| Technique | Per-pixel linked lists with depth sorting | Weighted blending (single-pass approximation) |
| Accuracy | Exact (sorts all fragments) | Approximate (depth-weighted average) |
| Memory | O(n) per pixel (linked list nodes via SSBOs) | O(1) per pixel (two fixed-size buffers) |
| GPU features | Requires atomic counters, SSBOs | Standard blending and render targets |
| Performance | Varies with overdraw and list length | Constant per-pixel cost |
| Volume support | Yes (HdxOitVolumeRenderTask) |
No |
| MSAA | Resolves before OIT pass | Not supported |
| WebGPU/Vulkan | May have driver compatibility issues | Simpler GPU requirements |
| Artifacts | None (exact ordering) | Possible with similar-depth overlapping surfaces |
- Scenes with moderate transparency where approximate results are acceptable.
- Platforms where linked-list OIT is unavailable or slow (e.g., WebGPU).
- When predictable, constant memory usage is preferred over per-scene variable allocation.
- Scenes requiring exact transparency ordering.
- Scenes with volumetric transparency.
- When visual accuracy is more important than performance predictability.