Впервые на русском: Документация Oculus для разработчиков. Руководство ПК SDK для разработчика. Расширенная настройка рендеринга

Продолжаем публикацию эксклюзивной информации для разработчиков от Oculus. Новый раздел посвящён расширенной настройке рендеринга.

  1. Документация Oculus для разработчиков:
    1. Введение в рекомендации
    2. Бинокулярное зрение, стереоскопическая визуализация и индикаторы глубины
    3. Поле зрения и масштаб
    4. Методы рендеринга
    5. Движение
    6. Отслеживание
    7. «Болезнь симуляции»
    8. Пользовательский интерфейс
    9. Пользовательский ввод и навигация
    10. Заключение
    11. Введение в SDK для ПК
    12. Руководство разработчика
    13. Руководство по началу работы с ПК SDK
    14. Руководство ПК SDK для разработчика. Интеграция LibOVR
    15. Инициализация и перечень сенсоров
    16. Рендеринг в Oculus Rift
    17. Расширенная настройка рендеринга
    18. Управление связью с виртуальной реальностью
    19. Система безопасности Oculus Guardian
    20. Аудио Oculus Rift
    21. Контроллеры Oculus Touch
    22. Дополнительная информация об Oculus Touch
    23. Примеры использования SDK и геймпада
    24. Оптимизация вашего приложения
    25. Подключение контроллеров Oculus Touch
    26. Асинхронный SpaceWarp
    27. Дополнительная информация о подключении контроллеров Oculus Touch
    28. Справочная информация

Чтобы оставаться в курсе новостей о виртуальной реальности, подписывайтесь на наш Telegram!

По умолчанию SDK генерирует значения конфигурации, которые оптимизируются для необходимого качества визуализации.

Он также обеспечивает определённую степень гибкости. Например, вы можете вносить изменения при создании целевых текстур рендеринга.

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

Борьба между графическим API и гранулярностью целевого оборудования

SDK разработан с учётом того, что вы можете захотеть использовать вашу видеопамять как можно более продуктивно и что вы можете создать именно то, что вам нужно.

Однако реальные видеокарты и графические API-интерфейсы имеют ограничения по размеру (все они имеют максимальный размер, а некоторые также имеют минимальный размер). У них также могут быть ограничения гранулярности, например, когда есть только возможность создавать цели рендеринга, которые кратны 32 пикселям или имеют ограничения на возможные пропорции. Как разработчик приложений вы также можете установить дополнительные ограничения для того, чтобы избежать использования слишком большого количества графической памяти.

В дополнение к вышесказанному отметим, что размер фактической целевой поверхности рендеринга в памяти не обязательно должен быть того же размера, что и отображаемая часть. Последний может быть немного меньше. Однако, поскольку он задан как экран вида, он обычно не имеет ограничений в гранулярности. Тем не менее, когда вы устанавливаете цель рендеринга как текстуру, он использует всю поверхность, поэтому UV-координаты следует скорректировать с учётом разницы между размером рендеринга и размером поверхности, на которой она находится. API сделает это за вас, но вам нужно сообщить ему соответствующую информацию.

Следующий код демонстрирует двухступенчатый подход для настройки целевого разрешения параметров рендеринга. Сначала код вызывает ovr_GetFovTextureSize для того, чтобы вычислить идеальный размер цели рендеринга. Затем вызывается графическая библиотека для создания цели рендеринга требуемого разрешения. В целом, из-за особенностей платформы и оборудования итоговый размер текстуры может отличаться от запрошенного.

 

// Get recommended left and right eye render target sizes.

Sizei recommenedTex0Size = ovr_GetFovTextureSize(session, ovrEye_Left,

session->DefaultEyeFov[0], pixelsPerDisplayPixel);

Sizei recommenedTex1Size = ovr_GetFovTextureSize(session, ovrEye_Right,

session->DefaultEyeFov[1], pixelsPerDisplayPixel);

 

// Determine dimensions to fit into a single render target.

Sizei renderTargetSize;

renderTargetSize.w = recommenedTex0Size.w + recommenedTex1Size.w;

renderTargetSize.h = max ( recommenedTex0Size.h, recommenedTex1Size.h );

 

// Create texture.

pRendertargetTexture = pRender->CreateTexture(renderTargetSize.w, renderTargetSize.h);

 

// The actual RT size may be different due to HW limits.

renderTargetSize.w = pRendertargetTexture->GetWidth();

renderTargetSize.h = pRendertargetTexture->GetHeight();

 

