real-time & offline rendering
Gaussian Renderer
featuredCPU software rasterizer that splats 2D Gaussians into an HDR buffer and composites them with the over operator. Phase 1 of a planned progression toward GPU and WebGPU splatting.

A renderer built from scratch to understand Gaussian splatting one layer at a time, starting at the bottom. Phase 1 is pure CPU: evaluate a 2D Gaussian falloff per pixel, accumulate colour and alpha into separate HDR buffers, and composite to display range only at write time. No GPU, no shaders, no dependencies — just the maths and a PPM file. Getting the compositing right by hand is the point; the GPU and WebGPU phases build on the same splat function.
techniques
- Per-pixel Gaussian falloff — exp(-0.5 · r² / σ²) evaluated against each splat centre
- Separate colour and alpha accumulation buffers so alpha stays unbounded through the pipeline
- Over-compositing operator applied at write time, not baked into the splat
- HDR values clamped to display range only at the final unsigned-char cast
renders & comparisons

Before — premultiplied write
σ = 100, α = 0.5 (original buggy params)
pColor = color × alpha written as RGB → centre appears dim and washed out. Index bug accumAlpha[i×w+i] corrupts the alpha buffer.

After — composite at write time
σ = 100, α = 0.5 (fixed)
Straight colour stored, alpha kept separate. writePPM composites colour × alpha over black. Index bug fixed.

Eye-candy — HDR clamp core
σ = 80, α = 25
α = 25 drives G/B to 1.25 → clamping creates a white core. A red halo emerges as R stays saturated further out than G/B.
gallery

phase 1 bug fixes · main.cpp
// Bug 1 — splat(): straight colour, no premultiplication
- pColor = gauss.color * pAlpha;
+ pColor = gauss.color;
// Bug 2 — index typo
- accumAlpha[i*width + i] += pixelAlpha;
+ accumAlpha[j*width + i] = pixelAlpha;
// Bug 3 — writePPM: HDR alpha through, clamp only at uchar cast
- float a = clamp(alphaBuf[i], 0, 1); // killed HDR intent
+ float a = alphaBuf[i];
// Bug 4 — writePPM: file-open error check
+ if (!f) { cerr << "cannot open " << filename; return; }
// Bug 5 — multi-Gaussian: additive accumulation (renderGaussians)
+ for (const auto& g : gaussians)
+ accumRGB[j*W + i] += pColor * float3(pAlpha); // was = (overwrite)roadmap
- doneSoftware rasterizer — single Gaussian on the CPU
- doneExtended software rasterizer — multi-Gaussian scene, over operator
- nextGPU rasterizer — splatting via shaders
- nextWebGPU rasterizer — real-time in the browser
- nextBonus — radiance field rendering on the CPU
more in real-time & offline rendering