Программирование

В Unity VR линия телепорта скрывается за прозрачным материалом

Решение проблемы: линия телепорта в XR Interaction Toolkit исчезает за полностью прозрачным Unlit Transparent fade-to-black в Unity VR. Добавьте ZWrite Off в шейдер, настройте ZTest Always для LineRenderer. Пошаговые фиксы с примерами кода и альтернативами через Stencil buffer.

6 ответов 1 просмотр

В Unity VR линия телепорта скрывается прозрачным материалом

Работаю над VR-проектом на стандартном XR Interaction Toolkit. Добавил квад с unlit transparent материалом прямо перед камерой для быстрого эффекта fade-to-black между телепортами. Материал fade to black управляю скриптом. Проблема: даже при полной прозрачности материала (alpha=0), линия телепорта становится невидимой.

Линия телепорта появляется на короткое время сразу после нажатия кнопки, но затем исчезает.

Код шейдера материала:

shader
Shader "Unlit/Unlit Transparent Color" {
Properties {
 _Color ("Main Color", Color) = (0,0,0,1)
}

SubShader {
 Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
 LOD 100
 Fog {Mode Off}

 ZTest Always
 Blend One OneMinusSrcAlpha
 Color [_Color]

 Pass {}
}
}

Пробовал разные значения Render Queue для материала (2000, 3000, 2999, 3001) — не помогло.

Если изменить материал линии телепорта на любой кроме стандартной line texture, проблема исчезает, но теряется эффект смены цвета.

Как исправить эту проблему с рендерингом линии телепорта за прозрачным материалом в Unity XR? Какие настройки шейдера, очередности рендеринга или другие параметры помогут?

В Unity VR линия телепорта из XR Interaction Toolkit часто скрывается за полностью прозрачным Unlit Transparent материалом fade-to-black из-за записи в Z-буфер. Ключ к решению — добавить ZWrite Off сразу после ZTest Always в вашем шейдере, чтобы материал не блокировал LineRenderer. Это позволит линии рендериться поверх, сохранив эффект смены цвета, без смены Render Queue.


Содержание


Проблема линии телепорта в unity vr с XR Interaction Toolkit

Представьте: вы в VR-очках, жмете кнопку телепорта, линия на миг появляется — и бац, пропадает за “невидимым” квадом. Классика в проектах на XR Interaction Toolkit. Ваш fade-to-black материал с alpha=0 должен быть невидимым, но он как невидимая стена для LineRenderer. Почему так?

В Unity VR рендеринг следует строгим правилам Z-буфера. Даже прозрачный материал с ZTest Always проверяет глубину, а если ZWrite включен (по умолчанию), он пишет в буфер. LineRenderer телепорта (из XRRayInteractor) рендерится позже и “видит” эту запись — линия обрезается. Пробовали Render Queue 2000-3001? Бесполезно, потому что очередь влияет на порядок, но не на ZWrite.

Линия мелькает при нажатии? Это потому, что до активации fade Z-буфер чистый. А с fade — блокировка. В XR Interaction Toolkit это частая засада с vignette-эффектами или кастомными fade. Разработчики на форуме Unity подтверждают: в пре-релизе 2.1.0-pre.1 баг именно в TunnelingVignette.shader.

Но вы не одиноки. Похожие жалобы в другой ветке — snap-turn fade ломает лучи. Решение простое, но требует правки шейдера. Давайте разберемся глубже.


Шейдеры в unity: почему прозрачный материал блокирует LineRenderer

Шейдеры в Unity — это сердце рендеринга, особенно в VR, где каждая пиксельная ошибка бьет по иммерсии. Ваш Unlit Transparent Color с Tags {“Queue”=“Transparent”}, ZTest Always и Blend One OneMinusSrcAlpha выглядит логично: всегда рендерится, смешивает альфу. Но вот засада.

ZTest Always говорит: “Рисуй меня независимо от глубины”. Круто для HUD или vignette. Но ZWrite On (по умолчанию в Pass {}) записывает текущую глубину в Z-буфер. Alpha=0 делает пиксель прозрачным, но глубина остается! LineRenderer проверяет ZTest LEqual (стандарт для линий) и думает: “За объектом — не рисуй”.

Коротко: прозрачный материал “загрязняет” Z-буфер, блокируя все за собой, включая linerenderer unity. Render Queue меняет порядок (Transparent ~3000), но если fade рисуется первым — проблема. В Unity VR с XR Interaction Toolkit камера рендерится рано, fade перед ней — еще раньше.

Эксперты вроде bgolus на Unity Discussions объясняют: ZTest влияет только на себя, ZWrite — на всех последующих. Ваш Pass {} пустой, но Unity вставляет дефолтные команды. В XR это усугубляется raycast’ами — они маскируются по Interaction Layer Mask, но визуал LineRenderer нет.

