Fact-checked by Grok 2 weeks ago

Z-fighting

Z-fighting, also known as stitching or planefighting, is a visual artifact in rendering that occurs when two or more primitives, such as polygons, occupy nearly identical depths relative to the viewer, causing the depth buffer to fail in consistently determining which surface is in front. This leads to a shimmering or flickering effect as the renderer alternates between displaying pixels from the overlapping surfaces across frames, often appearing as an unstable, moiré-like pattern on coplanar or closely spaced geometry. The phenomenon stems from the inherent limitations of the z-buffer, a per-pixel that stores depth values to resolve during hidden surface removal; when these values are quantized to finite precision (typically 16 to 32 bits), surfaces closer than the buffer's minimum distinguishable distance map to the same entry, triggering indeterminate comparisons. Z-fighting is especially prevalent in applications like and simulations, where large depth ranges—from near clipping planes at a few units to far planes at thousands—exacerbate precision loss due to nonlinear depth transformations in perspective projection. To mitigate z-fighting, rendering pipelines often employ techniques such as polygon offset adjustments to artificially separate depths, increased z-buffer resolution for finer granularity, or auxiliary buffers like stencils to mask and selectively render overlapping regions without relying solely on depth tests. While unavoidable in some scenarios involving exact , these methods ensure stable output in hardware-accelerated graphics APIs such as and .

Fundamentals

Definition

Z-fighting is a rendering artifact in that arises when two or more overlapping polygons or fragments have nearly identical depth values, causing them to compete inconsistently for visibility during the rasterization process. This competition leads to erratic rendering outcomes, such as one surface intermittently appearing in front of the other across frames or within the same frame. The z-buffer, also known as the depth buffer, is the primary mechanism used to resolve visibility in such scenarios by maintaining a per-pixel record of the closest depth value encountered during rendering. For each incoming fragment, the algorithm computes its depth (z-coordinate) and compares it against the stored value in the buffer; if the new depth is closer (typically smaller in a right-handed ), the fragment is rendered and the buffer is updated, while farther fragments are discarded. This process enables hidden surface removal without requiring polygons to be sorted by depth beforehand, making it efficient for complex scenes. However, z-fighting emerges from inherent limitations in this depth test, particularly sorting ambiguities caused by floating-point precision errors or quantization in the buffer representation. When the difference in z-values (Δz) between competing fragments falls below the effective precision threshold—such as machine epsilon (ε ≈ 1.19 × 10^{-7} for single-precision floats) or the buffer's resolution—small numerical discrepancies can cause the comparison to flip unpredictably. The core comparison can be expressed as: z_{\text{new}} < z_{\text{buffer}}[x, y] where z_{\text{new}} is the depth of the incoming fragment at pixel coordinates (x, y), and z_{\text{buffer}}[x, y] is the stored depth. If Δz < ε, round-off errors may alter the inequality's outcome, resulting in flickering or popping artifacts as fragments alternate in visibility.

Causes

