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

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

Необходимые ресурсы

Для прорисовки карты глубины для теневых томов требуются следующие аппаратно-зависимые ресурсы Direct3D:

  • Ресурс (буфер) для карты глубины
  • Представление набора элементов глубины и представление ресурсов шейдера для ресурса
  • Объект сравнения состояний дискретизатора
  • Буферы констант для матриц освещения точки обзора
  • Окно просмотра для прорисовки карты теней (обычно прямоугольное окно просмотра)
  • Объект состояния прорисовки для включения выбора передней поверхности
  • Объект состояния прорисовки (если он уже не используется) также потребуется вам для возвращения к выбору задней поверхности.

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

Проверка поддержки функции

Перед созданием карты глубины вызовите метод CheckFeatureSupport на устройстве Direct3D, запросите D3D11_FEATURE_D3D9_SHADOW_SUPPORT и укажите структуру D3D11_FEATURE_DATA_D3D9_SHADOW_SUPPORT .

D3D11_FEATURE_DATA_D3D9_SHADOW_SUPPORT isD3D9ShadowSupported;
ZeroMemory(&isD3D9ShadowSupported, sizeof(isD3D9ShadowSupported));
pD3DDevice->CheckFeatureSupport(
    D3D11_FEATURE_D3D9_SHADOW_SUPPORT,
    &isD3D9ShadowSupported,
    sizeof(D3D11_FEATURE_D3D9_SHADOW_SUPPORT)
    );

if (isD3D9ShadowSupported.SupportsDepthAsTextureWithLessEqualComparisonFilter)
{
    // Init shadow map resources

Если эта функция не поддерживается, не пытайтесь загружать шейдеры, скомпилированные для модели шейдера уровня 4, 9_x которые вызывают функции сравнения образцов. Во многих случаях отсутствие поддержки этой функции означает, что GPU — это устаревшее устройство, и его драйвер не обновлен для поддержки по крайней мере WDDM 1.2. Если устройство поддерживает по крайней мере уровень функций 10_0, вы можете загрузить пример шейдера сравнения, скомпилированный для модели шейдера 4_0.

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

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

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

D3D11_TEXTURE2D_DESC shadowMapDesc;
ZeroMemory(&shadowMapDesc, sizeof(D3D11_TEXTURE2D_DESC));
shadowMapDesc.Format = DXGI_FORMAT_R24G8_TYPELESS;
shadowMapDesc.MipLevels = 1;
shadowMapDesc.ArraySize = 1;
shadowMapDesc.SampleDesc.Count = 1;
shadowMapDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_DEPTH_STENCIL;
shadowMapDesc.Height = static_cast<UINT>(m_shadowMapDimension);
shadowMapDesc.Width = static_cast<UINT>(m_shadowMapDimension);

HRESULT hr = pD3DDevice->CreateTexture2D(
    &shadowMapDesc,
    nullptr,
    &m_shadowMap
    );

Затем создайте представления ресурсов. Задайте нулевое значение для MIP-среза в представлении трафарета глубины и значение 1 для уровней MIP в представлении ресурса шейдера. Оба имеют размер текстуры TEXTURE2D, и оба должны использовать соответствующий DXGI_FORMAT.

D3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc;
ZeroMemory(&depthStencilViewDesc, sizeof(D3D11_DEPTH_STENCIL_VIEW_DESC));
depthStencilViewDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
depthStencilViewDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
depthStencilViewDesc.Texture2D.MipSlice = 0;

D3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;
ZeroMemory(&shaderResourceViewDesc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));
shaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
shaderResourceViewDesc.Format = DXGI_FORMAT_R24_UNORM_X8_TYPELESS;
shaderResourceViewDesc.Texture2D.MipLevels = 1;

hr = pD3DDevice->CreateDepthStencilView(
    m_shadowMap.Get(),
    &depthStencilViewDesc,
    &m_shadowDepthView
    );

hr = pD3DDevice->CreateShaderResourceView(
    m_shadowMap.Get(),
    &shaderResourceViewDesc,
    &m_shadowResourceView
    );

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

Теперь создайте объект сравнения состояний дискретизатора. Уровень компонентов 9_1 поддерживает только D3D11_COMPARISON_LESS_EQUAL. Возможности фильтрации подробнее объясняются в документе Поддержка карт теней на разном оборудовании. Можно также выбрать точечную фильтрацию для ускорения работы карт теней.

Обратите внимание, что можно указать D3D11_TEXTURE_ADDRESS_BORDER адресный режим, и он будет работать на устройствах с уровнем компонентов 9_1. Это применимо к шейдерам, которые не проверяют, находится ли пиксель в конусе представления освещения, перед выполнением теста глубины. Указывая значения 0 или 1 для каждой границы, вы можете контролировать, проходят или не проходят пиксели вне конуса освещения тест на глубину, то есть освещены они или находятся в тени.

На уровне компонентов 9_1 необходимо задать следующие обязательные значения: Значение MinLOD равно нулю, MaxLODD3D11_FLOAT32_MAX, а MaxAnisotropy — нулю.

