Настройка функций Depth-Stencil

В этом разделе приводится пошаговая инструкция по настройке буфера трафарета глубины и рассматривается состояние трафарета глубины для стадии слияния вывода.

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

Создание ресурса Depth-Stencil

Создайте буфер трафарета глубины с помощью ресурса текстуры.

ID3D11Texture2D* pDepthStencil = NULL;
D3D11_TEXTURE2D_DESC descDepth;
descDepth.Width = backBufferSurfaceDesc.Width;
descDepth.Height = backBufferSurfaceDesc.Height;
descDepth.MipLevels = 1;
descDepth.ArraySize = 1;
descDepth.Format = pDeviceSettings->d3d11.AutoDepthStencilFormat;
descDepth.SampleDesc.Count = 1;
descDepth.SampleDesc.Quality = 0;
descDepth.Usage = D3D11_USAGE_DEFAULT;
descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL;
descDepth.CPUAccessFlags = 0;
descDepth.MiscFlags = 0;
hr = pd3dDevice->CreateTexture2D( &descDepth, NULL, &pDepthStencil );

Создание состояния трафарета глубины

Состояние трафарета глубины сообщает стадии средства слияния вывода способ выполнения проверки трафарета глубины. Проверка трафарета глубины позволяет определить, нужно ли рисовать тот или иной пиксель.

D3D11_DEPTH_STENCIL_DESC dsDesc;

// Depth test parameters
dsDesc.DepthEnable = true;
dsDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
dsDesc.DepthFunc = D3D11_COMPARISON_LESS;

// Stencil test parameters
dsDesc.StencilEnable = true;
dsDesc.StencilReadMask = 0xFF;
dsDesc.StencilWriteMask = 0xFF;

// Stencil operations if pixel is front-facing
dsDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
dsDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR;
dsDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
dsDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

// Stencil operations if pixel is back-facing
dsDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;
dsDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR;
dsDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
dsDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;

// Create depth stencil state
ID3D11DepthStencilState * pDSState;
pd3dDevice->CreateDepthStencilState(&dsDesc, &pDSState);

DepthEnable и StencilEnable позволяют (и отключают) тестирование глубины и набора элементов. Задайте для параметра DepthEnable значение FALSE , чтобы отключить тестирование глубины и запретить запись в буфер глубины. Задайте для параметра StencilEnable значение FALSE , чтобы отключить тестирование трафарета и предотвратить запись в буфер трафарета (если depthEnable имеет значение FALSE , а StencilEnable — TRUE, проверка глубины всегда проходит операцию набора элементов).

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

Привязка данных трафарета глубины к стадии средства слияния вывода

Привязка состояния трафарета глубины.

// Bind depth stencil state
pDevice->OMSetDepthStencilState(pDSState, 1);

Привязка ресурса трафарета глубины с использованием представления.

D3D11_DEPTH_STENCIL_VIEW_DESC descDSV;
descDSV.Format = DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
descDSV.Texture2D.MipSlice = 0;

// Create the depth stencil view
ID3D11DepthStencilView* pDSV;
hr = pd3dDevice->CreateDepthStencilView( pDepthStencil, // Depth stencil texture
                                         &descDSV, // Depth stencil desc
                                         &pDSV );  // [out] Depth stencil view

// Bind the depth stencil view
pd3dDeviceContext->OMSetRenderTargets( 1,          // One rendertarget view
                                &pRTV,      // Render target view, created earlier
                                pDSV );     // Depth stencil view for the render target

Массив представлений, предназначенных для отрисовки, может передаваться в ID3D11DeviceContext::OMSetRenderTargets, однако все эти представления целевых объектов отрисовки будут соответствовать одному представлению трафарета глубины. Массив целевых объектов отрисовки в Direct3D 11 — это функция, которая позволяет приложению одновременно выполнять отрисовку в нескольких целевых объектах отрисовки на уровне примитива. Массивы целевых объектов отрисовки обеспечивают повышенную производительность по сравнению с отдельными целевыми объектами отрисовки с несколькими вызовами ID3D11DeviceContext::OMSetRenderTargets (по сути, метод, используемый в Direct3D 9).

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

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

  • Одновременно можно связать до 8 однобуферных прорисовок.
  • Все целевые объекты отрисовки должны иметь одинаковый размер во всех измерениях (ширина и высота и глубина для трехмерных объектов или размер массива для *Типов массивов).
  • Каждая однобуферная прорисовка может иметь свой формат данных.
  • Маски записи контролируют, какие данные записываются в однобуферную прорисовку. Маски записи вывода контролируют, какие данные записываются в однобуферные прорисовки на уровне однобуферных прорисовок и компонентов.

Сложные техники работы с трафаретами

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

Компоновка

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

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

Переводные картинки

В приложениях Direct3D пользователи с помощью переводных картинок контролируют, какие пиксели из определенного изображения-примитива рисуются на поверхность однобуферной прорисовки. Приложения применяют переводные картинки к изображениям примитивов для правильной отрисовки многоугольников.

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

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

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

Структуры и силуэты

Буфер трафарета можно использовать и для более абстрактных эффектов, таких как создание структур и силуэтов.

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

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

Двусторонний трафарет

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

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

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

Пример реализации двустороннего набора элементов можно найти в примере ShadowVolume10.

Считывание буфера трафарета глубины как текстуры

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

Чтобы создать буфер трафарета глубины, который можно использовать как ресурс трафарета глубины, так и ресурс шейдера, необходимо ввести несколько изменений в образец кода в разделе Создание ресурса Depth-Stencil .

  • Ресурс трафарета глубины должен иметь нетипизированный формат, например DXGI_FORMAT_R32_TYPELESS.

    descDepth.Format = DXGI_FORMAT_R32_TYPELESS;
    
  • Ресурс трафарета глубины должен использовать флаги D3D10_BIND_DEPTH_STENCIL и D3D10_BIND_SHADER_RESOURCE привязки.

    descDepth.BindFlags = D3D10_BIND_DEPTH_STENCIL | D3D10_BIND_SHADER_RESOURCE;
    

Кроме того, необходимо создать представление ресурсов шейдера для буфера глубины с помощью структуры D3D11_SHADER_RESOURCE_VIEW_DESC и ID3D11Device::CreateShaderResourceView. Представление ресурсов шейдера будет использовать типизированный формат, например DXGI_FORMAT_R32_FLOAT , который эквивалентен бестипному формату, указанному при создании ресурса трафарета глубины.

В первом проходе отрисовки буфер глубины привязывается, как описано в разделе Привязка данных Depth-Stencil к этапу OM . Обратите внимание, что формат, передаваемый в D3D11_DEPTH_STENCIL_VIEW_DESC. Формат будет использовать типизированный формат, например DXGI_FORMAT_D32_FLOAT. После первого прохода отрисовки буфер глубины будет содержать значения глубины для сцены.

Во втором проходе отрисовки функция ID3D11DeviceContext::OMSetRenderTargets используется для установки представления трафарета глубины в значение NULL или другого ресурса трафарета глубины, а представление ресурсов шейдера передается в шейдер с помощью ID3D11EffectShaderResourceVariable::SetResource. Это позволяет шейдеру искать значения глубины, вычисленные в первом проходе отрисовки. Обратите внимание, что преобразование необходимо будет применить для получения значений глубины, если точка зрения первого прохода отрисовки отличается от точки зрения второго прохода отрисовки. Например, если используется метод сопоставления теней, первый проход отрисовки будет осуществляться с точки зрения источника света, а второй проход отрисовки — с точки зрения средства просмотра.

Стадия средства слияния вывода

Этапы конвейера (Direct3D 10)