Depth Complexity based on DirectX 12 and the Blend demo of Introduction To 3D Game Programming With DirectX 12:
cpp code:
// define the depth complexity level const int gDepthComplexityLevel = 5; // constant buffer define the color at different depth complexity level struct StencilBufferConstants { DirectX::XMFLOAT4 Color = { 0.f, 0.f, 0.f, 0.f }; }; std::unique_ptr<UploadBuffer<StencilBufferConstants>> mStencilBufferCB = nullptr; // root signature for the depth complexity ComPtr<ID3D12RootSignature> mStencilBufferRootSignature = nullptr; bool BlendApp::Initialize() { ... // at initialization stage, we create the different color for different depth complexity level. // and store the color in the constant buffer. mStencilBufferCB = std::make_unique<UploadBuffer>(md3dDevice.Get(), gDepthComplexityLevel, true); for (int i = 0; i < gDepthComplexityLevel; i++) { StencilBufferConstants sBC; sBC.Color = XMFLOAT4(1.f / gDepthComplexityLevel * i, 1.f / gDepthComplexityLevel * i, 1.f / gDepthComplexityLevel * i, 1.f / gDepthComplexityLevel * i); mStencilBufferCB.get()->CopyData(i, sBC); } ... return true; } void BlendApp::Draw(const GameTimer& gt) { ... // after we draw all the render item in the scene. // we need to switch to depth complexity's pipeline state object and root signature. mCommandList->SetPipelineState(mPSOs["stencilBuffer"].Get()); mCommandList->SetGraphicsRootSignature(mStencilBufferRootSignature.Get()); UINT sCBByteSize = d3dUtil::CalcConstantBufferByteSize(sizeof(StencilBufferConstants)); // we use some post process tech to draw the depth complexity picture. // pls refer to the hlsl file. mCommandList->IASetVertexBuffers(0, 0, nullptr); mCommandList->IASetIndexBuffer(nullptr); mCommandList->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); for (int i = 0; i < gDepthComplexityLevel; i++) { // choose the corlor for different depth complexity. D3D12_GPU_VIRTUAL_ADDRESS sCBAddress = mStencilBufferCB->Resource()->GetGPUVirtualAddress() + i * sCBByteSize; mCommandList->SetGraphicsRootConstantBufferView(0, sCBAddress); mCommandList->OMSetStencilRef(i); // set the stencil test benchmark. mCommandList->DrawInstanced(3, 1, 0, 0); } ... } void BlendApp::BuildRootSignature() { ... // we use the specific root signature for the depth complexity. CD3DX12_ROOT_PARAMETER stencilBufferSlotRootParameter[1]; stencilBufferSlotRootParameter[0].InitAsConstantBufferView(0); CD3DX12_ROOT_SIGNATURE_DESC stencilBufferRootSigDesc(1, stencilBufferSlotRootParameter, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT); hr = D3D12SerializeRootSignature(&stencilBufferRootSigDesc, D3D_ROOT_SIGNATURE_VERSION_1, serializedRootSig.GetAddressOf(), errorBlob.GetAddressOf()); if (errorBlob != nullptr) { ::OutputDebugStringA((char*)errorBlob->GetBufferPointer()); } ThrowIfFailed(hr); ThrowIfFailed(md3dDevice->CreateRootSignature( 0, serializedRootSig->GetBufferPointer(), serializedRootSig->GetBufferSize(), IID_PPV_ARGS(mStencilBufferRootSignature.GetAddressOf()))); } void BlendApp::BuildShadersAndInputLayout() { ... // specific vertex / pixel shader for depth complexity. mShaders["stencilBufferVS"] = d3dUtil::CompileShader(L"Shaders\StencilBuffer.hlsl", nullptr, "VS", "vs_5_0"); mShaders["stencilBufferPS"] = d3dUtil::CompileShader(L"Shaders\StencilBuffer.hlsl", nullptr, "PS", "ps_5_0"); ... } void BlendApp::BuildPSOs() { // Use depth / stencil settings to record the depth complexity. D3D12_DEPTH_STENCIL_DESC depthComplexitySS; depthComplexitySS.DepthEnable = true; depthComplexitySS.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; depthComplexitySS.DepthFunc = D3D12_COMPARISON_FUNC_LESS; depthComplexitySS.StencilEnable = true; depthComplexitySS.StencilReadMask = 0xff; depthComplexitySS.StencilWriteMask = 0xff; // no matter pixel pass the depth test, we need to add the value in stencil buffer depthComplexitySS.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; depthComplexitySS.FrontFace.StencilPassOp = D3D12_STENCIL_OP_INCR; depthComplexitySS.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_INCR; depthComplexitySS.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; ... opaquePsoDesc.DepthStencilState = depthComplexitySS; ... // PSO for Stencil Buffer Quad. D3D12_GRAPHICS_PIPELINE_STATE_DESC stencilBufferPsoDesc = opaquePsoDesc; D3D12_DEPTH_STENCIL_DESC stencilBufferDSS; stencilBufferDSS.DepthEnable = false; stencilBufferDSS.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; stencilBufferDSS.DepthFunc = D3D12_COMPARISON_FUNC_LESS; stencilBufferDSS.StencilEnable = true; stencilBufferDSS.StencilReadMask = 0xff; stencilBufferDSS.StencilWriteMask = 0xff; // we need the draw depth complexity for several times for each level. // only the stencil buffer's value match the depth complexity level, then we draw it. // but we do not need to update the stencil buffer any longer. stencilBufferDSS.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_EQUAL; stencilBufferDSS.FrontFace.StencilFailOp = D3D12_STENCIL_OP_KEEP; stencilBufferDSS.FrontFace.StencilPassOp = D3D12_STENCIL_OP_KEEP; stencilBufferDSS.FrontFace.StencilDepthFailOp = D3D12_STENCIL_OP_KEEP; ... stencilBufferPsoDesc.DepthStencilState = stencilBufferDSS; stencilBufferPsoDesc.InputLayout = { nullptr, 0 }; stencilBufferPsoDesc.pRootSignature = mStencilBufferRootSignature.Get(); stencilBufferPsoDesc.VS = { reinterpret_cast<<BYTE*>(mShaders["stencilBufferVS"]->GetBufferPointer()), mShaders["stencilBufferVS"]->GetBufferSize() }; stencilBufferPsoDesc.PS = { reinterpret_cast<BYTE*>(mShaders["stencilBufferPS"]->GetBufferPointer()), mShaders["stencilBufferPS"]->GetBufferSize() }; ThrowIfFailed(md3dDevice->CreateGraphicsPipelineState(&stencilBufferPsoDesc, IID_PPV_ARGS(&mPSOs["stencilBuffer"]))); }
HLSL code:
// StencilBuffer.HLSL cbuffer cbMaterial : register(b0) { float4 color; }; // for this part, we dont need vertex buffer, index buffer or input layout. // what we do is to draw a triangle with three vertices. // this triangle will cover the whole window. struct VertexOut { float4 Pos : SV_POSITION; }; // in the NDC space, the position of the vertex will be (x/w, y/w, z/w, 1). // the id will be 0, 1 and 2. // so the position will be (-1, 1, 0, 1), (1, 3, 0, 1), (3, -3, 0, 1). // it means that the triangle is much larger than the window, and we can let hardware to do the clip. VertexOut VS(uint id : SV_VERTEXID) { VertexOut vout; vout.Pos.x = (float)(id / 2) * 4 - 1.0; vout.Pos.y = (float)(id % 2) * 4 - 1.0; vout.Pos.z = 0.0; vout.Pos.w = 1.0; return vout; } float4 PS(VertexOut pin) : SV_Target { return color; }