Искать

Графика для XR: лучшие методики и решения для отдельных случаев

Last updated: January 2019

Чем эта страница будет вам полезна: здесь вы найдете советы по оптимизации шейдеров и эффективному использованию стека постобработки. Замечательная статья для опытных программистов Unity, планирующих или уже занимающихся разработкой XR-контента (VR и/или AR).

В настоящее время Unity поддерживает четыре метода рендеринга для XR: многопроходный, однопроходный, недавно выпущенный процесс однопроходного дублирования, а также однопроходный для Android (аналогичный обычному однопроходному рендерингу).

Многопроходный
  • Требует двукратной обработки графики в сцене: в каждой из двух текстур, предназначенных для каждого глаза, объекты и сцены отрисовываются отдельно.
  • Расчеты в GPU ведутся для каждой текстуры отдельно, что делает такой способ рендеринга наименее эффективным, но он работает на большинстве устройств по умолчанию и требует минимальных изменений.
  • Общим может быть только отсечение и часть результатов построения теней.
Однопроходный
  • Этот метод объединяет две текстуры в одну большую, которая называется текстурой с удвоенной шириной.
  • Отрисовка каждого объекта начинается с левого ракурса, затем камера переключается на правый ракурс, и объект отрисовывается вновь.
  • Обработка графики в сцене проводится один раз, что гораздо быстрее обрабатывается CPU, но при этом переключение состояния GPU происходит гораздо чаще.

XR-графика в Unity

Простой пример однопроходного стереорендеринга

Однопроходное дублирование
  • Доступно в Unity версии 2017.2 и выше.
  • Обе текстуры для левого и правого глаза содержатся в одном массиве текстур (два слайса одного и того же массива текстур).
  • При задании ракурса он применяется к обоим слайсам автоматически. При вызове draw создается экземпляр draw удвоенного размера. В зависимости от экземпляра Unity определяет, в каком слайсе сохранять результат рендеринга.
  • Требует столько же ресурсов GPU, но с меньшим количеством вызовов отрисовки и ресурсов CPU, что повышает производительность.
  • Технология поддерживается на следующих устройствах:
    • Windows 10 с D3D11 и современными драйверами графики;
    • Hololens;
    • PS4.
  • Произойдет переключение на многопроходный рендеринг, если расширение не поддерживается на целевом устройстве.
  • DrawProceduralIndirect(...) требует изменений, вносимых вручную.

XR-графика в Unity

Пример однопроходного дублирования

Добавьте эти макросы в шейдеры для использования однопроходного дублирования

Если вам нужно однопроходное дублирование, то мы настоятельно рекомендуем добавить следующие макросы в вершинные и фрагментные шейдеры. Это несложно сделать, а все встроенные шейдеры Unity их уже поддерживают.

Для вершинных шейдеров:

XR-графика в Unity

Код шейдера без макроса.

XR-графика в Unity

Тот же код с добавленным первым макросом...

XR-графика в Unity

вторым...

XR-графика в Unity

третьим...

XR-графика в Unity

и четвертым.

Если вам необходимо использовать unity_StereoEyeIndex в коде фрагментного шейдера, то нужно сделать следующее:

XR-графика в Unity

Различия между RenderScale и RenderViewportScale

Допустим, вы хотите уменьшить размер текстуры с 1 до 0,5. Если делать это с помощью RenderScale, то исходная текстура удалится и создастся новая, с разрешением в четыре раза меньше, поскольку каждая из сторон уменьшилась в 2 раза.

Эта операция затратна при динамическом обновлении во время работы игры; RenderViewportScale в этом случае будет эффективнее. Ракурс при этом переключается на меньший участок той же текстуры и сохраняет результаты рендеринга на этом участке, вот так:

XR-графика в Unity

Это требует меньше ресурсов, но создает определенные проблемы, поскольку вы используете лишь часть текстуры (дополнительные советы по RenderViewportScale смотрите ниже).

Итак, RenderScale удаляет текстуру с последующим созданием новой, а RenderViewportScale просто изменяет ракурс. Есть и другие важные отличия.

  • XRSettings.eyeTextureResolutionScale предназначен для фактического размера текстуры, а XRSettings.renderViewportScale применяется для ракурса.
  • При отложенном рендеринге XRSettings.renderViewportScale не поддерживается.
  • Масштаб применяется к обоим размерам, поэтому значение 0,5 приведет к уменьшению текстуры в 4 раза с итоговой площадью в 25% от исходной.
Стек постобработки в XR: трудности и решения