Z-fighting primarily arises from the limited resolution of the depth buffer, which quantizes depth values into discrete levels, leading to indistinguishable z-values for surfaces that are spatially close but not identical in depth. For instance, a 16-bit integer depth buffer provides only 65,536 levels across the entire view volume, whereas a 32-bit floating-point buffer offers greater dynamic range but still suffers precision loss due to the IEEE 754 representation's inherent limitations on relative accuracy for large magnitudes. This quantization becomes problematic when the difference in depth (Δz) between fragments falls below the buffer's least significant bit, causing ambiguous depth comparisons during rasterization. Perspective projection exacerbates this issue through nonlinear compression of depth values in the view frustum, where precision is highest near the camera and diminishes rapidly toward the far plane. The near and far plane settings define the depth range, and a large ratio between them (e.g., far/near > 1000:1) allocates most buffer precision to distant geometry at the expense of nearby surfaces, amplifying quantization errors. In the perspective divide, the transformed z-value can be expressed as z' = \frac{(f + n) z - 2 f n}{(f - n) z}, where f is the far plane distance, n is the near plane distance, and z is the eye-space depth coordinate. This compression is inherent to the , as the depth buffer stores values proportional to 1/z, prioritizing close-range detail but leading to relative precision loss over large depths. Geometric configurations involving coplanar or near-coplanar polygons further contribute, as their surface normals and positions yield Δz values approaching zero after , falling within the same quantized depth level. Common in scenarios like adjacent meshes, volumes, or surface decals, these setups result in fragments from multiple competing for the same without a clear depth ordering. Hardware and software factors influence susceptibility, with fixed-function pipelines in legacy OpenGL versions (e.g., 1.x) relying on uniform fixed-point depth computations that lack the flexibility of programmable shaders for precision adjustments. Modern GPUs adhere to IEEE 754 single-precision floating-point for most operations, but depth buffers may use 24-bit fixed-point or 32-bit float formats, where single-precision's 23-bit mantissa limits distinguishability in high-dynamic-range scenes. Z-fighting intensifies in large view volumes, as the expansive dynamic range of z-values overwhelms the buffer's capacity, causing precision loss proportional to the scene scale.

Manifestations

Visual Artifacts

Z-fighting manifests as several distinct visual defects in rendered scenes, primarily due to imprecise depth comparisons between overlapping surfaces. The core artifacts include temporal flickering, where pixels alternate ownership between competing primitives across successive frames, creating an unstable, noisy appearance; spatial noise, characterized by random speckling or granular distortions within the overlapping regions; and popping, involving abrupt swaps of surface visibility that disrupt smooth transitions. These effects often produce more intricate symptoms, such as moiré-like patterns on grid-based structures, shimmering distortions on distant where depth diminishes. In dynamic scenes, artifacts intensify during motion, as camera or object movement alters sub-pixel z-ordering, making inconsistencies particularly evident at frame rates of 60 or higher. The presence of Z-fighting significantly degrades overall image quality, reducing scene realism especially in areas with fine details like foliage, wires, or dense urban environments, where it can amplify when interacting with . This instability not only compromises visual fidelity but also contributes to user discomfort through persistent flickering.

Common Scenarios

Z-fighting frequently arises in within CAD and BIM software, particularly when overlapping elements like walls, floors, or railings are precisely aligned, leading to coplanar surfaces that compete for depth priority during viewport navigation or zoomed-out views. In tools such as Revit or , this manifests as flickering internal lines or distorted geometry on large models, exacerbated by the need for exact alignments in building designs. Similarly, rendering plugins like used in architectural workflows can exhibit surface flickering in previews due to these coplanar issues. In , Z-fighting commonly appears during stitching, where seams between tiles or procedural landscapes cause adjacent polygons to share near-identical depths, resulting in visible artifacts along boundaries. techniques also contribute, as the from the light's perspective can produce fighting between shadowed surfaces and receivers when precision is insufficient. Particle systems with layered sprites, such as foliage or debris, often overlap in ways that trigger this issue, especially in dynamic environments. Specific examples highlight Z-fighting's prevalence in interactive applications. Flight simulators, for instance, encounter it between runway markings and the underlying , where projected decals fight with the mesh during low-altitude views or taxiing. In (FPS) games, bullet decals applied to walls create coplanar overlaps with the base geometry, leading to flickering upon impact. Historically, Z-fighting was prominent in early titles like (1996), owing to the era's fixed 16-bit depth buffers that offered limited precision for overlapping BSP surfaces and entities. In and contexts, Z-fighting is intensified by head tracking and near-eye displays, which demand higher z-precision for holographic overlays or environmental meshes that align closely with real-world or virtual surfaces. Coplanar elements, such as holograms projected onto scene , flicker noticeably during rapid movements, disrupting in applications like mixed-reality simulations. Even in non-real-time offline rendering environments like or , Z-fighting can occur in interactive previews or viewports using rasterization, such as when coplanar meshes are displayed during modeling. However, final ray-traced renders typically resolve these issues, as they trace rays per pixel without relying on a depth buffer, eliminating the precision conflicts seen in real-time previews.

