为什么 DirectX Toolkit 会破坏我的深度测试
Why does DirectXToolkit ruin my depth testing
我确定我只是错过了一些简单的步骤,到目前为止我一直太盲目而没有注意到,但我似乎根本无法进行深度测试。这是 DirectX 11。
应该设置的代码:
DXGI_SWAP_CHAIN_DESC swapDesc = { };
swapDesc.BufferDesc.Width = 0;
swapDesc.BufferDesc.Height = 0;
swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
swapDesc.BufferDesc.RefreshRate.Numerator = 0;
swapDesc.BufferDesc.RefreshRate.Denominator = 1;
swapDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
swapDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
swapDesc.SampleDesc.Count = 1;
swapDesc.SampleDesc.Quality = 0;
swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapDesc.BufferCount = 1;
swapDesc.OutputWindow = hwnd;
swapDesc.Windowed = TRUE;
swapDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
swapDesc.Flags = 0;
UINT flg = 0;
#if MAGE_DEBUG
flg |= D3D11_CREATE_DEVICE_DEBUG;
#endif
GFX_THROW_INFO(D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr,
flg,
nullptr, 0,
D3D11_SDK_VERSION, &swapDesc, &mSwap, &mDevice, nullptr,
&mContext));
COMptr<ID3D11Resource> backBuffer;
GFX_THROW_INFO(mSwap->GetBuffer(0, __uuidof(ID3D11Resource), &backBuffer));
GFX_THROW_INFO(mDevice->CreateRenderTargetView(backBuffer.Get(), nullptr, &mTarget));
LOG_INFO("Setting depth stencil dimensions ({}, {})", width, height);
COMptr<ID3D11Texture2D> depthStencil;
D3D11_TEXTURE2D_DESC texDesc = { };
texDesc.Width = width;
texDesc.Height = height;
texDesc.MipLevels = 1;
texDesc.ArraySize = 1;
texDesc.Format = DXGI_FORMAT_D32_FLOAT;
texDesc.SampleDesc.Count = 1;
texDesc.SampleDesc.Quality = 0;
texDesc.Usage = D3D11_USAGE_DEFAULT;
texDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
GFX_THROW_INFO(mDevice->CreateTexture2D(&texDesc, nullptr, &depthStencil));
D3D11_DEPTH_STENCIL_DESC depth = { };
depth.DepthEnable = TRUE;
depth.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
depth.DepthFunc = D3D11_COMPARISON_LESS;
COMptr<ID3D11DepthStencilState> depthState;
GFX_THROW_INFO(mDevice->CreateDepthStencilState(&depth, &depthState));
D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc = { };
dsvDesc.Format = DXGI_FORMAT_D32_FLOAT;
dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
dsvDesc.Texture2D.MipSlice = 0;
GFX_THROW_INFO(mDevice->CreateDepthStencilView(depthStencil.Get(), &dsvDesc, &mDepthStencilView));
mContext->OMSetDepthStencilState(depthState.Get(), 1);
mContext->OMSetRenderTargets(1, mTarget.GetAddressOf(), mDepthStencilView.Get());
LOG_INFO("Setting viewport dimensions ({}, {})", width, height);
D3D11_VIEWPORT vp;
vp.Width = (float) width;
vp.Height = (float) height;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0.0f;
vp.TopLeftY = 0.0f;
mContext->RSSetViewports(1, &vp);
当然,在每一帧之前我都会调用以下内容:
mContext->ClearRenderTargetView(mTarget.Get(), color);
mContext->ClearDepthStencilView(mDepthStencilView.Get(), D3D11_CLEAR_DEPTH, 1.0f, 0);
但不幸的是,最终结果是这样的(请注意孤岛危机纳米服模型在地精头后面)我相信这也可能是地精模型即使在单独时也无法正确渲染但还没有想到的原因一个出来了。
而且只有妖精,从一个角度看
如果有人能帮我弄清楚为什么它不起作用,我将不胜感激!
编辑
经过一些更令人沮丧的测试后,我发现深度测试被破坏了,因为我使用 DirectX ToolKit 的 SpriteBatch 和 SpriteFont 类 进行了一些测试文本渲染。有没有人遇到过这个问题?我真的 want/need 除了文本渲染和加载 dds 纹理之外,我真的没有其他工具包,所以我希望如果我想使用那些 类 我不需要彻底改变我现有的代码?
DirectX Tool Kit 不像旧版 D3DX9/D3DX10 sprite 那样 'capture/restore' 状态。这是低效的,并且依赖于一些 hacky 后门功能来捕获 Direct3D 10+ 的 'state block'。在大多数情况下,无论如何,您已经准备好设置大量常用状态,以便为下一次绘制调用做好准备。
相反,我已经完整记录了每个 class 影响的所有状态。您需要在 DirectX Tool Kit 对象呈现后更改所有必需的状态。例如,SpriteBatch 文档状态:
SpriteBatch
makes use of the following states:
- BlendState
- Constant buffer (Vertex Shader stage, slot 0)
- DepthStencilState
- Index buffer
- Input layout
- Pixel shader
- Primitive topology
- RasterizerState
- SamplerState (Pixel Shader stage, slot 0)
- Shader resources (Pixel Shader stage, slot 0)
- Vertex buffer (slot 0)
- Vertex shader
所以简而言之,您只需要在调用 SpriteBatch::End
后将 DepthStencilState 设置为您想要使用的内容即可。
As a general habit for state management, you should set all state you rely on every frame. While in Direct3D 11 the 'last state' at the time you call Present
is still there at the start of the next frame, this isn't true of DirectX 12. As such, you should make a habit of at the start of a new frame setting everything like current render target, viewport, render states you expect to be present for your whole scene, etc.
For example, most "HUD" rendering is done last, so the state changes by SpriteBatch would normally be reset on the next frame's start--again, assuming you set up the required state at the start of the frame rather than assuming it remains unchanged over many frames.
TL;DR: 将此代码移动到每帧清除渲染目标之后:
mContext->OMSetDepthStencilState(depthState.Get(), 1);
mContext->OMSetRenderTargets(1, mTarget.GetAddressOf(), mDepthStencilView.Get());
D3D11_VIEWPORT vp = { 0.f, 0.f, float(width), float(height), D3D11_MIN_DEPTH, D3D11_MAX_DEPTH };
mContext->RSSetViewports(1, &vp);
我确定我只是错过了一些简单的步骤,到目前为止我一直太盲目而没有注意到,但我似乎根本无法进行深度测试。这是 DirectX 11。
应该设置的代码:
DXGI_SWAP_CHAIN_DESC swapDesc = { };
swapDesc.BufferDesc.Width = 0;
swapDesc.BufferDesc.Height = 0;
swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
swapDesc.BufferDesc.RefreshRate.Numerator = 0;
swapDesc.BufferDesc.RefreshRate.Denominator = 1;
swapDesc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
swapDesc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
swapDesc.SampleDesc.Count = 1;
swapDesc.SampleDesc.Quality = 0;
swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapDesc.BufferCount = 1;
swapDesc.OutputWindow = hwnd;
swapDesc.Windowed = TRUE;
swapDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
swapDesc.Flags = 0;
UINT flg = 0;
#if MAGE_DEBUG
flg |= D3D11_CREATE_DEVICE_DEBUG;
#endif
GFX_THROW_INFO(D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr,
flg,
nullptr, 0,
D3D11_SDK_VERSION, &swapDesc, &mSwap, &mDevice, nullptr,
&mContext));
COMptr<ID3D11Resource> backBuffer;
GFX_THROW_INFO(mSwap->GetBuffer(0, __uuidof(ID3D11Resource), &backBuffer));
GFX_THROW_INFO(mDevice->CreateRenderTargetView(backBuffer.Get(), nullptr, &mTarget));
LOG_INFO("Setting depth stencil dimensions ({}, {})", width, height);
COMptr<ID3D11Texture2D> depthStencil;
D3D11_TEXTURE2D_DESC texDesc = { };
texDesc.Width = width;
texDesc.Height = height;
texDesc.MipLevels = 1;
texDesc.ArraySize = 1;
texDesc.Format = DXGI_FORMAT_D32_FLOAT;
texDesc.SampleDesc.Count = 1;
texDesc.SampleDesc.Quality = 0;
texDesc.Usage = D3D11_USAGE_DEFAULT;
texDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL;
GFX_THROW_INFO(mDevice->CreateTexture2D(&texDesc, nullptr, &depthStencil));
D3D11_DEPTH_STENCIL_DESC depth = { };
depth.DepthEnable = TRUE;
depth.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
depth.DepthFunc = D3D11_COMPARISON_LESS;
COMptr<ID3D11DepthStencilState> depthState;
GFX_THROW_INFO(mDevice->CreateDepthStencilState(&depth, &depthState));
D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc = { };
dsvDesc.Format = DXGI_FORMAT_D32_FLOAT;
dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
dsvDesc.Texture2D.MipSlice = 0;
GFX_THROW_INFO(mDevice->CreateDepthStencilView(depthStencil.Get(), &dsvDesc, &mDepthStencilView));
mContext->OMSetDepthStencilState(depthState.Get(), 1);
mContext->OMSetRenderTargets(1, mTarget.GetAddressOf(), mDepthStencilView.Get());
LOG_INFO("Setting viewport dimensions ({}, {})", width, height);
D3D11_VIEWPORT vp;
vp.Width = (float) width;
vp.Height = (float) height;
vp.MinDepth = 0.0f;
vp.MaxDepth = 1.0f;
vp.TopLeftX = 0.0f;
vp.TopLeftY = 0.0f;
mContext->RSSetViewports(1, &vp);
当然,在每一帧之前我都会调用以下内容:
mContext->ClearRenderTargetView(mTarget.Get(), color);
mContext->ClearDepthStencilView(mDepthStencilView.Get(), D3D11_CLEAR_DEPTH, 1.0f, 0);
但不幸的是,最终结果是这样的(请注意孤岛危机纳米服模型在地精头后面)我相信这也可能是地精模型即使在单独时也无法正确渲染但还没有想到的原因一个出来了。
而且只有妖精,从一个角度看
如果有人能帮我弄清楚为什么它不起作用,我将不胜感激!
编辑
经过一些更令人沮丧的测试后,我发现深度测试被破坏了,因为我使用 DirectX ToolKit 的 SpriteBatch 和 SpriteFont 类 进行了一些测试文本渲染。有没有人遇到过这个问题?我真的 want/need 除了文本渲染和加载 dds 纹理之外,我真的没有其他工具包,所以我希望如果我想使用那些 类 我不需要彻底改变我现有的代码?
DirectX Tool Kit 不像旧版 D3DX9/D3DX10 sprite 那样 'capture/restore' 状态。这是低效的,并且依赖于一些 hacky 后门功能来捕获 Direct3D 10+ 的 'state block'。在大多数情况下,无论如何,您已经准备好设置大量常用状态,以便为下一次绘制调用做好准备。
相反,我已经完整记录了每个 class 影响的所有状态。您需要在 DirectX Tool Kit 对象呈现后更改所有必需的状态。例如,SpriteBatch 文档状态:
SpriteBatch
makes use of the following states:
- BlendState
- Constant buffer (Vertex Shader stage, slot 0)
- DepthStencilState
- Index buffer
- Input layout
- Pixel shader
- Primitive topology
- RasterizerState
- SamplerState (Pixel Shader stage, slot 0)
- Shader resources (Pixel Shader stage, slot 0)
- Vertex buffer (slot 0)
- Vertex shader
所以简而言之,您只需要在调用 SpriteBatch::End
后将 DepthStencilState 设置为您想要使用的内容即可。
As a general habit for state management, you should set all state you rely on every frame. While in Direct3D 11 the 'last state' at the time you call
Present
is still there at the start of the next frame, this isn't true of DirectX 12. As such, you should make a habit of at the start of a new frame setting everything like current render target, viewport, render states you expect to be present for your whole scene, etc.
For example, most "HUD" rendering is done last, so the state changes by SpriteBatch would normally be reset on the next frame's start--again, assuming you set up the required state at the start of the frame rather than assuming it remains unchanged over many frames.
TL;DR: 将此代码移动到每帧清除渲染目标之后:
mContext->OMSetDepthStencilState(depthState.Get(), 1);
mContext->OMSetRenderTargets(1, mTarget.GetAddressOf(), mDepthStencilView.Get());
D3D11_VIEWPORT vp = { 0.f, 0.f, float(width), float(height), D3D11_MIN_DEPTH, D3D11_MAX_DEPTH };
mContext->RSSetViewports(1, &vp);