D3D11_SAMPLER_DESC comparisonSamplerDesc;
ZeroMemory(&comparisonSamplerDesc, sizeof(D3D11_SAMPLER_DESC));
comparisonSamplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_BORDER;
comparisonSamplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_BORDER;
comparisonSamplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_BORDER;
comparisonSamplerDesc.BorderColor[0] = 1.0f;
comparisonSamplerDesc.BorderColor[1] = 1.0f;
comparisonSamplerDesc.BorderColor[2] = 1.0f;
comparisonSamplerDesc.BorderColor[3] = 1.0f;
comparisonSamplerDesc.MinLOD = 0.f;
comparisonSamplerDesc.MaxLOD = D3D11_FLOAT32_MAX;
comparisonSamplerDesc.MipLODBias = 0.f;
comparisonSamplerDesc.MaxAnisotropy = 0;
comparisonSamplerDesc.ComparisonFunc = D3D11_COMPARISON_LESS_EQUAL;
comparisonSamplerDesc.Filter = D3D11_FILTER_COMPARISON_MIN_MAG_MIP_POINT;

// Point filtered shadows can be faster, and may be a good choice when
// rendering on hardware with lower feature levels. This sample has a
// UI option to enable/disable filtering so you can see the difference
// in quality and speed.

DX::ThrowIfFailed(
    pD3DDevice->CreateSamplerState(
        &comparisonSamplerDesc,
        &m_comparisonSampler_point
        )
    );

Создание состояний прорисовки

Теперь создайте состояние прорисовки, которое можно использовать для выбора передней поверхности. Обратите внимание, что для устройств уровня компонентов 9_1 требуется параметр DepthClipEnable , равный true.

D3D11_RASTERIZER_DESC drawingRenderStateDesc;
ZeroMemory(&drawingRenderStateDesc, sizeof(D3D11_RASTERIZER_DESC));
drawingRenderStateDesc.CullMode = D3D11_CULL_BACK;
drawingRenderStateDesc.FillMode = D3D11_FILL_SOLID;
drawingRenderStateDesc.DepthClipEnable = true; // Feature level 9_1 requires DepthClipEnable == true
DX::ThrowIfFailed(
    pD3DDevice->CreateRasterizerState(
        &drawingRenderStateDesc,
        &m_drawingRenderState
        )
    );

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

D3D11_RASTERIZER_DESC shadowRenderStateDesc;
ZeroMemory(&shadowRenderStateDesc, sizeof(D3D11_RASTERIZER_DESC));
shadowRenderStateDesc.CullMode = D3D11_CULL_FRONT;
shadowRenderStateDesc.FillMode = D3D11_FILL_SOLID;
shadowRenderStateDesc.DepthClipEnable = true;

DX::ThrowIfFailed(
    pD3DDevice->CreateRasterizerState(
        &shadowRenderStateDesc,
        &m_shadowRenderState
        )
    );

Создание буферов констант

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

DX::ThrowIfFailed(
    m_deviceResources->GetD3DDevice()->CreateBuffer(
        &viewProjectionConstantBufferDesc,
        nullptr,
        &m_lightViewProjectionBuffer
        )
    );

Заполните данные буфера констант. Обновляйте буферы констант один раз при инициализации и затем снова, если значения освещения были изменены по сравнению с предыдущим кадром.

{
    XMMATRIX lightPerspectiveMatrix = XMMatrixPerspectiveFovRH(
        XM_PIDIV2,
        1.0f,
        12.f,
        50.f
        );

    XMStoreFloat4x4(
        &m_lightViewProjectionBufferData.projection,
        XMMatrixTranspose(lightPerspectiveMatrix)
        );

    // Point light at (20, 15, 20), pointed at the origin. POV up-vector is along the y-axis.
    static const XMVECTORF32 eye = { 20.0f, 15.0f, 20.0f, 0.0f };
    static const XMVECTORF32 at = { 0.0f, 0.0f, 0.0f, 0.0f };
    static const XMVECTORF32 up = { 0.0f, 1.0f, 0.0f, 0.0f };

    XMStoreFloat4x4(
        &m_lightViewProjectionBufferData.view,
        XMMatrixTranspose(XMMatrixLookAtRH(eye, at, up))
        );

    // Store the light position to help calculate the shadow offset.
    XMStoreFloat4(&m_lightViewProjectionBufferData.pos, eye);
}
context->UpdateSubresource(
    m_lightViewProjectionBuffer.Get(),
    0,
    NULL,
    &m_lightViewProjectionBufferData,
    0,
    0
    );

Создание окна просмотра

Для прорисовки на карте глубины требуется отдельное окно просмотра. Окно просмотра не является аппаратно-зависимым ресурсом. Вы можете создать его в любом месте кода. Создание окна просмотра вместе с картой теней делает более удобным согласование размеров окна просмотра с размерами карты теней.

// Init viewport for shadow rendering
ZeroMemory(&m_shadowViewport, sizeof(D3D11_VIEWPORT));
m_shadowViewport.Height = m_shadowMapDimension;
m_shadowViewport.Width = m_shadowMapDimension;
m_shadowViewport.MinDepth = 0.f;
m_shadowViewport.MaxDepth = 1.f;

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