创建深度缓冲区设备资源Create depth buffer device resources

了解如何创建支持阴影卷的深度测试所需的 Direct3D 设备资源。Learn how to create the Direct3D device resources necessary to support depth testing for shadow volumes. 操作实例:使用 Direct3D 11 中的深度缓冲区实现阴影卷的第 1 部分。Part 1 of Walkthrough: Implement shadow volumes using depth buffers in Direct3D 11.

所需资源Resources you'll need

呈现阴影卷的深度映射需要以下与 Direct3D 设备有关的资源:Rendering a depth map for shadow volumes requires the following Direct3D device-dependent resources:

  • 用于深度映射的资源(缓冲区)A resource (buffer) for the depth map
  • 资源的深度模具视图和着色器资源视图A depth stencil view and shader resource view for the resource
  • 比较采样器状态对象A comparison sampler state object
  • 光线 POV 矩阵的常量缓冲区Constant buffers for light POV matrices
  • 用于呈现阴影映射的视区(通常为方形视区)A viewport for rendering the shadow map (typically a square viewport)
  • 用于启用正面剔除的呈现状态对象A rendering state object to enable front face culling
  • 你还将需要一个呈现状态对象(如果你尚未使用),用于切换回背面剔除。You will also need a rendering state object to switch back to back face culling, if you don't already use one.

请注意,这些资源的创建需要包含在与设备有关的资源创建例程中,这样你的呈现器便可以在安装新的设备驱动程序时或者用户将你的应用移动到与其他图形适配器连接的监视器时重新创建它们。Note that creation of these resources needs to be included in a device-dependent resource creation routine, that way your renderer can recreate them if (for example) a new device driver is installed, or the user moves your app to a monitor attached to a different graphics adapter.

检查功能支持Check feature support

在创建深度映射之前,请在 Direct3D 设备上调用 CheckFeatureSupport 方法,请求 D3D11 _ 功能 _ D3D9 _ 影子 _ 支持,并提供 D3D11 _ 功能 _ 数据 _ D3D9 _ 影子 _ 支持 结构。Before creating the depth map, call the CheckFeatureSupport method on the Direct3D device, request D3D11_FEATURE_D3D9_SHADOW_SUPPORT, and provide a D3D11_FEATURE_DATA_D3D9_SHADOW_SUPPORT structure.

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 编译着色器。If this feature is not supported, do not try to load shaders compiled for shader model 4 level 9_x that call sample comparison functions. 在大多数情况下,缺乏对该功能的支持意味着 GPU 是一款旧设备,其驱动器未更新至支持 WDDM 1.2。In many cases, lack of support for this feature means that the GPU is a legacy device with a driver that isn't updated to support at least WDDM 1.2. 如果设备至少支持功能级别 10 0,则 _ 可以加载为着色器模型 # 0 编译的示例比较着色器 _ 。If the device supports at least feature level 10_0 then you can load a sample comparison shader compiled for shader model 4_0 instead.

创建深度缓冲区Create depth buffer

首先,尝试使用精度较高的深度格式创建深度映射。First, try creating the depth map with a higher-precision depth format. 首先设置匹配的着色器资源视图属性。Set up matching shader resource view properties first. 如果资源创建失败,例如由于设备内存不足或者存在硬件不支持的格式,请尝试精度较低的格式并更改要匹配的属性。If the resource creation fails, for example due to low device memory or a format that the hardware doesn't support, try a lower-precision format and change properties to match.

如果只需要低精度的深度格式(例如,在中等分辨率的 Direct3D 功能级别 9 1 设备上呈现时),则此步骤是可选的 _ 。This step is optional if you only need a low-precision depth format, for example when rendering on medium-resolution Direct3D feature level 9_1 devices.

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
    );

