This tutorial is part of a Collection: 03. DirectX 11 - Braynzar Soft Tutorials
rate up
0
rate down
9688
views
bookmark
07. Depth

Here is another short lesson on how to impliment depth. We will create a depth/stencil buffer, then create a depth/stencil view which we bind to the OM stage of the pipeline. Binding a depth/stencil view to the OM stage will allow it to check each pixel fragement's depth value on the render target. If there are more than one pixel fragments in a spot on the render target, the pixel fragment with the lowest depth value (closest to the screen) will get drawn, and the others will be discarded. The outcome of this tutorial will not look any different from the last tutorial, but implimenting a depth/stencil buffer is necessary to render 3D scenes in directx.

986 downloads
##Introduction## In this lesson we will learn how to impliment the depth of a 3D scene by creating a depth/stencil buffer and view, then binding it to the OM stage. ##Depth/Stencil View## What a depth/stencil view does, is let the OM stage of the pipeline check all the pixel fragement's depth/stencil values on a render target. A pixel fragment is a pixel that has the potential to being written to the screen. Think of a sphere and a box. The sphere is behind the box, so as they are pumped through the rendering pipeline, the pixels from the sphere AND the pixels from the box are both put onto the render target. These pixels are called Pixel Fragments. When a pixel reaches the OM stage, the OM stage will compare the pixel fragments depth value with the pixel fragment already in that position. If the new pixel fragments depth value is less than the pixel fragments that was already there, then the pixel fragment that was already there will be discarded, and the new pixel fragment will be kept on the render target. So back to the sphere and box. Say the sphere is rendered first. There is no geometry on the render target yet, so the entire sphere will be rendered. But when the box's pixels get to the OM stage, the OM stage compares the depth values of the the pixels from the box and the pixels from the sphere. Since the box is in front of the sphere, the spheres pixel fragments are discarded, and the box's pixel fragments are kept on the render target. After all geometry is drawn, the pixels left on the render target are the ones displayed on the screen. The stencil part of the depth view is for advanced techniques, such as mirrors, which we will learn later. ##Global Declarations## Here we have two new interface objects. One is to store the depth/stencil view, the other is to store the depth/stencil buffer. ID3D11DepthStencilView* depthStencilView; ID3D11Texture2D* depthStencilBuffer; ##Depth/Stencil Buffer Description## Now go down to where we initialize d3d. Before we create our depth/stencil buffer, we need to define it. We can do that by filling out a D3D11_TEXTURE2D_DESC, like when we filled out a back buffer. The only thing that really needs mentioning here, since we already covered this structure, isthe bind flags and the format. The format is giving space for two variables; 24 bits for the depth, and 8 bits for the stencil. The bind flags says that this texture will be bound to the OM stage as a depth/stencil buffer. //Describe our Depth/Stencil Buffer D3D11_TEXTURE2D_DESC depthStencilDesc; depthStencilDesc.Width = Width; depthStencilDesc.Height = Height; depthStencilDesc.MipLevels = 1; depthStencilDesc.ArraySize = 1; depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; depthStencilDesc.SampleDesc.Count = 1; depthStencilDesc.SampleDesc.Quality = 0; depthStencilDesc.Usage = D3D11_USAGE_DEFAULT; depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; depthStencilDesc.CPUAccessFlags = 0; depthStencilDesc.MiscFlags = 0; ##Creating the Depth/Stencil View## Now that we have defined our depth/stencil buffer, we need to create it. We can create it using the CreateTexture2D method of the device interface. After we have created a depth/stencil buffer, we need to create the depth/stencil view that will be bound to the OM stage of the pipeline. We do this by calling CreateDepthStencilView() from the device interface. the first parameter is our depth/stencil buffer description, the second parameter is for a depth/stencil state. We don't have one so set it to NULL. and the third is the returned depth/stencil buffer. d3d11Device->CreateTexture2D(&depthStencilDesc, NULL, &depthStencilBuffer); d3d11Device->CreateDepthStencilView(depthStencilBuffer, NULL, &depthStencilView); ##Binding the Depth/Stencil View## The last thing we need to do to impliment depth in our program is to bind it to the OM stage of the pipeline. Remember using this function when we bound the render target? Before we called this function and set the third parameter to NULL, since we didn't have a depth/stencil view. We have one now, so we can change that NULL parameter to our depthStencilView object we created from above. d3d11DevCon->OMSetRenderTargets( 1, &renderTargetView, depthStencilView ); ##Adding depth values to the viewport## Go to where we created and described our viewport. We need to tell the OM stage of the pipeline to convert the pixels z-values, or depth values to a value between 0 and 1, where 0 is the closest possible, and 1 is the furthest possible. viewport.MinDepth = 0.0f; viewport.MaxDepth = 1.0f; ##Cleaning Up## Don't forget to release COM objects! depthStencilView->Release(); depthStencilBuffer->Release(); ##Clearing the Depth/Stencil View## The last thing we need to make sure we do is clear the depth/stencil view every frame, like we do with the render target view. We do this by calling the ClearDepthStencilView() method of the device context interface. The first value is the depth/stencil view we want to clear, the second is an enumerated type, ored together, specifying the part of the depth/stencil view to clear, the fourth parameter is the value we want to clear the depth to. We set this to 1.0f, since 1.0f is the largest depth value anything can have. This makes sure that all the objects are drawn on the screen. If we were to set 0.0f here, nothing would be drawn to the screen since all the depth values of the pixel fragments would be between 0.0f and 1.0f. The last parameter is the value we set the stencil to. We set this to 0 since we're not using it. d3d11DevCon->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0); That wasn't too hard! ##Exercise:## 1. Test the depth/stencil view by creating another triangle whose z value is less than the square we have. Heres the final code: main.cpp //Include and link appropriate libraries and headers// #pragma comment(lib, "d3d11.lib") #pragma comment(lib, "d3dx11.lib") #pragma comment(lib, "d3dx10.lib") #include <windows.h> #include <d3d11.h> #include <d3dx11.h> #include <d3dx10.h> #include <xnamath.h> //Global Declarations - Interfaces// IDXGISwapChain* SwapChain; ID3D11Device* d3d11Device; ID3D11DeviceContext* d3d11DevCon; ID3D11RenderTargetView* renderTargetView; ID3D11Buffer* squareIndexBuffer; ID3D11Buffer* squareVertBuffer; ///////////////**************new**************//////////////////// ID3D11DepthStencilView* depthStencilView; ID3D11Texture2D* depthStencilBuffer; ///////////////**************new**************//////////////////// ID3D11VertexShader* VS; ID3D11PixelShader* PS; ID3D10Blob* VS_Buffer; ID3D10Blob* PS_Buffer; ID3D11InputLayout* vertLayout; //Global Declarations - Others// LPCTSTR WndClassName = L"firstwindow"; HWND hwnd = NULL; HRESULT hr; const int Width = 300; const int Height = 300; //Function Prototypes// bool InitializeDirect3d11App(HINSTANCE hInstance); void CleanUp(); bool InitScene(); void UpdateScene(); void DrawScene(); bool InitializeWindow(HINSTANCE hInstance, int ShowWnd, int width, int height, bool windowed); int messageloop(); LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); //Vertex Structure and Vertex Layout (Input Layout)// struct Vertex //Overloaded Vertex Structure { Vertex(){} Vertex(float x, float y, float z, float cr, float cg, float cb, float ca) : pos(x,y,z), color(cr, cg, cb, ca){} XMFLOAT3 pos; XMFLOAT4 color; }; D3D11_INPUT_ELEMENT_DESC layout[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, }; UINT numElements = ARRAYSIZE(layout); int WINAPI WinMain(HINSTANCE hInstance, //Main windows function HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { if(!InitializeWindow(hInstance, nShowCmd, Width, Height, true)) { MessageBox(0, L"Window Initialization - Failed", L"Error", MB_OK); return 0; } if(!InitializeDirect3d11App(hInstance)) //Initialize Direct3D { MessageBox(0, L"Direct3D Initialization - Failed", L"Error", MB_OK); return 0; } if(!InitScene()) //Initialize our scene { MessageBox(0, L"Scene Initialization - Failed", L"Error", MB_OK); return 0; } messageloop(); CleanUp(); return 0; } bool InitializeWindow(HINSTANCE hInstance, int ShowWnd, int width, int height, bool windowed) { typedef struct _WNDCLASS { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HANDLE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCTSTR lpszMenuName; LPCTSTR lpszClassName; } WNDCLASS; WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = NULL; wc.cbWndExtra = NULL; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 2); wc.lpszMenuName = NULL; wc.lpszClassName = WndClassName; wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); if (!RegisterClassEx(&wc)) { MessageBox(NULL, L"Error registering class", L"Error", MB_OK | MB_ICONERROR); return 1; } hwnd = CreateWindowEx( NULL, WndClassName, L"Lesson 4 - Begin Drawing", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, width, height, NULL, NULL, hInstance, NULL ); if (!hwnd) { MessageBox(NULL, L"Error creating window", L"Error", MB_OK | MB_ICONERROR); return 1; } ShowWindow(hwnd, ShowWnd); UpdateWindow(hwnd); return true; } bool InitializeDirect3d11App(HINSTANCE hInstance) { //Describe our SwapChain Buffer DXGI_MODE_DESC bufferDesc; ZeroMemory(&bufferDesc, sizeof(DXGI_MODE_DESC)); bufferDesc.Width = Width; bufferDesc.Height = Height; bufferDesc.RefreshRate.Numerator = 60; bufferDesc.RefreshRate.Denominator = 1; bufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; bufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; bufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; //Describe our SwapChain DXGI_SWAP_CHAIN_DESC swapChainDesc; ZeroMemory(&swapChainDesc, sizeof(DXGI_SWAP_CHAIN_DESC)); swapChainDesc.BufferDesc = bufferDesc; swapChainDesc.SampleDesc.Count = 1; swapChainDesc.SampleDesc.Quality = 0; swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapChainDesc.BufferCount = 1; swapChainDesc.OutputWindow = hwnd; swapChainDesc.Windowed = TRUE; swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; //Create our SwapChain hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, NULL, NULL, NULL, D3D11_SDK_VERSION, &swapChainDesc, &SwapChain, &d3d11Device, NULL, &d3d11DevCon); //Create our BackBuffer ID3D11Texture2D* BackBuffer; hr = SwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ), (void**)&BackBuffer ); //Create our Render Target hr = d3d11Device->CreateRenderTargetView( BackBuffer, NULL, &renderTargetView ); BackBuffer->Release(); ///////////////**************new**************//////////////////// //Describe our Depth/Stencil Buffer D3D11_TEXTURE2D_DESC depthStencilDesc; depthStencilDesc.Width = Width; depthStencilDesc.Height = Height; depthStencilDesc.MipLevels = 1; depthStencilDesc.ArraySize = 1; depthStencilDesc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; depthStencilDesc.SampleDesc.Count = 1; depthStencilDesc.SampleDesc.Quality = 0; depthStencilDesc.Usage = D3D11_USAGE_DEFAULT; depthStencilDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL; depthStencilDesc.CPUAccessFlags = 0; depthStencilDesc.MiscFlags = 0; //Create the Depth/Stencil View d3d11Device->CreateTexture2D(&depthStencilDesc, NULL, &depthStencilBuffer); d3d11Device->CreateDepthStencilView(depthStencilBuffer, NULL, &depthStencilView); //Set our Render Target d3d11DevCon->OMSetRenderTargets( 1, &renderTargetView, depthStencilView ); ///////////////**************new**************//////////////////// return true; } void CleanUp() { //Release the COM Objects we created SwapChain->Release(); d3d11Device->Release(); d3d11DevCon->Release(); renderTargetView->Release(); squareVertBuffer->Release(); squareIndexBuffer->Release(); VS->Release(); PS->Release(); VS_Buffer->Release(); PS_Buffer->Release(); vertLayout->Release(); ///////////////**************new**************//////////////////// depthStencilView->Release(); depthStencilBuffer->Release(); ///////////////**************new**************//////////////////// } bool InitScene() { //Compile Shaders from shader file hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "VS", "vs_4_0", 0, 0, 0, &VS_Buffer, 0, 0); hr = D3DX11CompileFromFile(L"Effects.fx", 0, 0, "PS", "ps_4_0", 0, 0, 0, &PS_Buffer, 0, 0); //Create the Shader Objects hr = d3d11Device->CreateVertexShader(VS_Buffer->GetBufferPointer(), VS_Buffer->GetBufferSize(), NULL, &VS); hr = d3d11Device->CreatePixelShader(PS_Buffer->GetBufferPointer(), PS_Buffer->GetBufferSize(), NULL, &PS); //Set Vertex and Pixel Shaders d3d11DevCon->VSSetShader(VS, 0, 0); d3d11DevCon->PSSetShader(PS, 0, 0); //Create the vertex buffer Vertex v[] = { Vertex( -0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f ), Vertex( -0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f ), Vertex( 0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f ), Vertex( 0.5f, -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f ), }; DWORD indices[] = { 0, 1, 2, 0, 2, 3, }; D3D11_BUFFER_DESC indexBufferDesc; ZeroMemory( &indexBufferDesc, sizeof(indexBufferDesc) ); indexBufferDesc.Usage = D3D11_USAGE_DEFAULT; indexBufferDesc.ByteWidth = sizeof(DWORD) * 2 * 3; indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; indexBufferDesc.CPUAccessFlags = 0; indexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA iinitData; iinitData.pSysMem = indices; d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, &squareIndexBuffer); d3d11DevCon->IASetIndexBuffer( squareIndexBuffer, DXGI_FORMAT_R32_UINT, 0); D3D11_BUFFER_DESC vertexBufferDesc; ZeroMemory( &vertexBufferDesc, sizeof(vertexBufferDesc) ); vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT; vertexBufferDesc.ByteWidth = sizeof( Vertex ) * 4; vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vertexBufferDesc.CPUAccessFlags = 0; vertexBufferDesc.MiscFlags = 0; D3D11_SUBRESOURCE_DATA vertexBufferData; ZeroMemory( &vertexBufferData, sizeof(vertexBufferData) ); vertexBufferData.pSysMem = v; hr = d3d11Device->CreateBuffer( &vertexBufferDesc, &vertexBufferData, &squareVertBuffer); //Set the vertex buffer UINT stride = sizeof( Vertex ); UINT offset = 0; d3d11DevCon->IASetVertexBuffers( 0, 1, &squareVertBuffer, &stride, &offset ); //Create the Input Layout hr = d3d11Device->CreateInputLayout( layout, numElements, VS_Buffer->GetBufferPointer(), VS_Buffer->GetBufferSize(), &vertLayout ); //Set the Input Layout d3d11DevCon->IASetInputLayout( vertLayout ); //Set Primitive Topology d3d11DevCon->IASetPrimitiveTopology( D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); //Create the Viewport D3D11_VIEWPORT viewport; ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT)); viewport.TopLeftX = 0; viewport.TopLeftY = 0; viewport.Width = Width; viewport.Height = Height; ///////////////**************new**************//////////////////// viewport.MinDepth = 0.0f; viewport.MaxDepth = 1.0f; ///////////////**************new**************//////////////////// //Set the Viewport d3d11DevCon->RSSetViewports(1, &viewport); return true; } void UpdateScene() { } void DrawScene() { //Clear our backbuffer float bgColor[4] = {(0.0f, 0.0f, 0.0f, 0.0f)}; d3d11DevCon->ClearRenderTargetView(renderTargetView, bgColor); ///////////////**************new**************//////////////////// //Refresh the Depth/Stencil view d3d11DevCon->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0); ///////////////**************new**************//////////////////// //Draw the triangle d3d11DevCon->DrawIndexed( 6, 0, 0 ); //Present the backbuffer to the screen SwapChain->Present(0, 0); } int messageloop(){ MSG msg; ZeroMemory(&msg, sizeof(MSG)); while(true) { BOOL PeekMessageL( LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg ); if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) break; TranslateMessage(&msg); DispatchMessage(&msg); } else{ // run game code UpdateScene(); DrawScene(); } } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch( msg ) { case WM_KEYDOWN: if( wParam == VK_ESCAPE ){ DestroyWindow(hwnd); } return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wParam, lParam); } Effects.fx: struct VS_OUTPUT { float4 Pos : SV_POSITION; float4 Color : COLOR; }; VS_OUTPUT VS(float4 inPos : POSITION, float4 inColor : COLOR) { VS_OUTPUT output; output.Pos = inPos; output.Color = inColor; return output; } float4 PS(VS_OUTPUT input) : SV_TARGET { return input.Color; }
Comments
Hi!here is my question. I set the vertex array like this: {-0.5f, -0.5f, 1.1f, -0.5f, 0.5f, 1.1f, 0.5f, 0.5f, 1.1f, 0.5f, -0.5f, 1.1f.} In this array,i set the z component to 1.1f. then i set viewport.MaxDepth=1.0f,also,i write this.d3d11DevCon->ClearDepthStencilView(depthStencilView, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.1f, 0); But when i done this anything,i run the program,but i got nothing on win32,only black background.i urgently wonder know why,Thanks!
on Jun 24 `19
adonis
sorry!I made a mistake in the previous comments.i set viewport.MaxDepth=1.1f
on Jun 24 `19
adonis
I'm not sure if you still check these comments or not. But after following along I wasn't able to see the square anymore since creating the DepthStencilBuffer. After digging around online it seems a DepthStencilState was also required, which you never went over, or included in your code.
on Oct 14 `19
CGunn86