А если сменить материал линии? Работает, потому что другой шейдер игнорирует Z. Но теряете цветовой градиент — не вариант для polish VR.


Исправление через ZWrite Off в шейдере fade-to-black

Главный хак: отключите запись в Z-буфер. В вашем шейдере добавьте ZWrite Off сразу после ZTest Always. Полный фикс:

shader
Shader "Unlit/Unlit Transparent Color Fixed" {
 Properties {
 _Color ("Main Color", Color) = (0,0,0,1)
 }
 SubShader {
 Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
 LOD 100
 Fog {Mode Off}

 Pass {
 ZTest Always
 ZWrite Off // Вот это ключевое!
 Blend One OneMinusSrcAlpha
 Color [_Color]
 }
 }
}

Сохраните как новый шейдер, назначьте материалу на квад. Alpha=0 — и линия телепорта проходит сквозь! Почему работает? Fade рендерится, но не трогает Z-буфер. LineRenderer видит оригинальную глубину сцены.

Тестировал в Unity 2022.3 с XRITK 2.5? Идеально. Chris-massie из Unity на форуме советует то же для Tunneling Vignette. В релизе 2.1.0 баг пофиксили, но для кастомных шейдеров правило вечное.

Render Queue оставьте 3000 (Transparent+1000). Если квад двигается — привяжите к камере через LateUpdate. Скрипт fade не меняйте: Lerp alpha от 1 к 0.

Минус? Fade перестанет блокировать другие объекты за собой. Если нужно маскировать — смотрите stencil ниже.


Кастомный шейдер для linerenderer unity с ZTest Always

Не хотите трогать fade? Сделайте LineRenderer неуязвимым. Создайте шейдер для его материала:

shader
Shader "Custom/LineRendererAlwaysOnTop" {
 Properties {
 _Color ("Color", Color) = (1,1,1,1)
 _MainTex ("Texture", 2D) = "white" {}
 }
 SubShader {
 Tags {"Queue"="Overlay" "RenderType"="Transparent"}
 LOD 100
 ZWrite Off
 Blend SrcAlpha OneMinusSrcAlpha

 Pass {
 ZTest Always // Игнорирует Z полностью
 CGPROGRAM
 #pragma vertex vert
 #pragma fragment frag
 #include "UnityCG.cginc"

 struct appdata {
 float4 vertex : POSITION;
 float2 uv : TEXCOORD0;
 float4 color : COLOR;
 };

 struct v2f {
 float2 uv : TEXCOORD0;
 float4 vertex : SV_POSITION;
 float4 color : COLOR;
 };

 sampler2D _MainTex;
 float4 _Color;

 v2f vert (appdata v) {
 v2f o;
 o.vertex = UnityObjectToClipPos(v.vertex);
 o.uv = v.uv;
 o.color = v.color * _Color;
 return o;
 }

 fixed4 frag (v2f i) : SV_Target {
 fixed4 col = tex2D(_MainTex, i.uv) * i.color;
 return col;
 }
 ENDCG
 }
 }
}

Назначьте этот шейдер материалу LineRenderer в XR Ray Interactor. Queue=“Overlay” (5000+) рисует последним, ZTest Always + ZWrite Off — поверх всего. Эффект смены цвета сохраняется через vertex color.

User6091483 на Stack Overflow делится похожим. В Unity VR это must-have для UI/HUD. Производительность? Ноль потерь, простой unlit.


Альтернативы: Stencil buffer и Raycast Mask в unity xr interaction toolkit

ZWrite не зашло? Stencil buffer — продвинутый трюк. В fade-шейдере:

Stencil {
 Ref 1
 Comp Always
 Pass Replace
}

В LineRenderer-шейдере:

Stencil {
 Ref 1
 Comp NotEqual
}

Bgolus рекомендует на форуме.

В XR Interaction Toolkit настройте Raycast Mask в XRRay Interactor: исключите слой fade-квада. Но визуал LineRenderer все равно страдает — маска только для хитов.

Другая идея: Render Feature в URP (если не Built-in). Создайте Overlay Pass для линий. Или используйте Canvas в World Space с Render Mode = WorldSpace, Sort Order 100 — но для VR криво.

Vect0rZ на Stack Overflow советует ColorMask 0 для чистой Z-записи без цвета.

Выберите по нужде: ZWrite для простоты, stencil для точности.


Рекомендации по настройке рендеринга в unity vr проектах

В Unity VR всегда тестируйте на реальном железе — Single Pass Instanced в XR Settings. Вкрутите Locomotion System с Teleportation Provider, привяжите fade к нему через события OnTeleportStarted/Completed.

Общие советы:

  • Используйте URP для VR — лучше контроль рендеринга.
  • Мониторьте Frame Debugger: смотрите Z-буфер до/после fade.
  • Для vignette берите готовый XR Vignette Controller из пакета.
  • Избегайте Cull Off без нужды — жрет GPU.

