工程GIT地址:https://gitee.com/yaksue/yaksue-graphics
目标
如果不设置“深度缓冲”,场景中的图形将不能以正确的遮挡关系渲染。(见本篇的【无深度缓冲的情况】)
本篇目标是配置好工程中各个图形API的深度缓冲。
无深度缓冲的情况
现在将顶点数据变成一个立方体,并为每一个面都设置不同的颜色:
//作为测试的顶点缓冲数据:
std::vector<RawVertexData> vertices = {
//六个面,颜色各不同
{ {-1.0f, 1.0f, -1.0f}, {1.0f, 0.0f, 0.0f, 1.0f} },
{ {1.0f, 1.0f, -1.0f}, {1.0f, 0.0f, 0.0f, 1.0f} },
{ {1.0f, 1.0f, 1.0f}, {1.0f, 0.0f, 0.0f, 1.0f} },
{ {-1.0f, 1.0f, 1.0f}, {1.0f, 0.0f, 0.0f, 1.0f} },
{ {-1.0f, -1.0f, -1.0f}, {0.0f, 1.0f, 0.0f, 1.0f} },
{ {1.0f, -1.0f, -1.0f}, {0.0f, 1.0f, 0.0f, 1.0f} },
{ {1.0f, -1.0f, 1.0f}, {0.0f, 1.0f, 0.0f, 1.0f} },
{ {-1.0f, -1.0f, 1.0f}, {0.0f, 1.0f, 0.0f, 1.0f} },
{ {-1.0f, -1.0f, 1.0f}, {0.0f, 0.0f, 1.0f, 1.0f} },
{ {-1.0f, -1.0f, -1.0f}, {0.0f, 0.0f, 1.0f, 1.0f} },
{ {-1.0f, 1.0f, -1.0f}, {0.0f, 0.0f, 1.0f, 1.0f} },
{ {-1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 1.0f, 1.0f} },
{ {1.0f, -1.0f, 1.0f}, {1.0f, 1.0f, 0.0f, 1.0f} },
{ {1.0f, -1.0f, -1.0f}, {1.0f, 1.0f, 0.0f, 1.0f} },
{ {1.0f, 1.0f, -1.0f}, {1.0f, 1.0f, 0.0f, 1.0f} },
{ {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 0.0f, 1.0f} },
{ {-1.0f, -1.0f, -1.0f}, {1.0f, 0.0f, 1.0f, 1.0f} },
{ {1.0f, -1.0f, -1.0f}, {1.0f, 0.0f, 1.0f, 1.0f} },
{ {1.0f, 1.0f, -1.0f}, {1.0f, 0.0f, 1.0f, 1.0f} },
{ {-1.0f, 1.0f, -1.0f}, {1.0f, 0.0f, 1.0f, 1.0f} },
{ {-1.0f, -1.0f, 1.0f}, {0.0f, 1.0f, 1.0f, 1.0f} },
{ {1.0f, -1.0f, 1.0f}, {0.0f, 1.0f, 1.0f, 1.0f} },
{ {1.0f, 1.0f, 1.0f}, {0.0f, 1.0f, 1.0f, 1.0f} },
{ {-1.0f, 1.0f, 1.0f}, {0.0f, 1.0f, 1.0f, 1.0f} },
};
//作为测试的索引数据
std::vector<unsigned int> indices = {
//六个面:
3,1,0,
2,1,3,
6,4,5,
7,4,6,
11,9,8,
10,9,11,
14,12,13,
15,12,14,
19,17,16,
18,17,19,
22,20,21,
23,20,22
};
效果:
可以看到,在没有设置深度缓冲的情况下,各个面没有正确的遮挡关系。
准确来说,也不是一定能看到这种情况。如果设置了剔除,即反向时针的三角形不被渲染,那么对于这个正方体,依旧能看到“正确”的渲染结果。
图中OpenGL版的错误,也代表着现在工程里的OpenGL版没有设置剔除,即两面都渲染了。
对于工程中目前的D3D11就设置了剔除,因此它看不到这种错误,要想看到这种错误,需要设置为不剔除,例如在D3D11中需要:
D3D11_RASTERIZER_DESC RasterizerState_desc;
ZeroMemory(&RasterizerState_desc, sizeof(D3D11_RASTERIZER_DESC));
RasterizerState_desc.FillMode = D3D11_FILL_SOLID;
RasterizerState_desc.CullMode = D3D11_CULL_NONE;//不剔除
ThrowIfFailed(Device->CreateRasterizerState(&RasterizerState_desc, &RasterizerState));
ImmediateContext->RSSetState(RasterizerState);
在D3D12中需要:
//光栅化状态
D3D12_RASTERIZER_DESC RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
RasterizerState.CullMode = D3D12_CULL_MODE_NONE;//不剔除
...
psoDesc.RasterizerState = RasterizerState;
工程中发现Vulkan版本似乎也没有剔除,所以不用设置。
设置深度缓冲
OpenGL
对于OpenGL非常简单:
首先启用GL_DEPTH_TEST
:
glEnable(GL_DEPTH_TEST);
然后,Clear的时候附带上GL_DEPTH_BUFFER_BIT
。
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
D3D11
首先,需要两个成员:
ID3D11Texture2D* DepthStencil = NULL;
ID3D11DepthStencilView* DepthStencilView = NULL;
在初始化中创建他们:
//创建一个 深度/模板 贴图
D3D11_TEXTURE2D_DESC descDepth;
ZeroMemory(&descDepth, sizeof(descDepth));
descDepth.Width = WindowWidth;
descDepth.Height = WindowHeight;
descDepth.MipLevels = 1;
descDepth.ArraySize = 1;
descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
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;
ThrowIfFailed(Device->CreateTexture2D(&descDepth, NULL, &DepthStencil));
//创建一个 深度/模板 view
D3D11_DEPTH_STENCIL_VIEW_DESC descDSV;
ZeroMemory(&descDSV, sizeof(descDSV));
descDSV.Format = descDepth.Format;
descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
descDSV.Texture2D.MipSlice = 0;
ThrowIfFailed(Device->CreateDepthStencilView(DepthStencil, &descDSV, &DepthStencilView));
在OM阶段设置上DepthStencilView
//输出合并阶段(Output-Merger Stage)设置RenderTarget和DepthStencilView
ImmediateContext->OMSetRenderTargets(1, &RenderTargetView, DepthStencilView);
在Clear时:
//清理DepthStencilView
ImmediateContext->ClearDepthStencilView(DepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);
D3D12
D3D12的代码相对较多,概括起来:
- 创建depth stencil view的Descriptor堆
- 创建depth stencil view
OMSetRenderTargets
中设置 depth stencil view- ClearDepthStencilView
- 管线状态中 DepthEnable = true;等设置
Vulkan
Vulkan这部分的代码最多。
此部分主要参照《Depth buffering - Vulkan Tutorial》