Последняя версия нашего стека постобработки уже вышла. Использование стека в XR-контенте связано с определенными трудностями: о них и о ключевых решениях мы расскажем в этом разделе.

Проблема

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

  • В случае удвоенной ширины итоговая текстура должна иметь ширину как минимум в два раза больше, чем ширина части текстура для одного глаза.
  • При однопроходном дублировании и аналогичном однопроходном рендеринге для Android необходим текстурный массив с одним слайсом на каждый глаз.

Решение

  • Для упрощения работы и ускорения генерации правильного формата экранной текстуры класс XRSettings.eyeTextureDesc сделан общедоступным (в Unity 2017.2 и выше).
  • Операция возвращает структуру RenderTextureDescriptor, получаемую от диспетчера XRTexture в составе движка, что означает, что эта структура настроена в соответствии с текстурами, находящимися под управлением движка.
  • RenderTextureDescriptor также можно получить из исходной текстуры, если это возможно. Если вы используете устаревшую инфраструктуру MonoBehaviour.OnRenderImage, то вам потребуется извлечь дескриптор прямо из текстуры.
  • Используя eyeTextureDesc, вы можете заменить все назначения RenderTexture на RenderTextureDescriptor, что эффективнее ручной генерации параметров.
  • API RenderTextureDescriptor проще; он соответствует операциям нижележащих API. Если вы используете явные аргументы, то Unity упаковывает их в RenderTextureDescriptor и направляет их в базовую систему движка (в RenderTexture.GetTemporary или CommandBuffer.GetTemporaryRT), что создает необходимость работы со структурой посредством скриптов, без промежуточного слоя, который управлял бы этим автоматически.

Проблема

В XR есть разница между физическим размером текстуры рендеринга и логическим размером экрана / изображения для глаза.

Решение

  • Для промежуточного размещения текстуры с результатами рендеринга в памяти следует использовать метод eyeTextureDesc, поскольку он отвечает за размещение физических текстур.
  • Если в скриптах или в шейдерах используется размер экрана (например, если вы используете в шейдере параметры размера экрана или строите текстурную пирамиду), то лучше работать с логическим размером. Для этого можно использовать методы XRSettings.eyeTextureHeight и XRSettings.eyeTextureWidth.
  • Эти методы отражают размеры текстуры для одного глаза, и в них следует задать размер экрана, на котором запущено приложение.
  • Помните, что в случае удвоенной ширины значение eyeTextureWidth не будет равно половине значения ширины eyeTextureDesc, поскольку дескриптор в целях размещения текстуры в памяти удваивает ширину, а затем слегка меняет размер, чтобы подготовить ее для мипмаппинга.

Ниже приводится пример кода, который использовался до появления eyeTextureWidth для определения ширины при заданной ширине экрана.

XR-графика в Unity

Теперь ширину экрана можно получить из eyeTextureWidth и использовать ее для задания соотношения сторон, как в примере ниже:

XR-графика в Unity

Проблема

Как убедиться, что координаты текстуры подходят для стереоизображения, и что изображение выводится на правильный глаз?

Если изображение берется из текстуры с удвоенной шириной, то необходимо убедиться, что координаты изображения соответствуют нужной половине текстуры (для каждого глаза используется своя половина текстуры).

Решение

Понадобится корректор координат текстуры, и для этого Unity предлагает два решения:

1. Макрос TRANSFORM_TEX.

  • This is a macro that works with _ST properties in ShaderLab. It’s most commonly used with MainTex_ST.
  • Свойство _ST обновляется до рендеринга изображения для глаза в режиме однопроходного рендеринга с удвоенной шириной текстуры, преобразуя координаты текстуры в правильный для глаза диапазон. Кроме того, свойство управляет методом RenderViewportScale автоматически.

XR-графика в Unity

  • Unity will populate _ST shader properties if you’re using Graphics.Blit or CommandBuffer.Blit:
    • MainTex_ST всегда содержит данные.
    • Другие свойства _ST будут содержать данные, если текстура является XR-текстурой (VRTextureUsage!=None).
  • Макрос не работает с нестандартными процедурами, например, BlitFullScreenTriangle в последнем выпуске стека постобработки.

2. Вспомогательные функции unity_StereoScaleOffset и UnityStereoTransformScreenSpaceTex/TransformStereoScreenSpaceTex.

unity_StereoScaleOffset — это массив значений float4:

  • UnityStereoTransformScreenSpaceTex
  • TransformStereoScreenSpaceTex

Он объявляется как часть блока констант однопроходного рендеринга (UnityStereoGlobals) и индексируется с помощью Unity_StereoEyeIndex.

