问题描述
我确定我只是遗漏了一些简单的步骤,到目前为止我太盲目而没有注意到,但我似乎根本无法进行深度测试。这是 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,D3D11_SDK_VERSION,&swapDesc,&mSwap,&mDevice,&mContext));
COMptr<ID3D11Resource> backBuffer;
GFX_THROW_INFO(mSwap->GetBuffer(0,__uuidof(ID3D11Resource),&backBuffer));
GFX_THROW_INFO(mDevice->CreaterendertargetView(backBuffer.Get(),&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,&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 ({},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);
但不幸的是,结果最终是这样的(请注意,crysis nanosuit 模型在地精头部后面)我相信这也可能是为什么地精模型即使在单独时也呈现错误的原因,但还没有弄清楚那个
只有地精,从一个角度看
如果有人能帮我弄清楚为什么它不起作用,我将不胜感激!
编辑
经过一些更令人沮丧的测试后,我发现深度测试被破坏了,因为我使用 DirectX ToolKit 的 SpriteBatch 和 SpriteFont 类进行了一些测试文本渲染。有没有人遇到过这个问题?除了文本渲染和加载 dds 纹理之外,我真的不需要/不需要工具包,所以我希望如果我想使用这些类,我不需要彻底改变我现有的代码?
解决方法
DirectX Tool Kit 不像传统的 D3DX9/D3DX10 精灵那样“捕获/恢复”状态。这是低效的,并且依赖于一些黑客后门功能来捕获 Direct3D 10+ 的“状态块”。在大多数情况下,您已经准备好为下一次绘制调用设置大部分常用状态。
相反,我已经完整记录了每个类影响的所有状态。您应该在 DirectX Tool Kit 对象呈现后更改所有必需的状态。例如,SpriteBatch 文档状态:
SpriteBatch
使用以下状态:
- 混合状态
- 常量缓冲区(顶点着色器阶段,插槽 0)
- DepthStencilState
- 索引缓冲区
- 输入布局
- 像素着色器
- 原始拓扑
- 光栅化状态
- SamplerState(像素着色器阶段,插槽 0)
- 着色器资源(像素着色器阶段,插槽 0)
- 顶点缓冲区(插槽 0)
- 顶点着色器
所以简而言之,您只需要在调用 SpriteBatch::End
后将 DepthStencilState 设置为您想要使用的内容。
作为状态管理的一般习惯,你应该设置你每一帧依赖的所有状态。虽然在 Direct3D 11 中,您调用 Present
时的“最后状态”仍然存在于下一帧的开始处,但 DirectX 12 并非如此。因此,您应该养成在新帧的开始设置所有内容,例如当前渲染目标、视口、您希望在整个场景中呈现的渲染状态等。
例如,大多数“HUD”渲染是最后完成的,因此 SpriteBatch 的状态更改通常会在下一帧开始时重置——同样,假设您在帧开始时设置了所需的状态,而不是假设它在许多帧中保持不变。
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);