Mitigation

Depth Buffer Techniques

Depth buffer techniques address Z-fighting by enhancing the and accuracy of depth comparisons during rendering, particularly for coplanar or nearly coplanar polygons. These methods modify the depth buffer's , range, or values to reduce the likelihood of ambiguous depth ties that cause flickering artifacts. Increasing the of the depth buffer significantly improves z- and mitigates Z-fighting. Traditional 16-bit depth buffers offer only discrete depth levels, leading to severe loss and frequent Z-fighting in scenes. Upgrading to a 24-bit buffer expands this to over 16 million levels, while 32-bit floating-point buffers provide even greater and , especially beneficial for floating-point implementations. Modern GPUs commonly support 32-bit floating-point depth buffers, which allocate higher where it is most needed—near the viewer—reducing artifacts without substantial performance penalties in most cases. Optimizing the near and far clipping planes minimizes z-range compression in the depth buffer, a primary cause of precision loss. The depth buffer's nonlinear mapping allocates most precision to values near the near plane, so setting the near plane as far from the camera as possible (while avoiding clipping visible ) and limiting the far plane improves overall resolution. A guideline for most scenes is maintaining a far/near below 1000 to preserve sufficient precision for distant objects and prevent Z-fighting. For example, in a typical application with a near plane at 1 unit, the far plane should not exceed 1000 units unless higher-precision buffers are used. Polygon offset, also known as z-bias, applies a post-projection adjustment to fragment depth values before the depth test, effectively separating coplanar surfaces. This technique adds a sloped or constant offset to avoid ties without altering geometry. In OpenGL, it is enabled via glEnable(GL_POLYGON_OFFSET_FILL) and configured with glPolygonOffset(factor, units), where the offset is computed as: \text{offset} = \text{factor} \times \Delta z + \text{units} \times r Here, \Delta z is the maximum depth slope of the polygon relative to the screen, and r is the smallest resolvable depth value in the buffer (often the minimum depth range). The factor handles sloped surfaces, while units provide a constant bias for flat ones; typical values are factor=1.0 and units=1.0 for moderate separation, adjustable based on scene scale to balance artifact reduction and potential shadow acne in lighting applications. This method was introduced in OpenGL 1.1 to resolve stitching artifacts between adjacent polygons. Reverse-Z buffering remaps the depth range from [0,1] to [1,0], leveraging the higher relative of floating-point numbers near zero for distant objects. In standard , precision decreases with distance due to the nonlinear , exacerbating Z-fighting far from the camera. By reversing the range—clearing the depth to 1.0 and using a that maps near to 1.0 and far to 0.0—the of the floating-point format provides better distribution for larger depths, improving by orders of magnitude in scenes with high far/near ratios. This approach, particularly effective with 32-bit floating-point buffers, was detailed in early hardware optimization work and is now widely adopted in modern rendering pipelines.

Alternative Approaches