// Initialize eye rendering information.

// The viewport sizes are re-computed in case RenderTargetSize changed due to HW limitations.

ovrFovPort eyeFov[2] = { session->DefaultEyeFov[0], session->DefaultEyeFov[1] };

 

EyeRenderViewport[0].Pos  = Vector2i(0,0);

EyeRenderViewport[0].Size = Sizei(renderTargetSize.w / 2, renderTargetSize.h);

EyeRenderViewport[1].Pos  = Vector2i((renderTargetSize.w + 1) / 2, 0);

EyeRenderViewport[1].Size = EyeRenderViewport[0].Size;

 

Эти данные передаются в ovr_SubmitFrame как часть описания слоя.

Вы можете выбрать размер целевой текстуры рендеринга, а также экраны просмотра влево и вправо в том случае, если хотите, чтобы эти значения указывались при вызове ovr_SubmitFrameusing ovrTexture. Однако использование ovr_GetFovTextureSize гарантирует, что вы распределите оптимальный размер для конкретного используемого шлема виртуальной реальности. В следующих разделах описывается, как изменить конфигурации по умолчанию для того, чтобы добиться компромисса между качеством и производительностью. Следует также отметить, что API поддерживает использование разных целей рендеринга для каждого глаза, если это требуется для вашего движка (хотя использование одной цели рендеринга, по-видимому, будет лучше работать, поскольку это уменьшит контекстные переключатели). OculusWorldDemo позволяет переключаться между использованием одной комбинированной цели рендеринга и отдельной для каждого глаза путём перехода к меню настроек (нажатием клавиши Tab) и выбора опции Share RenderTarget.

Принуждение к симметричному полю зрения

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

Это объясняется тем, что люди, так же как и Oculus Rift, имеют более широкое поле зрения при взгляде наружу. Когда вы смотрите внутрь, ваш нос вам мешает. Мы также лучше видим, когда смотрим вниз, чем когда поднимаем глаза. По тем же причинам взгляд Oculus Rift не симметричен. Он управляется формой линзы, различными битами пластика и краями экрана. Точные нюансы зависят от формы вашего лица, вашего расстояния меду зрачками, а также от того, где именно вы размещаете Oculus Rift на вашем лице; всё это настраивается в инструментах конфигурации и сохраняется в профиле пользователя. Всё это означает, что почти ни у кого нет всех четырех рёбер поля зрения, установленных на один и тот же угол, потому усечённая область будет смещена. Кроме того, большинство людей не будут иметь одинаковые поля зрения для обоих глаз. Они будут близки, но редко идентичны.

Например, на первом поколении гарнитуры DK1 левый глаз автора имеет следующее поле зрения:

  • 53.6 градусов вверх
  • 58.9 градусов  вниз
  • 50.3 градусов внутрь (к носу)
  • 58.7 градусов  наружу (от носа)

В коде и документации они называются «полууглы», потому что традиционно поле зрения выражается как полный угол от края до края. В этом примере общее горизонтальное поле зрения составляет 50,3 + 58,7 = 109,0 градусов, а общее вертикальное поле обзора – 53,6 + 58,9 = 112,5 градусов.

Рекомендуемый и максимальный поля зрения могут быть доступны из шлема виртуальной реальности, как показано ниже:

 

ovrFovPort defaultLeftFOV = session->DefaultEyeFov[ovrEye_Left];

 

ovrFovPort maxLeftFOV = session->MaxEyeFov[ovrEye_Left];

 

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

Значения по умолчанию обеспечивают хороший пользовательский интерфейс без излишней дополнительной загрузки графического процессора. Если ваше приложение не потребляет значительных ресурсов графического процессора, вы можете использовать максимальные настройки поля зрения для того, чтобы снизить зависимость от точности настроек профиля. Вы можете также внедрить ползунок на панели управления приложения, который позволяет пользователям выбирать интерполированные настройки поля зрения между значением по умолчанию и максимумом. Но если ваше приложение сильно потребляет ресурсы графического процессора, вы можете уменьшить поле зрения ниже значения по умолчанию, как описано в разделе «Улучшение производительности за счёт уменьшения поля зрения».

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

 

ovrFovPort fov;

 

// Determine fov.

 

ovrMatrix4f projMatrix = ovrMatrix4f_Projection(fov, znear, zfar, 0);

 

