Лекция 7 мин.
z-fighting (Z-конфликт) , также называемый сшиванием или плоскостным конфликтом , — это явление в 3D-рендеринге , которое возникает, когда два или более примитива находятся на очень похожем расстоянии от камеры. Это приводит к тому, что их значения в z-буфере , отслеживающем глубину, оказываются почти одинаковыми или идентичными. Это означает, что при рендеринге определенного пикселя возникает неоднозначность относительно того, какой из двух примитивов отображается в этом пикселе, поскольку z-буфер не может точно определить, какой из них находится дальше от другого. Если один пиксель однозначно ближе, то менее близкий может быть отброшен. Это особенно характерно для копланарных многоугольников, где две грани занимают практически одно и то же пространство, и ни одна из них не находится перед ними. В результате затронутые пиксели отображаются фрагментами одного или другого многоугольника произвольно, способом, определяемым точностью z-буфера. Это также может меняться при изменении сцены или камеры, в результате чего один многоугольник «выигрывает» z-тест, затем другой и так далее. В итоге получается мерцание и шумное растеризация двух многоугольников, которые «борются» за цвет пикселей экрана. Эта проблема обычно вызвана ограниченной точностью на субпиксельном уровне, ошибками округления при вычислении чисел с плавающей и фиксированной запятой .
Чем выше точность z-буфера, тем меньше вероятность возникновения z-конфликтов. Но для копланарных многоугольников эта проблема неизбежна, если не принять меры по ее устранению.
По мере увеличения расстояния между ближней и дальней плоскостями отсечения , и особенно при выборе ближней плоскости вблизи глаза, возрастает вероятность возникновения z-конфликтов между примитивами. В больших виртуальных средах неизбежно возникает конфликт между необходимостью обеспечения видимости на расстоянии и на переднем плане. Например, в симуляторе космического полета, если далекая галактика нарисована в масштабе, у зрителя не будет достаточной точности для обеспечения видимости любой геометрии кабины на переднем плане (хотя даже численное представление создаст проблемы до рендеринга с использованием z-буфера). Для смягчения этих проблем точность z-буфера смещается в сторону ближней плоскости отсечения, но это не всегда так, и этого недостаточно для устранения всех проблем, связанных с z-конфликтами.

Демонстрация z-бойца с использованием нескольких цветов и текстур на сером фоне.
Проблемы с Z-конфликтом, вызванные недостаточной точностью буфера глубины, можно решить простым уменьшением видимого расстояния в мире. Это уменьшает расстояние между ближней и дальней плоскостями и решает проблему с точностью. Однако в некоторых виртуальных средах, где видимость на больших расстояниях имеет важное значение или где уменьшенная видимость была бы нереалистичной, например, в космическом или авиационном симуляторе, это невозможно. В таких случаях существуют альтернативные методы. Один из них — «имитация» расстояния до объектов, находящихся далеко от пользователя, без фактического изменения их положения. Например, если максимальное безопасное расстояние видимости (за пределами которого возникает Z-конфликт) составляет 10 000 единиц, а объект, который нужно отобразить, находится на расстоянии 15 000 единиц, то этот объект можно отобразить на расстоянии 10 000 единиц, но масштабировать пропорционально перемещению. Таким образом, объект, масштабированный вдвое, будет выглядеть так, будто он находится вдвое дальше, чем на самом деле. Если это делается только для объектов, которые уже находятся близко к максимальной дальности обзора или на ней, и объекты, близкие к пользователю, отображаются нормально, то этот метод не должен быть заметен. Другой метод, используемый для уменьшения или полного устранения Z-конфликтов, — это переход на логарифмический Z-буфер, то есть инвертирование Z. Этот метод используется в игре Grand Theft Auto V. Из-за способа кодирования числа с плавающей запятой имеют гораздо большую точность, когда они ближе к 0. Здесь инвертирование Z приводит к большей точности при хранении глубины очень удаленных объектов, тем самым значительно уменьшая Z-конфликты.