One alternative to depth buffer modifications is exact sorting of polygons prior to rasterization, as exemplified by the painter's algorithm. This method requires all primitives by their average depth from the viewer and rendering them in back-to-front order without a depth buffer, ensuring that nearer surfaces obscure farther ones and eliminating Z-fighting entirely. However, the approach incurs an O(n log n) computational cost for in complex scenes containing thousands of polygons, and it fails in cases of cyclic depth dependencies (e.g., intersecting polygons), necessitating additional scene decomposition techniques. Another technique involves remapping depth values using a logarithmic depth buffer to enhance precision across vastly different scales, mitigating Z-fighting in expansive environments. The nonlinear transformation compresses the depth range near the viewer while expanding it for distant objects, given by the equation z' = \frac{\log_2 \left( \frac{z}{n} \right)}{\log_2 \left( \frac{f}{n} \right)} where z is the linear eye-space depth, n is the near plane distance, and f is the far plane distance. This is particularly effective in simulations of large worlds, such as planetary or space rendering, where standard linear buffers suffer from precision loss at distance. Deferred shading provides a pipeline-level solution by decoupling geometry rendering from lighting computation. During the geometry pass, scene primitives are rasterized into geometry buffers (G-buffers) with standard depth testing to resolve visibility and store per-pixel attributes like position, normal, and albedo; only the closest fragment per pixel survives. The subsequent lighting pass renders a fullscreen quad that samples the G-buffers to compute shading, bypassing repeated depth tests for overlapping light contributions. However, Z-fighting can still occur during the geometry pass due to depth precision limitations. This separation reduces fragment overdraw in lit scenes while maintaining depth-resolved geometry from the initial pass. In modern low-level APIs like Vulkan and DirectX 12, explicit multi-sample anti-aliasing (MSAA) with per-sample depth testing can help resolve visibility at subpixel levels, potentially reducing the visibility of Z-fighting artifacts in cases where overlaps occur at finer scales than single-sample rendering. By performing depth comparisons and coverage at multiple samples per pixel (e.g., 4x or 8x MSAA), the renderer can better determine occlusion for near-coplanar geometry. This approach integrates seamlessly with explicit render passes but increases memory and bandwidth demands proportional to the sample count. Hybrid methods combine algorithmic and shader-based adjustments tailored to specific elements, such as decals and shadows, to sidestep depth buffer dependencies. For instance, dithered offsets introduce spatial or temporal noise to depth biases, distributing potential artifacts evenly and reducing their visibility over time or across pixels. Shader-based extrusion, implemented in vertex or fragment shaders (e.g., via slight positional offsets normal to the surface), separates coplanar geometry like decals from base meshes without global buffer changes, preserving precision elsewhere. These are commonly applied to avoid Z-fighting in projected decals or shadow polygons, often in conjunction with deferred pipelines for efficiency.