Верхний и нижний края поля зрения являются общими для левого и правого краёв при просмотре монитора компьютера. Это обычно называют «соотношением сторон» на дисплее, и очень мало дисплеев при этом являются квадратными. Однако некоторые графические процессоры не поддерживают внецентровые срезы. Чтобы быть совместимым с этими движками, вам нужно будет изменить значения поля зрения, которые указаны в структуре ovrHmdDesc. В целом, лучше увеличивать края, чем сокращать их. Это накладывает немного больше нагрузки на графический процессор, но зато даст пользователям полный эффект погружения, даже если они не смогут увидеть некоторые из отображаемых пикселей.

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

 

ovrFovPort fovLeft = session->DefaultEyeFov[ovrEye_Left];

ovrFovPort fovRight = session->DefaultEyeFov[ovrEye_Right];

 

ovrFovPort fovMax = FovPort::Max(fovLeft, fovRight);

float combinedTanHalfFovHorizontal = max ( fovMax.LeftTan, fovMax.RightTan );

float combinedTanHalfFovVertical = max ( fovMax.UpTan, fovMax.DownTan );

 

ovrFovPort fovBoth;

fovBoth.LeftTan = fovBoth.RightTan = combinedTanHalfFovHorizontal;

fovBoth.UpTan = fovBoth.DownTan = combinedTanHalfFovVertical;

 

 

// Create render target.

Sizei recommenedTex0Size = ovr_GetFovTextureSize(session, ovrEye_Left,

fovBoth, pixelsPerDisplayPixel);

Sizei recommenedTex1Size = ovr_GetFovTextureSize(session, ovrEye_Right,

fovBoth, pixelsPerDisplayPixel);

 

 

 

// Initialize rendering info.

ovrFovPort eyeFov[2];

eyeFov[0]                       = fovBoth;

eyeFov[1]                       = fovBoth;

 

 

 

// Compute the parameters to feed to the rendering engine.

// In this case we are assuming it wants a horizontal FOV and an aspect ratio.

float horizontalFullFovInRadians = 2.0f * atanf ( combinedTanHalfFovHorizontal );

float aspectRatio = combinedTanHalfFovHorizontal / combinedTanHalfFovVertical;

 

GraphicsEngineSetFovAndAspect ( horizontalFullFovInRadians, aspectRatio );

 

Примечание: Перед созданием объектов рендеринга вам необходимо определить поле зрения, поскольку поле зрения влияет на размер рекомендованной цели рендеринга, необходимой для определённого качества.

Повышение производительности за счет снижения плотности пикселей

DK1 имеет разрешение в 1280×800 пикселей, разделённое между двумя глазами. Однако из-за широкого поля зрения в Oculus Rift и способа реализации перспективы проекции, размер промежуточной цели рендеринга, необходимой для соответствия собственному разрешению в центре дисплея, значительно выше.

Например, для достижения 1:1 пиксельного соответствия в центре экрана для параметров поля зрения автора на DK1 требуется гораздо больший размер объекта рендеринга, который имеет размер в 2000×1056 пикселей.

Даже если современные видеокарты могут отображать это разрешение на требуемой частоте в 60 Гц, будущие шлемы виртуальной реальности могут иметь значительно более высокие разрешения. Для виртуальной реальности падение частоты ниже показателя в 60 Гц обеспечивает отвратительный пользовательский опыт; всегда лучше уменьшить разрешение для поддержания частоты кадров. Это подходит для пользователя с монитором высокого разрешения в 2560×1600. Очень немногие 3D-приложения могут работать на этом родном разрешении на полной скорости, поэтому большинство из них позволяет пользователю выбирать меньшее разрешение, которое монитор расширяет до заполнения экрана.

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

Один из способов решить это – разрешить пользователю настраивать разрешение с помощью выбора разрешения. Однако фактическое разрешение цели рендеринга зависит от конфигурации пользователя, а не от стандартного аппаратного параметра. Это означает, что «родное» разрешение отличается для разных людей. Кроме того, представление разрешений выше, чем физическое аппаратное разрешение, что может смутить некоторых пользователей. Возможно, они не понимают, что выбор 1280×800 – это значительное снижение качества, хотя это разрешение, сообщаемое аппаратным обеспечением.

Лучшим вариантом является изменение значения pixelsPerDisplayPixel, передаваемого функции ovr_GetFovTextureSize. Это также может быть основано на ползунке, представленном в настройках рендеринга приложений. Она определяет относительный размер целевых пикселей рендеринга по мере их отображения на пиксели в центре поверхности отображения. Например, значение 0.5 уменьшило бы целевой размер рендеринга с 2000×1056 до 1000×528 пикселей, что может позволить средним компьютерам поддерживать графические карты на частоте в 60 Гц.

 