然后创建资源视图。Then create the resource views. 在深度模具视图上将 mip 片设置为零,在着色器资源视图上将 mip 级别设置为 1。Set the mip slice to zero on the depth stencil view and set mip levels to 1 on the shader resource view. 二者都具有 TEXTURE2D 的纹理维度,并且都需要使用匹配的 DXGI _ 格式Both have a texture dimension of TEXTURE2D, and both need to use a matching 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
    );

创建比较状态Create comparison state

现在创建比较采样器状态对象。Now create the comparison sampler state object. 功能级别 9 _ 1 仅支持 D3D11 _ 比较 _ _ 。Feature level 9_1 only supports D3D11_COMPARISON_LESS_EQUAL. 在硬件范围内支持阴影映射中详细介绍了筛选选项,或者只是选取点筛选以便快速获得阴影映射。Filtering choices are explained more in Supporting shadow maps on a range of hardware - or you can just pick point filtering for faster shadow maps.

请注意,你可以指定 D3D11 _ 纹理 _ 地址 _ 边界地址模式,并且它将在功能级别 9 _ 1 设备上工作。Note that you can specify the D3D11_TEXTURE_ADDRESS_BORDER address mode and it will work on feature level 9_1 devices. 这适用于像素着色器,该着色器在进行深度测试之前不测试像素是否位于光线的视锥之内。This applies to pixel shaders that don't test whether the pixel is in the light's view frustum before doing the depth test. 通过为每个边框指定 0 或 1,你可以控制位于光线的视锥之外的像素是否可以通过深度测试,从而可以控制它们是被照亮还是处于阴影之中。By specifying 0 or 1 for each border, you can control whether pixels outside the light's view frustum pass or fail the depth test, and therefore whether they are lit or in shadow.

在功能级别 9 _ 1 上,必须设置以下必需值: MinLOD 设置为零, MaxLOD 设置为 D3D11 _ FLOAT32 _ MAXMaxAnisotropy 设置为零。On feature level 9_1, the following required values must be set: MinLOD is set to zero, MaxLOD is set to D3D11_FLOAT32_MAX, and MaxAnisotropy is set to zero.

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
        )
    );

创建呈现器状态Create render states

现在,创建你可以用来启用正面剔除的呈现器状态。Now create a render state you can use to enable front face culling. 请注意,功能级别 9 _ 1 设备需要将 DepthClipEnable 设置为 trueNote that feature level 9_1 devices require DepthClipEnable set to 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
        )
    );

创建你可以用来启用背面剔除的呈现器状态。Create a render state you can use to enable back face culling. 如果你的呈现代码已经启用了背面剔除,那么你可以跳过这一步。If your rendering code already turns on back face culling, then you can skip this step.

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
        )
    );

创建常量缓冲区Create constant buffers

不要忘记创建一个用于从光线的角度呈现的常量缓冲区。Don't forget to create a constant buffer for rendering from the light's point of view. 你还可以使用该常量缓冲区指定着色器的光线位置。You can also use this constant buffer to specify the light position to the shader. 对点光源使用透视矩阵,对定向光源(太阳光)使用正交矩阵。Use a perspective matrix for point lights, and use an orthogonal matrix for directional lights (such as sunlight).

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

填写常量缓冲数据。Fill the constant buffer data. 在初始化时更新常量缓冲,如果从上一帧起更改光值,则会再次更新。Update the constant buffers once during initialization, and again if the light values have changed since the previous frame.

{
    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
    );

创建视区Create a viewport

你需要一个单独的视区来呈现到阴影映射。You need a separate viewport to render to the shadow map. 视区不是基于设备的资源;你可以在代码中的其他地方创建视区。The viewport isn't a device-based resource; you're free to create it elsewhere in your code. 创建具有阴影映射的视口可便于将视口的尺寸与阴影映射的尺寸保持一致。Creating the viewport along with the shadow map can help make it more convenient to keep the dimension of the viewport congruent with the shadow map dimension.

// 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;

在本操作实例的下一部分中,将介绍如何通过呈现到深度缓冲区来创建阴影映射。In the next part of this walkthrough, learn how to create the shadow map by rendering to the depth buffer.