пятница, 6 января 2012 г.

Stereo Texture

Пока мы все вынуждены ждать Direct3D 11.1, где появится нэйтивная поддержка стереоскопии, под DirectX 9 для всяких этих ваших ассассинов кридов приходится использовать различные хаки, чтобы улучшить поддержку стерео.

Механизм, через который работает 3D Vision на картах NVidia, довольно прост - драйвер добавляет footer в каждый вершинный шейдер таким образом, чтобы позиция вершины в clip space различалась для правого и левого кадра (см. NVIDIA 3D Vision Automatic Best Practices Guide). Таким образом стерео автоматически работает в большинстве трёхмерных игр и даже тех, которые изначально под него не разрабатывались.

Проблемы начинаются в случаях, если при рендеринге использовались какие-то нечестные техники или в случае различных deferred подходов - картинка обычно выглядит неестественно, если не сказать хуже. 3D Vision работает на уровне геометрического пайплайна, во всех остальных случаях нужно что-то делать руками самостоятельно. Вот недавно по работе мне было поручено исправить некоторые огрехи нашего движка при работе в стерео-режиме. Как выяснилось, одна из техник использовала механизм отложенного рендеринга партиклов, при котором цвет и дополнительная информация сначала рисуются в промежуточный render target, а потом изображение из него "вклеивается" в основной кадр. Понятно, что 3D Vision проплывает в этом случае по левому борту, и необходимы поправки уже в пиксельном шейдере. Но вот загвоздка - стандартными средствами Direct3D выяснить какой из кадров - правый или левый, рисуется в данный момент - в шейдере невозможно, а без возможности их различать нет и возможности что-либо изменить.

Начав искать в Сети методы решения подобных проблем, я обнаружил, что NVidia предоставляет пример StereoIssues в NVIDIA Graphics SDK 11, в котором имеется код по созданию специальной "стереотекстуры" (файл nvstereo.h). Стереотекстура позволяет семплировать в шейдере данные, которые будут различаться для левого и правого кадра, так что, например, можно вычислить смещение для каждого кадра по формуле:

pos.x += separation * (pos.w - convergence)

Это очередной хак на уровне D3D9/10 API, и для его включения необходимо разместить fourcc 'NV3D' в последней строке staging текстуры формата RGBA32F, данные из которой впоследствии необходимо скопировать в обычную текстуру посредством UpdateSurface()/CopySubresourceRegion(). При копировании драйвер разместит данные для левого кадра в целевой текстуре, а для правого (как я понимаю) создаст shadow copy и разместит в ней данные для правого кадра, взяв их по определённому адресу (см. раздел 3D Video в GDC09 3DVision The In and Out). Если всё сделать в том порядке, который предлагается в примере, то шейдер действительно будет семплировать различные данные по одним и тем же текстурным координатам. Ну а дальше уже дело техники...

Одним из подводных камней, о который я споткнулся, является то, что недостаточно просто при старте скопировать данные в стереотекстуру и в дальнейшем пользоваться ею. Всё дело в том, что 3D Vision не включается с первым же кадром: драйвер сначала собирает некоторую информацию о первых кадрах, и только потом включается стереорежим. После его включения и нужно создавать стереотекстуру, а лучше всего проверять  переключение режимов функцией NvAPI_Stereo_IsActivated() из NvAPI, и обновлять её после изменения результата. А в примере StereoIssues идут ещё дальше: т. к. пользователь может динамически изменять такие параметры 3D Vision как EyeSeparation и Convergence, то необходимо отслеживать изменения этих параметров и своевременно обновлять данные в стереотекстуре.

4 комментария:

  1. Тоже намучися помню с этим.
    Кстати ты функцию NvAPI_Stereo_IsActivated вызываешь каждый кадр?
    Если да, то могу посоветовать глянуть на механизм windows messenges для того чтобы не вызывать ее каждый кадр, а только по требованию. Потому что у нас например вызов этой функции приводил к жестким тормозам.( там видимо идет обращение к драйверу, который в свою очередь уходит в защищенный режим т.е. Свитчит контекст из юзер в кернел мод)

    ОтветитьУдалить
  2. Вызываю в каждом кадре :(
    Мы в основном работаем с дебажным билдом, final собирается отдельно скриптом, поэтому fps не особо и разницу заметить трудно. Но вообще то что ты описал было в XDDM, в WDDM всё по-другому, а т. к. 3D Vision работает только в Vista/7, то причина тормозов должна быть в чём-то другом. Вообще я считал что функция просто проверяет флажок в драйвере и всё.

    ОтветитьУдалить
  3. Да, сделали проверку значений раз в 20 кадров - думаю, это самое простое решение.

    ОтветитьУдалить
  4. Ха-ха, теперь смешно после предыдущего моего комментария это читать :-)

    ОтветитьУдалить