Ray Marching: How Volume Shaders Sample Space in WebGL
Learn ray marching for volume shaders in WebGL. Covers rays, step size, density sampling, early exit, GLSL loops, and the FPS tradeoffs in browser rendering.
What you'll learn
- How a camera ray becomes a repeated sampling loop in GLSL.
- Why step count and step size dominate volume shader cost.
- How density accumulation turns invisible samples into visible color.
- Why early exit can preserve appearance while reducing GPU work.
- Which ray marching settings matter most when comparing browser benchmark results.
Quick definition
Ray marching is a rendering method that advances a ray through space in small steps, samples a function at each point, and accumulates a visible result. In volume shaders, ray marching estimates density, color, and opacity across fog, clouds, smoke, or procedural fields.
How does ray marching work in a volume shader?
Ray marching starts with a camera origin and a ray direction. The shader advances along that ray in small steps, samples a density or distance function, and accumulates a result. If the sample says the ray is inside fog or cloud material, the pixel becomes more opaque or brighter.
The loop count, step size, and resolution determine how much work the GPU performs per frame. A 96-step loop at full screen is not one loop; it is one loop per pixel, repeated every frame. That is why a small-looking GLSL fragment can become a serious GPU workload.
In a benchmark, the useful property is repeatability. If the same shader, viewport, and complexity level run on two devices, changes in FPS and frame time are easier to attribute to GPU and browser behavior.
float t = 0.0;
float density = 0.0;
for (int i = 0; i < 96; i++) {
vec3 p = ro + rd * t;
float sampleValue = mapDensity(p);
density += sampleValue * 0.04;
t += 0.03;
if (density > 1.0 || t > 6.0) break;
}What ray marching settings change performance?
More steps usually improve detail but reduce FPS. Smaller steps can reveal smoother fog or fractals, but they also multiply shader work. Higher render resolution has a similar effect because the same loop runs across more pixels.
Early exit matters. If the ray has already accumulated enough density, stopping the loop saves work without changing the visible result much. Dense smoke can often terminate early, while transparent fog may need more samples to look stable.
Noise functions, lighting, shadows, and normal estimation can be more expensive than the marching loop itself. A benchmark preset should therefore describe the whole shader workload, not just the step count.
Why does ray marching make a good browser stress test?
Ray marching puts sustained pressure on fragment-shader math, texture-like sampling patterns, and fill rate. It can make performance differences visible without loading a large scene or requiring a native benchmark installer.
The downside is scope. A ray marching benchmark does not fully represent a game engine, video encoder, compute workload, or native API path. It answers a narrower question: how well does this browser and device handle this shader-heavy visual workload?
That narrower question is still useful when you need a quick sanity check, a mobile browser test, or a way to compare thermal drift during a sustained graphics load.
Key takeaways
- 01 Ray marching is simple to describe but expensive because every pixel can run the loop.
- 02 More steps usually improve detail, but they multiply shader work and reduce FPS.
- 03 Early exit is one of the most important optimization tools for dense volumes.
- 04 Benchmark comparisons should keep step count, render size, preset, and browser fixed.