References

  1. [1]
    Stencil buffers - UWP applications | Microsoft Learn
    Oct 20, 2022 · Stencil buffer information is embedded in the z-buffer data. How the ... This effect is called z-fighting or flimmering. To solve this ...
  2. [2]
    [PDF] Computer Graphics Framebuffers
    What Does the Depth Buffer Do If Two Polygons are in Exactly the Same 3D Location? You get an ugly phenomenon called "Z-Fighting", where the Depth Buffer ...
  3. [3]
    CMSC 23700 Winter 2014 Introduction to Computer Graphics ...
    The term Z-fighting is used to describe the situation when two primitives are mapped to the same Z-buffer value. Suppose you have an application with a near ...
  4. [4]
    Configuring Depth-Stencil Functionality - Win32 apps | Microsoft Learn
    Aug 18, 2020 · This effect is called z-fighting or flimmering. To solve this problem, use a stencil to mask the section of the back primitive where the ...
  5. [5]
    OpenGL FAQ / 12 The Depth Buffer
    Round-off errors or differences in rasterization typically create "Z fighting" for coplanar primitives. Here are some options to assist you when rendering ...
  6. [6]
    [PDF] A Subdivision Algorithm for Computer Display of Curved Surfaces
    Edwin Catmull. Utah Un iversity. Prepared for: Advanced Research Projects ... The author implemented the z-buffer algorithm by paging the z-buffer onto disk.Missing: paper | Show results with:paper
  7. [7]
    (PDF) The Magic of the Z-Buffer: A Survey - ResearchGate
    This paper presents a much-needed survey of key applications of the Z-buffer from the fields of rendering, modelling and vision in a common notation in order to ...
  8. [8]
    Depth testing - LearnOpenGL
    Z-fighting is a common problem with depth buffers and it's generally more noticeable when objects are further away (because the depth buffer has less precision ...
  9. [9]
    Visualizing Depth Precision | NVIDIA Technical Blog
    Oct 21, 2021 · In other words, it measures the rate at which depth comparison errors occur—which corresponds to issues like Z-fighting—under different ...Missing: definition | Show results with:definition
  10. [10]
    Depth Buffer Z Fighting - FlatRedBall
    Sep 30, 2023 · z fighting is a term which describes an artifact which occurs when two surfaces are parallel (or near parallel) and are close enough to one ...
  11. [11]
    The Perspective and Orthographic Projection Matrix - Scratchapixel
    The OpenGL perspective projection matrix is defined in OpenGL and used to project points onto an image plane, remapping them to a unit cube.Missing: compression | Show results with:compression
  12. [12]
    Preventing Z-fighting on coplanar polygons? - opengl - Stack Overflow
    Feb 12, 2013 · I'm trying to deal with Z-fighting on co-planar polygons in an OpenGL based renderer. Due to legacy issues I have my hands tied in many regards.How to avoid z-fighting in OpenGL when drawing both the mesh ...Z-Fighting coplanar faces. Draw one after another - Stack OverflowMore results from stackoverflow.comMissing: cause | Show results with:cause
  13. [13]
    Guide for beginners: What is Z-Fighting? - VisCircle GmbH
    Mar 14, 2019 · It is particularly common in coplanar polygons where two faces occupy substantially the same space without either face being in front.<|separator|>
  14. [14]
    z-fighting - OpenGL: Advanced Coding - Khronos Forums
    Jan 12, 2002 · Or if you mix vertex programs and fixed function pipelines in multipass. This will almost certainly result in the Z values of each pass ...Z-fighting - OpenGL: Advanced Coding - Khronos ForumsDeferred Rendering Z Fighting - OpenGL: Basic CodingMore results from community.khronos.org
  15. [15]
    [PDF] Floating Point and IEEE 754 Compliance for NVIDIA GPUs
    IEEE 754 defines floating point encoding with sign, exponent, and fraction fields. It standardizes how arithmetic results are approximated, and NVIDIA uses ...Missing: fighting | Show results with:fighting
  16. [16]
  17. [17]
  18. [18]
    Object(s) exhibits polygon z-fighting, moves erratically, and/or form ...
    Sep 17, 2024 · While working on a scene, the following behaviors may be observed: Translating (move/rotate/scale) is choppy or erratic Polygon z-fighting ...<|separator|>
  19. [19]
    Zoom out and view of internal lines of elements ( z-fighting)
    Sep 15, 2022 · How can I set the appearance of the internal lines of the elements, so as to never display them? When I zoom out of the view, by default some ...Missing: large volumes
  20. [20]
    Z-Fighting Fix - Feature Requests & Questions - Forum - Enscape
    Oct 19, 2020 · I'm seeing a lot of flickering surfaces where two planes are fighting for the same 3d space (Z-Fighting). This is really disorienting in VR.
  21. [21]
    Terrain z-fighting issues. - OpenGL: Basic Coding - Khronos Forums
    Aug 16, 2010 · In my game, I have train track laid out over a height-mapped terrain. The problem is the train track is so close to the terrain that it ...Missing: stitching shadow
  22. [22]
  23. [23]
    Z Fighting and Decals - Unity Discussions
    Jan 27, 2012 · A wild trick we found is to actually use boxes for decals instead of planes. The boxes don't seem to have the z-fighting issues that planes have.Missing: FPS bullet
  24. [24]
    FlightGear forum • View topic - z-fighting taxiway markings
    Jan 30, 2016 · I've built the latest version of FG and have noticed a lot of z-fighting in the taxiway/runway markings which make them flash annoyingly.
  25. [25]
    Way to fix bullet hole z-fighting? - Stack Overflow
    Dec 2, 2022 · Trying to work myself through basic FPS shooting as practice and I want to have bullet holes where you shoot. However, Texture Z-fighting occurs ...
  26. [26]
    Example of z-fighting with 16 bit z-buffer - VOGONS
    Apr 25, 2016 · Z-fighting with a 16-bit z-buffer occurs when objects intersect, especially at long distances, due to limited depth, causing a sawtooth effect.Missing: early | Show results with:early
  27. [27]
    Help with Z-Fighting with CAD models on Meta Quest - Unity Engine
    Mar 22, 2023 · Any suggestions for us to solve our z-fighting issue? Note we have had our application on HoloLens for some time with much less z-fighting but I ...Missing: VR AR holographic overlays
  28. [28]
    Flashing Surfaces + Misplaced Geometry (Z-Fighting) - IrisVR
    Z-fighting is when coplanar geometry shares space, causing flashing in VR. The engine struggles to render one over the other.
  29. [29]
    Z fighting and mesh incorrect rendering in Eevee
    Oct 2, 2022 · I have a scene with these objects look alright in perspective view, but messed up in camera and rendering output! It's in Eevee. Everything fighting and not ...What settings influence Z-Fighting in files with identical geometry?prevent z-fighting between billboard particles?More results from blender.stackexchange.comMissing: popping | Show results with:popping<|separator|>
  30. [30]
    [Maya] Fixing Z-Fighting - 3D Gumshoe
    May 29, 2013 · To fix this in Maya, select your camera and either reduce the Far Clip Plane or increase the Near Clip Plane.
  31. [31]
    13 Drawing Lines over Polygons and Using Polygon Offset - OpenGL
    The visual result is referred to as stitching, bleeding, or Z fighting. Polygon offset was an extension to OpenGL 1.0, and is now incorporated into OpenGL 1.1.
  32. [32]
    Learning to Love your Z-buffer. - Steve Baker
    The Z-buffer determines which objects are hidden. Poor precision causes 'flimmering'. Move the near clip plane further from the eye to improve precision.
  33. [33]
    Transparent? very urgent! need help! - OpenGL: Basic Coding ...
    You should try to keep the far/near ratio below 1000. For a far value of 200000 the near value should therefor be at least 200 (YMMV). HTH. Jean-Marc. Mazy ...
  34. [34]
    glPolygonOffset - OpenGL 4 Reference Pages - Khronos Registry
    The value of the offset is factor×DZ+r×units, where DZ is a measurement of the change in depth relative to the screen area of the polygon, and r is the smallest ...
  35. [35]
    How to avoid Z-fighting on plane object? Flickering issue - Rendering
    Mar 18, 2022 · Making it slightly convex is probably the best option but if you want another one you can just pick a random z offset from the surface when ...
  36. [36]
    Optimal depth buffer for low-cost graphics hardware
    Optimal depth buffer for low-cost graphics hardware. Authors: Eugene ... EUROGRAPHICS. HWWS99: 1999 SIGGRAPH/EUROGRAPHICS Workshop On Graphics Hardware.
  37. [37]
    Multiple depth buffer for layer effect - Vulkan - Khronos Forums
    Apr 6, 2022 · Draw that stuff in that order with no depth buffer. Problem solved. It is called Painter's Algorithm. By default subsequent draws hide previous ...
  38. [38]
    Logarithmic Depth Buffer - Game Developer
    Using logarithmic depth buffer to get rid of Z-fighting and near plane clipping in large-scale planetary rendering.
  39. [39]
    LearnOpenGL - Deferred Shading
    ### Summary: How Deferred Shading Handles Z-Fighting and Depth Testing in the Lighting Pass
  40. [40]
    [PDF] HIGH-QUALITY RASTERIZATION - Chris Wyman
    MSAA. Memory required. Rendering cost. Page 14. ACAA CAVEATS. Assumes z test during shade passes only one fragment per sample. Fails when z-fighting occurs.
  41. [41]
    How to avoid Z-fighting when using Deferred Decal?
    Jan 29, 2017 · What you need though is for the decal surface to be further above the surface its being applied to, thats why youre getting the z fighting ...Missing: shading avoidance