Ray marching
Ray marching is a rendering algorithm in 3D computer graphics that estimates intersections between rays and implicit surfaces by iteratively advancing the ray in discrete steps, sampling a distance function to determine safe step sizes and detect surface crossings without explicit geometric representations.[1] This approach is particularly suited for scenes defined by signed distance functions (SDFs), where the function value at a point indicates the shortest distance to the nearest surface, enabling efficient traversal that skips empty space and converges to intersections.[2] A foundational variant, known as sphere tracing, was introduced by John C. Hart in 1996 as a geometric method for antialiased ray tracing of implicit surfaces, marching rays using bounded distances to avoid penetration and support robust rendering of complex, non-smooth geometries.[3]
Unlike traditional ray tracing, which solves intersection equations analytically for polygonal or parametric surfaces, ray marching relies on numerical iteration, making it ideal for procedural, volumetric, or amorphous objects where closed-form solutions are unavailable or inefficient.[1] It excels in GPU-accelerated real-time applications, such as shader-based procedural generation in games and visual effects, by representing entire scenes compactly within fragment shaders.[4] Key advantages include simplicity in modeling organic or fractal-like structures via mathematical functions and the ability to handle deformations or constructive solid geometry (CSG) operations directly on SDFs.[5]
In modern contexts, ray marching has evolved with neural representations, where deep learning models approximate SDFs for high-fidelity novel view synthesis and reconstruction from images, often combined with volumetric rendering techniques to optimize for photorealism and efficiency.[6] Applications span volume rendering for medical imaging and scientific visualization to immersive graphics in virtual reality, though challenges like step count limitations and aliasing persist, addressed through adaptive stepping or hybrid methods.[7] Overall, ray marching democratizes advanced rendering by leveraging implicit functions for expressive, compute-bound scene description.
Fundamentals
Definition and Principles
Ray marching is a rendering technique used in computer graphics to approximate the intersection of rays with implicit surfaces or volumetric data by advancing rays in discrete steps along their path, rather than computing exact analytic intersections. This method is particularly suited for geometries defined procedurally or through distance functions, where closed-form solutions are unavailable or computationally prohibitive. In contrast to traditional ray tracing, which solves quadratic or higher-order equations for precise hit points, ray marching iteratively samples the scene, making it an efficient approximation for complex, non-algebraic shapes.[3]
The core algorithm of ray marching involves a loop that propagates a ray from its origin in the direction of travel, using a distance estimate to determine the step size at each iteration. Step sizes are typically derived from a signed distance field (SDF), which provides the minimum distance from the current point to the nearest surface, ensuring safe advancement without overshooting intersections. The process terminates when the ray position is sufficiently close to the surface (within a small epsilon threshold) or exceeds a maximum distance. A basic pseudocode outline for surface intersection is as follows:
function march(ray_origin, ray_direction, max_dist, [epsilon](/page/Epsilon)):
t = 0.0 // current [distance](/page/Distance) along [ray](/page/Ray)
while t < max_dist:
pos = ray_origin + t * ray_direction
dist = distance_function(pos) // e.g., from SDF
if dist < [epsilon](/page/Epsilon):
return t // intersection found
t += dist // advance by safe step
return no_intersection
function march(ray_origin, ray_direction, max_dist, [epsilon](/page/Epsilon)):
t = 0.0 // current [distance](/page/Distance) along [ray](/page/Ray)
while t < max_dist:
pos = ray_origin + t * ray_direction
dist = distance_function(pos) // e.g., from SDF
if dist < [epsilon](/page/Epsilon):
return t // intersection found
t += dist // advance by safe step
return no_intersection
This loop guarantees progress toward the surface while bounding the computational cost, though the specific implementation, such as sphere tracing, refines the stepping for antialiasing and efficiency.[3]
One key advantage of ray marching is its ability to handle intricate, non-intersectable geometries, such as fractals or procedural models, without requiring explicit mesh generation or storage, enabling real-time rendering of infinite detail in applications like scientific visualization. However, it can introduce artifacts from undersampling, including missed thin features or incorrect topology if step sizes are too coarse, necessitating careful tuning of parameters for accuracy.[3][8]
The origins of ray marching trace to the late 1980s in the context of volume rendering and implicit surfaces, with an early formulation appearing in the 1989 SIGGRAPH paper "Hypertexture" by Ken Perlin and Eric M. Hoffert, which applied iterative ray stepping to accumulate density in procedural volumes for phenomena like clouds and fur. The technique gained prominence for surface rendering in the 1990s, notably through John C. Hart's 1995 work on sphere tracing, which formalized distance-based marching for antialiased implicit surfaces in computer-aided design.
Signed Distance Fields
Signed distance fields (SDFs) provide a compact implicit representation of geometric shapes in computer graphics, particularly suited for ray marching due to their ability to encode spatial relationships efficiently. An SDF is defined as a continuous scalar function f: \mathbb{R}^n \to \mathbb{R} that, for any point \mathbf{p} in n-dimensional space (typically n=2 or $3), returns the signed minimum Euclidean distance from \mathbf{p} to the nearest point on the surface of the represented shape. The sign of this value indicates the position relative to the surface: positive for points outside the shape, negative for points inside, and exactly zero on the surface itself. This signed nature allows SDFs to distinguish interior and exterior regions, enabling robust handling of solid geometries without explicit boundary storage.[5]
A key mathematical property of true SDFs is their Lipschitz continuity with constant 1, expressed as |f(\mathbf{p}) - f(\mathbf{q})| \leq \|\mathbf{p} - \mathbf{q}\| for all points \mathbf{p}, \mathbf{q}. This condition ensures that the function gradient has magnitude at most 1 almost everywhere, satisfying the eikonal equation \|\nabla f(\mathbf{p})\| = 1 away from the surface, which guarantees that the returned value is a conservative estimate of the actual distance. Such properties prevent overstepping in traversal algorithms, as the difference in function values bounds the possible change in distance over any displacement.[9]
SDFs for basic primitive shapes are constructed analytically using closed-form distance formulas. For example, the SDF of a sphere centered at \mathbf{c} with radius r is given by
f(\mathbf{p}) = \|\mathbf{p} - \mathbf{c}\| - r,
where \|\cdot\| denotes the Euclidean norm; similar explicit formulas exist for other primitives like boxes, cylinders, and tori. More complex shapes are built via constructive solid geometry (CSG) operations on these primitives, which combine SDFs using simple pointwise functions: the union of two shapes is f_1 \cup f_2 = \min(f_1, f_2), the intersection is f_1 \cap f_2 = \max(f_1, f_2), and the difference (subtraction) is f_1 \setminus f_2 = \max(f_1, -f_2). These operations are closed under the distance property when the inputs are exact SDFs, though in practice, they often yield lower bounds that remain safe for applications like ray marching.[10]
In practice, SDFs are applied to generate 2D fields for curve rendering or 3D fields for volumetric shapes, including procedural geometries such as fractals. For instance, the 3D Mandelbulb fractal employs an iteratively estimated SDF derived from power-2 iterations in spherical coordinates to represent its intricate bulbous structure. Regarding error bounds, the Lipschitz condition of SDFs ensures conservative stepping: advancing along a ray by exactly the SDF value at the current point guarantees that no surface intersection is skipped, as any closer feature would violate the distance bound; this may lead to slightly larger steps near the surface but maintains topological correctness without undershooting.[11][3]
In the context of ray marching, the SDF value directly informs the step size in the traversal loop, while extensions to unsigned or density fields support volumetric rendering without signed surfaces.
Distance-based Algorithms
Sphere Tracing
Sphere tracing is a distance-guided variant of ray marching specifically designed for rendering implicit surfaces defined by signed distance fields (SDFs). Introduced by John C. Hart, the method advances a ray along its direction by placing a sphere at the current position with radius equal to the SDF value at that point, ensuring the next step does not overshoot or miss surface intersections.[12] This conservative stepping leverages the geometric properties of the SDF to guarantee convergence to the nearest intersection point.
The algorithm proceeds iteratively as follows: starting from the ray origin and initial parameter t = 0, compute the position p = \text{origin} + t \cdot \text{dir}, where \text{dir} is the normalized ray direction. The step size is then set to the SDF value at p, denoted d = \text{[SDF](/page/SDF)}(p), and t is updated by t \leftarrow t + d. This process repeats until d < \epsilon, where \epsilon is a small positive threshold (typically on the order of 0.001 times the scene scale) to account for numerical precision and terminate near the surface.[12]
The method provides conservative guarantees for intersection safety when the SDF satisfies the Lipschitz condition with constant 1, meaning the distance function does not change faster than the Euclidean distance (i.e., |\text{SDF}(x) - \text{SDF}(y)| \leq \|x - y\|). Under this condition, advancing by at most h \leq \text{SDF}(p) ensures the ray cannot jump over the surface, as the sphere of radius \text{SDF}(p) centered at p is guaranteed to be empty of intersections. A proof sketch involves showing that if the ray intersects the surface at some t^* > t, then \text{SDF}(p) \geq t^* - t, preventing penetration; the sequence of t_i is monotonically increasing and bounded above by t^*, hence converges to it.[12]
In scenes where the SDF violates the Lipschitz condition (e.g., due to non-smooth primitives or approximations), overstepping artifacts can occur, leading to missed intersections or tunneling. Mitigation typically involves tuning \epsilon smaller to detect closer to the surface or conservatively scaling steps by a factor less than 1, though this increases iteration count and computational cost.[12]
Grid-accelerated Ray Marching
Grid-accelerated ray marching is an acceleration technique for rendering implicit surfaces defined by signed distance fields (SDFs), where the distance field is voxelized into a uniform 3D grid. Each voxel stores the minimum distance to the nearest surface within its bounds, providing a conservative lower bound for safe stepping during traversal. This precomputation allows the ray to skip large portions of empty space by querying the voxel's minimum value instead of evaluating the full SDF at every point, significantly reducing computational cost in sparse environments. The grid is typically constructed by sampling the SDF at multiple points per voxel and taking the minimum value, ensuring the bound is tight enough for efficiency while remaining conservative to avoid missing intersections.[13]
The traversal begins with a ray-voxel intersection algorithm, such as the Digital Differential Analyzer (DDA), which efficiently computes the sequence of voxels intersected by the ray through incremental updates to the ray position and direction components. Upon entering a voxel, the ray steps forward by a distance determined by the stored minimum in that voxel, often using a formula like
h = \min(d_{\min} + \epsilon, \text{distance to voxel boundary})
where d_{\min} is the minimum distance in the current voxel and \epsilon is a small positive offset to prevent overshooting the surface due to numerical precision. If the step would exit the current voxel, a diagonal traversal adjustment may be applied to advance to the next relevant voxel boundary. Once the ray is sufficiently close to the surface (e.g., within a threshold), a finer SDF evaluation or analytic intersection within the voxel refines the hit point. This hybrid approach combines discrete grid skipping with continuous SDF marching for robustness.[13]
Compared to pure sphere tracing, grid-accelerated ray marching offers substantial speedups in sparse scenes by leveraging the grid to avoid redundant SDF queries in vast empty regions. Recent studies show speedups of 2–5× over pure sphere tracing depending on scene complexity and grid resolution.[14] However, the technique introduces memory overhead proportional to the grid resolution (e.g., a 128^3 grid storing a single float per voxel requires about 8 MB), and low-resolution grids can cause aliasing or popping artifacts at voxel boundaries due to abrupt changes in step sizes.[13]
Volumetric Techniques
Basic Volumetric Ray Marching
Basic volumetric ray marching adapts the ray marching technique to render participating media by integrating optical properties along rays through volumes, rather than seeking surface intersections. This involves sampling density and emission color at discrete intervals along the ray path, then accumulating transmittance and contributions according to the Beer-Lambert law, which describes exponential attenuation of light due to absorption and scattering in the medium.[15] The method enables visualization of effects like fog, smoke, and clouds by treating the volume as a continuous field of varying opacity and color.[16]
The roots of volumetric ray marching trace to early volume rendering techniques in the 1980s, such as Marc Levoy's 1988 work on displaying surfaces from sampled volume data using ray casting and splatting to composite voxel contributions along rays.[17] This approach was adapted in the 1990s for implicit volumes, incorporating step-wise integration to handle procedural or analytically defined density fields without explicit voxel grids, as seen in extensions of ray tracing for implicit surfaces.[3]
The core accumulation follows the volume rendering equation for emission and absorption, approximated discretely:
C = \int_{0}^{d} \sigma_t(s) \, c(s) \, \exp\left( -\int_{0}^{s} \sigma_t(u) \, du \right) ds
where C is the outgoing radiance, \sigma_t(s) is the extinction coefficient (density), c(s) is the emission color, and d is the ray length.[15] In practice, this integral is approximated by marching the ray in steps of size \Delta t, either fixed or adaptive, sampling \sigma_t and c at each point s_i = i \Delta t, and updating accumulated color C and transmittance T iteratively:
C \leftarrow C + T \cdot \sigma_t(s_i) \, c(s_i) \, \Delta t, \quad T \leftarrow T \cdot \exp(-\sigma_t(s_i) \, \Delta t).
This front-to-back compositing accumulates contributions from near to far, allowing early termination when T falls below a threshold for efficiency.[16]
Sampling strategies influence accuracy and performance; uniform steps with fixed \Delta t are simple but can lead to over- or under-sampling in varying densities.[7] For media with high opacity, importance sampling based on transmittance can reduce wasted samples by biasing toward optically thin areas, particularly in homogeneous media using an exponential distribution.[18] Jittering sample positions randomly within each step further mitigates discretization errors.
Common artifacts include banding, arising from regularly spaced uniform samples that create visible slicing in low-density regions, and noise from undersampling high-frequency details or sparse rays.[19] These can be alleviated by increasing step count or using noise-based jittering, though at higher computational cost.[20]
Density-based Variations
Density-based variations of volumetric ray marching utilize unsigned density functions to represent participating media, where the extinction coefficient \sigma_t(\mathbf{p}) at a point \mathbf{p} quantifies the medium's opacity due to both absorption and scattering per unit length. Unlike signed distance fields for surfaces, these densities are non-negative and spatially varying, allowing for smooth gradients in media like fog or clouds. During ray marching, the optical depth \tau along a ray from origin \mathbf{o} in direction \mathbf{d} up to distance t is approximated by numerically integrating \tau(\mathbf{o}, \mathbf{d}, t) = \int_0^t \sigma_t(\mathbf{o} + s \mathbf{d}) \, ds, typically via trapezoidal summation over discrete steps to compute transmittance T = e^{-\tau}. This enables realistic attenuation of light through the volume.
To account for light redirection within the medium, these techniques extend the accumulation loop with in-scattering contributions, where incoming radiance from direction \mathbf{\omega}_i is weighted by the phase function P(\theta), with \theta the angle between \mathbf{\omega}_i and the ray direction \mathbf{\omega}_o. The phase function models anisotropic scattering patterns observed in natural media, such as forward-peaking in clouds. A seminal and widely adopted model is the Henyey-Greenstein phase function,
P(\theta) = \frac{1 - g^2}{4\pi (1 + g^2 - 2g \cos\theta)^{3/2}},
parameterized by the asymmetry factor g \in [-1, 1], where g > 0 favors forward scattering and g < 0 backward scattering; g = 0 yields isotropic behavior. This function is analytically invertible for efficient importance sampling during Monte Carlo integration.[21]
Approximating multiple scattering in density-based marching often limits to single scattering for efficiency, computing direct in-scattering from light sources along the primary ray while ignoring re-scattered light. Higher-order effects, essential for dense media like milk or skin, can be approximated via iterative marching: after a scattering event, a secondary ray is spawned in the sampled direction, with its contribution back-propagated and accumulated in subsequent primary ray steps. Full multiple scattering requires unbiased volumetric path tracing, but iterative approximations balance realism and performance in real-time contexts.[22]
For efficient traversal of sparse density fields, where low-density regions represent near-empty space, variants employ hierarchical acceleration structures to skip segments with negligible extinction, reducing sample counts while preserving accuracy.
Modern developments in density-based ray marching are comprehensively detailed in Pharr et al.'s fourth edition of Physically Based Rendering (2023), particularly in chapters on volume scattering and rendering equations, which integrate these techniques into production path tracers supporting both single- and multiple-scattering models for photorealistic media simulation. Recent advances as of 2025 include Gaussian-based ray marching for accelerated real-time volumetric rendering, incorporating empty-space skipping and adaptive sampling.[23]
Pipeline Integrations
Deferred Shading
In deferred shading pipelines, ray marching integrates by leveraging the geometry pass to populate G-buffers with depth, normals, and material properties, followed by a shading pass where rays are marched from screen-space pixel positions using the stored data to compute effects like ambient occlusion, shadows, and reflections.[24] This separation allows complex lighting and visibility computations without re-rasterizing geometry, enabling efficient handling of dynamic scenes.[24]
Ray origins are derived in screen space from the G-buffer's depth and position reconstruction, with directions computed from view vectors and surface normals; step sizes are determined by querying signed distance fields (SDFs) stored in volume textures, allowing safe traversal without missing intersections.[24] For reflections, a secondary ray is traced from the surface point along the reflected direction, typically formulated as \mathbf{r}(t) = \mathbf{p} + t \cdot \mathbf{d}, where \mathbf{p} is the pixel's world position, t is the parameter scaled by SDF distances, and \mathbf{d} = \text{reflect}(-\mathbf{v}, \mathbf{n}) with \mathbf{v} as the view direction and \mathbf{n} the normal; intersections are evaluated via SDF sampling during marching.[25] This approach supports multi-bounce effects by iteratively querying the SDF for occlusion or color sampling.
The primary benefits include decoupling geometry complexity from effect computation, yielding soft shadows and occlusions independent of triangle counts, as demonstrated in Unreal Engine integrations from the 2010s, such as the UE3-based 2011 Samaritan demo and the 2015 Kite demo in UE4, using SDF ray marching for reflection shadowing.[24] Performance scales with volume resolution rather than scene geometry, achieving real-time rates on consumer hardware (e.g., 0.1ms for culling 2 million instances to 50,000 on PS4).[24]
Challenges arise from the limited resolution of screen-space origins and SDF volumes, potentially causing inaccuracies near surfaces or in sparse regions, which can introduce aliasing; mitigation often involves temporal reprojection across frames to accumulate samples and reduce noise, alongside clipmap hierarchies for finer detail.[24]
Screen-space Ray Marching
Screen-space ray marching is an efficient approximation technique for ray tracing effects that operates entirely within 2D screen coordinates, leveraging the depth buffer from a deferred shading pipeline to reconstruct 3D positions along the ray path. This approach avoids full 3D world-space computations by marching rays in UV (texture coordinate) space, unprojecting sample points to world space using the stored depth values, and checking for intersections against the scene geometry encoded in the depth buffer. It is particularly suited for real-time applications like reflections, shadows, and ambient occlusion, where full ray tracing would be too costly, as it exploits the perspective projection to linearize ray steps in screen space.[26]
The algorithm begins by initializing a ray origin at the current pixel's position, derived from the camera's view and the pixel's depth. The ray direction is computed in screen space, often as a reflection or shadow vector projected onto the UV plane. For each marching step, the UV coordinates are advanced by a fixed or adaptive increment: \Delta u = t \cdot \frac{\mathrm{dir}_{uv}}{f}, where t is the step distance in world space, \mathrm{dir}_{uv} is the normalized direction in UV space, and f is the focal length of the projection (derived from the field of view). This scaling ensures perspective-correct progression, with larger steps near the horizon to account for increasing pixel coverage in 3D space. At each sample point, the world position is reconstructed via unprojection: \mathbf{P} = \mathrm{unproject}(uv, z_{\mathrm{buffer}}), where z_{\mathrm{buffer}} is the depth value at the sampled UV, and unprojection inverts the view-projection matrix to obtain Cartesian coordinates. An intersection is detected if the marched ray's z-depth falls within an epsilon threshold of the buffer's depth: |z_{\mathrm{marched}} - z_{\mathrm{buffer}}| < \epsilon, typically with \epsilon on the order of 0.01 to 0.1 world units to tolerate precision errors. If no hit is found after a maximum number of steps (e.g., 16-32), the ray terminates without intersection.[26]
This technique was introduced in 2011 by Tiago Sousa at Crytek, initially for screen-space reflections and self-shadowing in the CryENGINE 3 pipeline, marking a shift from simple sampling-based methods to iterative marching for more robust occlusion handling. Common optimizations include binary search refinement along the final step to precisely locate the hit point, reducing aliasing by bisecting the interval between the last non-intersecting and first intersecting samples. Additionally, stride-based stepping (sampling every N pixels) with temporal jitter mitigates banding artifacts while improving performance by up to 4x on modern GPUs. However, the method suffers from view-dependent artifacts, such as missing intersections for geometry outside the current frustum or behind the camera, leading to incomplete effects like "leaking" reflections on edges. These limitations necessitate hybrid approaches, such as fallback to cubemaps for missed rays.[26]
Applications
Procedural Rendering
Ray marching enables the rendering of procedural geometry by evaluating signed distance fields (SDFs) directly in shaders, allowing for the construction of complex, non-polygonal shapes without traditional mesh generation.[27] Basic primitives such as spheres, boxes, and cylinders can be combined using operations that mimic constructive solid geometry (CSG), including unions via the minimum function, intersections via the maximum, and differences via subtraction of SDF values, to form intricate models.[27] These combinations extend to deformations like twisting and bending, where primitives are transformed through mathematical mappings—such as rotating coordinates around an axis for twisting or applying sinusoidal offsets for bending—before evaluating the distance function, enabling dynamic, parametric modeling of organic or architectural forms.[27]
A prominent application involves fractal geometry through distance-estimated iterated function systems (DE-IFS), which generate self-similar structures by iteratively applying transformations while estimating distances for efficient ray marching. The Mandelbox, a 3D fractal discovered in 2010, exemplifies this approach, where points are iterated via a folding operation that scales and inverts coordinates within a cubic boundary to produce bounded, intricate surfaces.[28] The iteration proceeds as \mathbf{p}_{n+1} = \text{fold}(\mathbf{p}_n), with the fold function typically defined as first scaling the point, then applying inversions when it exceeds box limits (e.g., if |x| > 1, set x = 2 - x for the positive side), followed by a power-2 scaling to create the fractal repetition.[29] Distance estimation in DE-IFS approximates the surface proximity by scaling the Euclidean norm after iterations, allowing sphere tracing variants to march rays conservatively toward the fractal boundary.[30]
Infinite terrains represent another procedural domain, where ray marching adapts steps based on heightfields derived from noise functions to simulate vast, detailed landscapes without explicit geometry. Heightfield marching evaluates a scalar height function h(x, z) along the ray, advancing by the minimum of the ray's vertical progress and the estimated terrain slope, often using Perlin or Simplex noise for natural variations in elevation.[31] Perlin noise, generated via layered gradient interpolations, provides coherent, continuous heights that prevent aliasing during marching, with step sizes dynamically adjusted to the noise's Lipschitz constant for accuracy. This method supports unbounded worlds by procedurally sampling noise at arbitrary positions, enabling seamless exploration of procedural environments like mountains or oceans.
The popularization of SDF-based procedural rendering in real-time contexts traces to Inigo Quilez's shader demonstrations from 2007 to 2010, which showcased GLSL implementations of fractal terrains and deformed primitives, influencing GPU-based graphics in tools like Shadertoy.[13] These works highlighted ray marching's efficiency for infinite, mathematical scenes, bridging demoscene artistry with practical shader programming.
Shading in procedural ray marching often derives surface normals directly from the SDF gradient at intersection points, computed numerically as the normalized difference of distances along perturbation axes:
\mathbf{n} = \normalize\left( \nabla \text{SDF}(\mathbf{p}) \right) = \frac{ \left( \text{SDF}(\mathbf{p} + h \mathbf{x}) - \text{SDF}(\mathbf{p} - h \mathbf{x}), \ \text{SDF}(\mathbf{p} + h \mathbf{y}) - \text{SDF}(\mathbf{p} + h \mathbf{y}), \ \text{SDF}(\mathbf{p} + h \mathbf{z}) - \text{SDF}(\mathbf{p} + h \mathbf{z}) \right) }{2h}
where h is a small epsilon (typically $0.001). This approximation enables Phong or physically-based lighting without precomputed geometry, enhancing the realism of procedurally generated surfaces.
Real-time Graphics Effects
Ray marching has become a staple in real-time graphics for rendering complex effects that traditional rasterization struggles with, leveraging GPU compute capabilities to simulate light propagation through volumes or signed distance fields (SDFs) at interactive frame rates. In modern game engines and visual effects pipelines, it enables phenomena such as volumetric fog, god rays, and subsurface scattering without requiring precomputed geometry, allowing dynamic environmental interactions. This approach is particularly suited to deferred rendering setups where ray marching occurs in screen-space or compute passes, balancing visual fidelity with performance constraints typical of 60 FPS targets on consumer hardware.
Shader integration of ray marching primarily occurs in fragment or compute shaders using languages like GLSL or HLSL, where developers implement iterative distance evaluations to trace rays from the camera or light sources. For instance, in GLSL fragment programs, god rays—simulating crepuscular beams through scattering media—are achieved by marching rays from pixel centers toward the light position, accumulating transmittance based on sampled density values along the path. A simplified GLSL snippet for such an effect might initialize a ray direction and step through the volume:
glsl
vec3 rayDir = normalize(worldPos - lightPos);
float t = 0.0;
for(int i = 0; i < maxSteps; i++) {
vec3 pos = lightPos + rayDir * t;
float density = sampleVolumeDensity(pos);
if(density > threshold) transmittance *= exp(-density * stepSize);
t += stepSize;
if(t > maxDist) break;
}
vec3 rayDir = normalize(worldPos - lightPos);
float t = 0.0;
for(int i = 0; i < maxSteps; i++) {
vec3 pos = lightPos + rayDir * t;
float density = sampleVolumeDensity(pos);
if(density > threshold) transmittance *= exp(-density * stepSize);
t += stepSize;
if(t > maxDist) break;
}
This code, adapted from common shader implementations, highlights the loop-based marching core, with early termination to avoid over-sampling empty space. Similarly, water caustics in real-time water shaders use ray marching to propagate light rays through refractive surfaces, bending them via SDF perturbations for caustic patterns on submerged objects.
Hardware considerations for ray marching emphasize GPU parallelism, as the technique distributes ray evaluations across thousands of shader invocations, exploiting SIMD architectures in modern GPUs like NVIDIA's Turing or AMD's RDNA series. In Vulkan with SPIR-V, ray marching for SDF evaluation is compiled into compute shaders that leverage descriptor sets for efficient texture sampling of distance fields, enabling scalable parallelism for effects in large scenes. This integration allows Vulkan-based engines to handle ray marching in parallel pipelines, reducing synchronization overhead compared to older OpenGL fixed-function paths.
Optimizations are crucial for maintaining real-time performance, with adaptive stepping adjusting march distances based on local SDF gradients to skip empty regions—reducing average steps per ray from 100 to under 20 in sparse volumes. Early ray termination checks for intersections or opacity thresholds, halting computation once a sufficient contribution is accumulated, which is vital for effects like volumetric lighting where full ray extents are unnecessary. Level-of-detail (LOD) techniques further enhance efficiency by using coarser SDF resolutions for distant objects, preserving detail near the viewer while minimizing fill-rate demands.
In the 2020s, ray marching powers advanced effects in engines like Unity's High Definition Render Pipeline (HDRP), where it renders volumetric clouds by marching through noise-based density fields to simulate scattering and absorption, achieving 60 FPS at 1080p on mid-range GPUs with temporal accumulation over frames. For example, Unreal Engine 5's Lumen system employs ray marching for real-time global illumination, complementing Nanite's geometry virtualization for advanced scene rendering, including terrain effects.[32] As of 2025, ray marching supports advanced volumetric effects in games like Assassin's Creed Shadows for realistic lighting and scattering, and is integrated with neural methods for efficient novel view synthesis in AR/VR applications.[33][23]
Despite these advances, ray marching faces limitations in real-time contexts, primarily fill-rate bottlenecks from high iteration counts overwhelming pixel shaders on bandwidth-limited hardware. Post-2015 GPU developments, such as NVIDIA's RT cores, have begun hybridizing ray marching with hardware-accelerated ray tracing, using marching for volumetric components while RT cores handle primary visibility, though full adoption remains limited by shader complexity. Screen-space variants briefly reference ambient occlusion by marching in depth buffers for local shadowing.