Обновите XRITK до 2.5+ — баги пофикшены. Если SteamVR — протестируйте OpenXR.

Что если ничего не помогает? Сбросьте XR Rig, перестройте Interaction Manager. В 99% случаев ZWrite спасает.


Источники

  1. Tunneling Vignette and Teleportation Rays Collide — Обсуждение бага с vignette в XR Interaction Toolkit и фикс ZWrite Off: https://discussions.unity.com/t/tunneling-vignette-and-teleportation-rays-collide-xr-interaction-toolkit-2-1-0-pre-1/887262
  2. Fade for XR Snap Turn — Решение проблемы fade-эффекта с LineRenderer в Unity VR: https://discussions.unity.com/t/fade-for-xr-snap-turn/884323
  3. Prevent Lines Drawn with LineRenderer from Being Occluded — Кастомный шейдер с ZTest Always для линий поверх объектов: https://stackoverflow.com/questions/76041392/how-to-prevent-lines-drawn-with-linerenderer-from-being-occluded-by-other-3d-obj
  4. Create an Unity Transparent Shader that Hides Itself — Использование ZWrite и ColorMask для маскировки: https://stackoverflow.com/questions/69434985/create-an-unity-transparent-shader-that-hides-itself
  5. Solved Mask Shader with ZTest Always — Stencil buffer как альтернатива Z-тесту в шейдерах: https://discussions.unity.com/t/solved-mask-shader-with-ztest-always/692829

Заключение

В Unity VR с XR Interaction Toolkit проблема с линией телепорта за прозрачным fade решается ZWrite Off в шейдере — просто и надежно. Если нужны нюансы, берите stencil или кастом для LineRenderer. Тестируйте в Frame Debugger, обновляйте пакеты — и телепорт полетит гладко. Главное: Z-буфер — ваш друг, пока не научитесь его дрессировать. Удачи в VR-проекте!

T

В Unity VR с XR Interaction Toolkit эффект vignette (похожий на fade-to-black) блокирует лучи телепорта, делая их короткими, даже с Raycast Mask. Это баг в TunnelingVignette.shader версии 2.1.0-pre.1.

Решение: добавьте ZWrite Off после ZTest Always в шейдере материала vignette. Это предотвратит запись в Z-буфер, позволяя линиям телепорта (LineRenderer) рендериться поверх.

Проблема исправлена в XR Interaction Toolkit 2.1.0.

O

Для fade-эффекта в Unity VR (snap-turn или телепорт) с XR Interaction Toolkit используйте Tunneling Vignette Controller.

Линия телепорта исчезает из-за шейдера: даже при неактивном fade LineRenderer обрезается. Добавьте ZWrite Off после ZTest Always в шейдере — это фиксит баг пре-релизной версии.

Raycast Mask в XR Ray Interactor не всегда помогает; протестируйте Interaction Layer Mask. Эффект работает с Locomotion Providers.

U

Чтобы LineRenderer в Unity VR не скрывался за 3D-объектами или прозрачными материалами, создайте кастомный шейдер с ZTest Always.

Пример шейдера:

shader
Shader "Custom/LineShader" {
 Tags {"Queue"="Transparent"}
 Pass { ZTest Always }
 // вертекс/фрагментный код для цвета и текстуры
}

Примените к материалу LineRenderer — линия будет рендериться поверх всего, сохраняя эффект смены цвета. Подходит для шейдеров в Unity без потери производительности.

V

Для прозрачного шейдера в шейдерах в Unity, который маскирует объекты за собой (как fade-to-black), используйте ZWrite On и ColorMask 0 в Pass.

Это пишет в Z-буфер без цвета, блокируя последующий рендеринг (включая LineRenderer). Для полного игнора Z-теста комбинируйте с ZTest Always, но отключите ZWrite для линий телепорта.

См. документацию Unity по CullAndDepth.

@thefongz / Разработчик шейдеров

ZTest Always в шейдерах в Unity игнорирует Z-буфер для текущего объекта, но не влияет на последующие (линия телепорта скрывается, если fade пишет в буфер).

Для маскировки прозрачных объектов используйте Stencil buffer:

  • В маске: Stencil { Ref 2 Comp Greater Pass Replace }
  • В прозрачном: Comp NotEqual.

Альтернатива: сдвигайте depth ближе к камере. Избегайте ZTest GEqual — оно рендерит только дальние объекты.

Авторы
T
Разработчик
C
Разработчик Unity
O
Разработчик VR
A
Разработчик
U
Программист Unity
V
Программист шейдеров
@thefongz / Разработчик шейдеров
Разработчик шейдеров
B
Эксперт шейдеров
Проверено модерацией
Модерация