В случае текстуры с удвоенной шириной unity_StereoEyeIndex ограничен буфером постоянных, и каждый вызов draw обновляет изображение для правильного глаза. Поэтому, если в буфере постоянных будет значение для левого глаза, то изображение будет отрисовываться для левого глаза и наоборот. Экземплярам шейдера будет известно, что для отрисовки для правильного глаза достаточно проверить буфер постоянных и найти правильное значение для unity_StereoEyeIndex.

XR-графика в Unity

Зная, что unity_StereoEyeIndex заполняется правильно, его можно использовать с уверенностью в том, что из unity_StereoScaleOffset будет взят правильный элемент.

XR-графика в Unity

Недостатки использования unity_StereoScaleOffset.

  • Доступно только для однопроходного рендеринга, поэтому необходимы вспомогательные методы.
  • При непосредственном использовании происходит переключение в режим многопроходного моноскопического рендеринга с появлением ошибок компиляции шейдера. Кроме того, при этом не учитывается RenderViewportScale.

Как убедиться в том, что текстурные массивы объявлены правильно, и что изображение берется из правильного слайса?

Решение

Используйте UNITY_DECLARE_SCREENSPACE_TEXTURE. Шейдерные языки обрабатывают текстурные массивы не так, как «нормальные» текстуры. Однопроходное дублирование и однопроходный рендеринг для Android используют текстурные массивы, поэтому необходимо следить, чтобы текстуры были объявлены правильно.

XR-графика в Unity

Как и в случае с текстурами удвоенной ширины, здесь необходимо правильно брать изображение для нужного глаза. Изображения размещаются в слайсах текстурного массива. Правильный слайс можно взять из unity_StereoEyeIndex использованием макроса UNITY_SAMPLE_SCREENSPACE_TEXTURE.

XR-графика в Unity

Управление RenderViewportScale: памятка

  • unity_StereoScaleOffset не обеспечивает никакой поддержки.
  • В большинстве эффектов постобработки используется CommandBuffer.Blit/Graphics.Blit, что означает, что в них можно использовать _MainTex_ST (и другие методы _ST) для поддержки RenderViewportScale.
  • Но эти методы недоступны в стеке постобработки версии v.2. Для обхода этого ограничения необходимо реализовать собственную поддержку, что вполне возможно, поскольку стек v.2 использует собственную инфраструктуру шейдеров, которую легко обойти. Вот как это можно сделать.

    На стороне шейдера:

    • создайте новую постоянную: объявите "float rvsGlobal" в xrLib.hlsl;
    • измените код TransformStereoScreenSpaceTex для использования rvsGlobal.

    На стороне скрипта:

    • сделайте привязку значения RenderViewportScale как глобального свойства из скрипта;
    • используйте Shader.SetGlobalFloat.

    XR-графика в Unity

Привязка координат

При обработке изображения с захватом соседних областей экранного пространства в шейдере необходимо следить, чтобы не было случайного захвата изображения для другого глаза в однопроходном рендеринге в текстуру с удвоенной шириной, или изображения, выходящего за пределы области RenderViewportScale. Здесь очень полезен метод UnityStereoClamp, который ограничивает координаты правильной областью текстуры.

XR-графика в Unity

Метод прост в использовании, но требует ручной проверки для поиска прилегающих областей.

Каждый раз, когда вы задаете смещение от координаты текстуры, полученной с помощью интерполяции, вам наверняка потребуется Unity.StereoClamp().

  • Знайте, что привязка в случае выбора одной и той же координаты приводит к нежелательным эффектам.
  • Не используйте этот метод в вершинных шейдерах.
Еще пара советов
  • Подумайте, стоит ли использовать выбранный вами эффект постобработки для XR.
    • Most temporal effects add blurriness and latency
    • Simulating depth of field, a property of physical cameras, in a headset, which is simulating the human eye, will most likely cause nausea.
  • Если вы используете многопроходный рендеринг и журнал текстур, то для каждого глаза необходим отдельный журнал текстур.
    • Singular history set could/will lead to sharing history between eyes!
    • Works automatically with single-pass
  • Если возникли неполадки, то попробуйте другие режимы стереоскопического рендеринга.
    • Nature of artifact can provide hints on where things might be going wrong.
Дополнительные ресурсы

Мы очень хотим знать, нравится ли вам наш контент.

Да, хочу больше Нет, могло быть и лучше
Согласен

We use cookies to ensure that we give you the best experience on our website. Visit our cookie policy page for more information.