float pixelsPerDisplayPixel = GetPixelsPerDisplayFromApplicationSettings();

 

Sizei recommenedTexSize = ovr_GetFovTextureSize(session, ovrEye_Left,  fovLeft,

pixelsPerDisplayPixel);

 

И пусть вы можете установить значение параметра больше 1.0 для того, чтобы создать промежуточную цель рендеринга с высоким разрешением, Oculus не выявил какого-либо полезного повышения качества и это в конце концов стоит высокой производительности.

OculusWorldDemo позволяет поэкспериментировать с изменением плотности пикселей целевого изображения. Перейдите в меню настроек (путём нажатия клавишу Tab) и выберите Pixel Density («Плотность пикселей»). Нажимайте клавиши со стрелками вверх и вниз для того, чтобы отрегулировать плотность пикселей в центре проекции глаза. Значение 1.0 устанавливает плотность пикселей целевого изображения на поверхность отображения 1:1 в этой точке на дисплее. Значение 0.5 означает, что плотность целевых пикселей рендеринга равна половине поверхности дисплея. Кроме того, вы можете выбрать динамическое масштабирование масштабирования, которое автоматически установит плотность пикселей между 0 и 1.

Повышение производительности за счёт уменьшения поля зрения

В дополнение к уменьшению числа пикселей в промежуточной цели рендеринга вы можете увеличить производительность путём уменьшения поля зрения, на которое растягиваются пиксели.

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

Сокращение поля зрения, установленного игроком – очень болезненный выбор. Одним из ключевых событий виртуальной реальности остаётся погружение в моделируемый мир, и большая часть этого – широкое поле зрения. Потеря этого аспекта – это не то, что мы бы рекомендовали. Однако если вы уже пожертвовали разрешением так, как только можете, и приложение всё ещё не работает на частоте в 60 Гц на компьютере пользователя, это вариант можно оставить на крайний случай.

Мы рекомендуем предоставить игрокам ползунок Maximum FOV («Максимальное поле зрения»), который определяет четыре края поля зрения каждого глаза.

 

ovrFovPort defaultFovLeft = session->DefaultEyeFov[ovrEye_Left];

ovrFovPort defaultFovRight = session->DefaultEyeFov[ovrEye_Right];

 

float maxFovAngle = …get value from game settings panel…;

float maxTanHalfFovAngle = tanf ( DegreeToRad ( 0.5f * maxFovAngle ) );

 

ovrFovPort newFovLeft  = FovPort::Min(defaultFovLeft, FovPort(maxTanHalfFovAngle));

ovrFovPort newFovRight = FovPort::Min(defaultFovRight, FovPort(maxTanHalfFovAngle));

 

 

// Create render target.

Sizei recommenedTex0Size = ovr_GetFovTextureSize(session, ovrEye_Left  newFovLeft, pixelsPerDisplayPixel);

Sizei recommenedTex1Size = ovr_GetFovTextureSize(session, ovrEye_Right, newFovRight, pixelsPerDisplayPixel);

 

 

 

// Initialize rendering info.

ovrFovPort eyeFov[2];

eyeFov[0]                       = newFovLeft;

eyeFov[1]                       = newFovRight;

 

 

 

// Determine projection matrices.

ovrMatrix4f projMatrixLeft = ovrMatrix4f_Projection(newFovLeft, znear, zfar, 0);

ovrMatrix4f projMatrixRight = ovrMatrix4f_Projection(newFovRight, znear, zfar, 0);

 

Может быть интересным поэкспериментировать с неквадратными полями зрения. Например, значительно увеличивая диапазоны вверх и вниз (например, 70 градусов поля зрения), сохраняя при этом полное горизонтальное поле зрения для синемаскопического ощущения.

OculusWorldDemo позволяет поэкспериментировать с уменьшением значения поля зрения ниже значения по умолчанию. Для этого перейдите в меню настроек (путём нажатия клавишу Tab) и выберите значение Max FOV. Нажатие стрелок вверх и вниз позволяет изменить максимальный угол в градусах.

Улучшение производительности путем рендеринга в моно

Значительная цена стерео рендеринга заключается в представлении двух видов: по одному для каждого глаза.