Если объекты действительно должны находиться друг на друге, слегка разнеси их:
Model A: Z = 10.000
Model B: Z = 10.001
Для UI/декалей иногда достаточно очень маленького смещения, например:
transform.position += transform.forward * 0.001f;
Но величина зависит от масштаба сцены.
Очень частая причина:
████████████ ← Mesh A
████████████ ← Mesh B
Если это, например, земля + плоскость травы, лучше сделать так:
████████████ ← трава
------------------ ← земля
То есть дать траве небольшой offset по высоте.
Camera Near/Far Clip PlaneЭто очень важный момент.
Если камера:
Near = 0.01
Far = 100000
точность depth buffer распределяется неравномерно, и Z-fighting становится гораздо вероятнее.
Можно пробовать, например:
Near = 0.1
Far = 1000
или даже:
Near = 0.5
Far = 500
если игра позволяет.
Особенно важно не ставить чрезмерно маленький Near Clip Plane.
Если ты пытаешься сделать, например:
не всегда стоит накладывать второй mesh прямо на первый.
В Unity можно использовать Decal Projector / decal-систему соответствующего render pipeline.
Если ты контролируешь shader, можно использовать depth offset, например концептуально:
Offset -1, -1
Это заставляет поверхность восприниматься как находящаяся немного ближе/дальше при сравнении глубины.
Но это скорее специализированное решение, а не замена исправлению геометрии.
Иногда кажется, что:
Position A = (0, 0, 0)
Position B = (0, 0, 0)
но проблема может быть не только в Transform.position.
Могут совпадать именно поверхности mesh после трансформаций, например из-за:
если мерцание происходит внутри одного Unity Terrain именно при переходе от одной painted texture к другой, то это не обязательно Z-fighting. Более того, в обычной ситуации с двумя слоями Terrain это скорее всего не Z-fighting, потому что Terrain все равно имеет одну геометрическую поверхность, а текстуры смешиваются шейдером.
Допустим, на одном Terrain есть раскаска :
Unity не кладет три поверхности друг на друга. У него примерно такая схема:
ОДНА поверхность Terrain
───────────────────────────────
↑ ↑ ↑
трава земля камень
Texture 1 ─┐
Texture 2 ─┼─→ смешивание → итоговый цвет
Texture 3 ─┘
Поэтому если на границе трава ↔ земля появляется мерцание, рябь, белые/темные пиксели или поверхность будто «перескакивает», надо искать причину в другом.
Если текстура имеет мелкие детали:
▒▒▒▒▒░░▒▒░░▒▒▒▒░░
░▒▒▒░░▒▒▒▒░░▒▒▒░░
и игрок смотрит на Terrain под острым углом или издалека, отдельные пиксели текстуры начинают быстро меняться между mip-уровнями.
Получается:
Кадр 1 → ░▒░▒░▒
Кадр 2 → ▒░▒░▒░
Кадр 3 → ░▒░▒░▒
И глаз воспринимает это как мерцание.
Это особенно заметно у:
Очень частый источник мерцания.
Если текстура имеет неправильные или отсутствующие mipmaps, при удалении камеры она может выглядеть нестабильно.
Проверь настройки текстуры в Inspector:
Texture Import Settings → Generate Mip Maps
Для обычных Terrain-текстур mipmaps обычно должны быть включены.
На границе используются несколько текстур одновременно.
Если сами текстуры сильно отличаются по детализации/контрасту, граница может визуально «шуметь».
Попробуйте увеличить Brush Opacity не обязательно — важнее сделать переход более широким и плавным.
Это очень распространенная причина, особенно если цветовая текстура выглядит нормально, но поверхность все равно «блестит» или рябит.
Например:
Albedo → нормально
Normal Map → X слишком сильная детализация
↓
мерцание света
Попробуй временно убрать Normal Map у проблемного Terrain Layer.
Если мерцание исчезло — проблема практически найдена.
Например:
Плохо:
белый камень █████
черные пятна █████
При уменьшении изображения видеокарта постоянно пересчитывает, какие пиксели должны попасть в итоговый цвет.
Лучше:
текстура с более равномерной частотой деталей и меньшим экстремальным контрастом.
Если у тебя два Terrain или две поверхности Terrain находятся друг над другом, тогда да:
Terrain A
────────────────────
Terrain B
────────────────────
И вот здесь уже настоящий Z-fighting.
Например, особенно стоит проверить:
Сделайте такой тест:
У проблемной текстуры временно уберите Normal Map.
Мерцание исчезло → проблема в Normal Map/освещении.
Заменитепроблемную текстуру на простую однотонную.
Исчезло → про блема в самой текстуре, mipmaps или aliasing.
Приблизьте камеру.
Если:
далеко → мерцает
близко → нормально
то очень вероятны mipmapping / texture aliasing / filtering.
Отключите второй Terrain Layer.
Если мерцание исчезает именно при его отключении — исследуй текстуру, normal map и параметры Terrain Layer, а не Z-buffer.
Включите Scene View и посмотри геометрию.
Если обнаружится:
Terrain
+
другая поверхность
тогда уже проверяй Z-fighting.
проверка в таком порядке:
1. Normal Map проблемного Terrain Layer
↓
2. Mip Maps текстуры
↓
3. Texture Filtering / Anisotropic Filtering
↓
4. Слишком мелкий/контрастный рисунок текстуры
↓
5. Terrain Layer / shader
↓
6. И только потом Z-fighting
То есть не стоит сразу лечить это через Near/Far Clip Plane. Если у тебя действительно одна поверхность Terrain, изменение Near/Far может вообще ничего не дать.
Комментарии