Two transparent panels intersect in an X through an opaque teapot — a case no per-object sort can ever draw correctly — alongside ordinary front/back panels. Keys 1/2/3 switch between naive alpha blending, per-object sorting, and weighted blended OIT (McGuire & Bavoil, JCGT 2013) to compare all three on the same scene. This is the MSc follow-on to the Blending demo.
There are two versions:
main.py— OpenGL, using an MRT FBO andglBlendFunci(a different blend function per attachment)OITWebGPU.py— WebGPU, where the per-attachment blends become per-targetblendstates on the pipeline
The scene and the OIT maths are defined once in oit_common.py — a numpy-only reference implementation of exactly what the shaders compute, unit tested in tests/ (including the key property: the composite is invariant under every permutation of fragment order).
| Key | Action |
|---|---|
1 |
naive alpha blend in scene order (wrong wherever order is wrong) |
2 |
per-object back-to-front sort (fixes the parallel panels; still wrong along the intersection of the X pair) |
3 |
weighted blended OIT — order independent, no sorting at all |
A / Z |
increase / decrease panel alpha |
| LMB / RMB / wheel | rotate / pan / zoom, Space resets, Esc quits |
The OVER operator is order dependent, but a sum and a product are not. Weighted blended OIT replaces the sorted composite with two commutative accumulators, rendered in three passes:
-
Opaque pass → opaque colour texture + depth texture.
-
Accumulation pass — all transparent geometry in any order, depth tested against (but never writing) the opaque depth, into two render targets with different blend functions:
accum(RGBA16F, blendONE, ONE): accumulatesweight(z, a) * vec4(rgb * a, a)— a weighted sum of premultiplied colourreveal(R16F, blendZERO, ONE_MINUS_SRC_COLOR): accumulatesΠ(1 - aᵢ)— the total transmittance
-
Composite pass — a full-screen triangle resolves the weighted average and lerps it over the opaque colour:
vec3 transparent = accum.rgb / max(accum.a, 1e-5); colour = opaque * reveal + transparent * (1.0 - reveal);
The depth-dependent weight makes near fragments dominate the average, approximating what a correct sort would have produced. It is an approximation — with very high alpha or extreme depth ranges it drifts from ground truth (try A to push alpha up) — but it is a single geometry pass, needs no sorting, and handles intersecting geometry that defeats sorting entirely.
uv run pytest OITransparency/tests- M. McGuire & L. Bavoil, "Weighted Blended Order-Independent Transparency", JCGT 2(2), 2013 — paper page — the technique behind mode
3. - Casual Effects — Weighted, Blended OIT — McGuire's implementation notes and weight-function variants.
- MJP — Weighted Blended OIT — analysis of where the approximation holds and breaks.
- T. Porter & T. Duff, "Compositing Digital Images", SIGGRAPH 1984 — ACM — the OVER operator whose order dependence motivates all of this.