Для некоторых приложений стереоскопический аспект может быть не особенно важным, и монокулярный вид может быть приемлемым в обмен на некоторую производительность. В других случаях некоторые пользователи могут чувствовать напряжение глаз от стереовидения и захотят переключиться на монокулярный. Тем не менее, они по-прежнему хотят носить шлем виртуальной реальности, поскольку он даёт им высокое поле зрения и отслеживание головы.

OculusWorldDemo позволяет пользователю переключать монорежим путём нажатия клавиши F7.

Чтобы превратить рендеринг в моно, ваш код должен иметь следующие изменения:

  1. Установите поле зрения на максимальное симметричное поле зрения на оба глаза.
  2. Вызовите ovhHmd_GetFovTextureSize с этим полем для определения рекомендуемого размера цели рендеринга.
  3. Настройте оба глаза для использования одной и той же цели визуализации и того же экрана просмотра при вызове ovr_SubmitFrame.
  4. Отрендерьте сцены один раз для общей цели рендеринга.

Это объединяет поле зрения левого и правого глаза в один промежуточный рендер. Этот рендеринг по-прежнему искажается дважды, один раз на глаз, потому что объективы не находятся точно перед глазами пользователя. Тем не менее, это всё же значительное увеличение производительности.

Установка виртуального межзрачкового расстояния на ноль означает, что всё будет казаться гигантским и бесконечно далеким, и, конечно же, пользователь потеряет чувство глубины в сцене.

Примечание: Важно масштабировать виртуальное межзрачковое расстояние и виртуальное движение головы, поэтому, если виртуальное межзрачковое расстояние установлено на ноль, все виртуальные движения головы из-за движения шеи также будут устранены. К сожалению, это приводит к потере большей части индикаторов глубины из-за параллакса. Но если движение головы и межзрачковое расстояние не совпадают, это может вызвать значительную дезориентацию и дискомфорт. Экспериментируйте с осторожностью!

Защищённый контент

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

Для того, чтобы использовать функцию защищённого контента, настройте приложение для создания одного или нескольких объектов ovrTextureSwapChain с указанной переменной ovrTextureMisc_ProtectedContent. Любое представление, которое ссылается на защищённую своповую цепочку, считается защищённым кдром; любые защищённые кадры компонуются и отображаются в шлеме виртуальной реальности, но не реплицируются в зеркала или не остаются доступными для API буфера захвата.

Если шлем виртуальной реальности несовместим с HDCP, API для создания цепи обмена текстурой не будет работать с ovrError_ContentProtectionNotAvailable. Если текстуры могут быть созданы (HDCP-совместимый шлем виртуальной реальности), но ссылка будет разбита позже, следующий вызов ovrSubmitFrame, который ссылается на защищённые цепи замены текстур, завершится с ошибкой ovrError_ContentProtectionNotAvailable. Настройте приложение для ответа в соответствии с вашими требованиями. Например, вы можете отправить следующий кадр без защищённых цепочек обмена, но с более низким качеством, которое не требует защиты. Или вы можете остановить воспроизведение и отобразить ошибку или предупреждение для пользователя.

Примечание: Поскольку окно зеркала находится под управлением приложения, если ваше приложение не использует текстуру зеркала, ваше приложение может отобразить что-то в окне предварительного просмотра. Убедитесь, что ваше приложение не отображает защищённый контент. Так как неизвестно точно, что поверхности защищены операционной системой, они будут отображаться как правило внутри приложения, которое их создало.

Чтобы включить защищённый контент, укажите:

 

ovrTextureMisc_ProtectedContent

flag similarly to the following:

ovrTextureSwapChainDesc desc = {};

desc.Type = ovrTexture_2D;

desc.ArraySize = 1;

desc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB;

desc.Width = sizeW;

desc.Height = sizeH;

desc.MipLevels = 1;

desc.SampleCount = 1;

desc.MiscFlags = ovrTextureMisc_DX_Typeless | ovrTextureMisc_ProtectedContent;

desc.BindFlags = ovrTextureBind_DX_RenderTarget;

desc.StaticImage = ovrFalse;

 

ovrResult result = ovr_CreateTextureSwapChainDX(session, Device, &desc, &TextureChain);

 

Примечание: Данный материал представлен для ознакомления, при перепечатывании ссылка на оригинал обязательна. Если вы хотите принять участие в помощи проекту, пишите на editor@vrgeek.ru

Если вы разработчик и вы хотите продолжить свою карьеру в лучших компаниях России, пишите на editor@vrgeek.ru с пометкой «Работа мечты», и мы поможем вам с этим.