This tutorial is part of a Collection: 03. DirectX 11 - Braynzar Soft Tutorials
rate up
1
rate down
7564
views
bookmark
06. Indices

This is another short lesson on how to use indices. Indices let you define how to draw your geometry. For example, you could draw a square by creating two triangles, each with 3 vertices. That makes 6 vertices you would need to create. Using an index buffer, you can create a square using only 4 vertices like we will do. Our index list will define the first triangle as vertex 0, 1, 2, and the second vertex as 0, 2, 3.

1098 downloads
##Introduction## This lesson builds directly off the last lesson. We will learn how to use indices. An index list is usually a list of triangles. They are very usefull for drawing geometry, and they can keep the vertex count down. Also very usefull for when we start loading models. ##Indices## If we want to draw a square, without an index buffer we would need to draw two triangles. Each of the triangles would contain 3 vertices, so the vertex buffer would look like: {v0, v1, v2, v3, v4, v5}, Where triangle 1 is defined by v0, v1, v2, and triangle 2 is defined by v3, v4, v5. Now, think about a huge model containing thousands of triangles, and how many duplicate vertices you would have to make. Using an index list to define the triangles, our vertex buffer could look like: {v0, v1, v2, v3}, where triangle one is defined by v0, v1, v2, and triangle 2 is defined by v0, v2, v3. ##Global Declarations## Since we already covered ID3D11Buffer in a previous lesson, I will not cover them here. But as you can see we have created a new buffer to hold our squares index list. You can also see we changed the name of our vertex buffer to be a square instead of a triangle. ID3D11Buffer* squareIndexBuffer; ID3D11Buffer* squareVertBuffer; ##Vertex List## We have modified our Vertex list to look like a square and added a vertex. Not much to be said here. 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 ), }; ##Index Buffer## This is where we will create the index buffer. First create an array of DWORDs to define our two triangles. Then we initialize and fill out a D3D11_BUFFER_DESC object which will define our index buffer. You can see the byte width is the size of DWORD times 2 times 3. This is because each element in the index array is a DWORD type, so we need to get the size of the DWORD type, multiply it by the amount of faces (or triangles), then multiply it by 3, since each triangle contains 3 vertices, or elements in the index array. After that you se we set the bind flags to D3D11_BIND_INDEX_BUFFER. This will tell the IA stage of the pipeline that this is an index buffer. 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; Next we put the index array into a D3D11_SUBRESOURCE_DATA, create the buffer by calling D3D11DeviceContext::CreateBuffer(), then bind the buffer to the IA stage of the pipeline by calling the function D3D11DeviceContext::IASetIndexBuffer(). The first parameter is the created index buffer, the second parameter is the format. We specify DXGI_FORMAT_R32_UINT, which is a simple 32 bit float, but we could also have specified DXGI_FORMAT_R16_UINT for a 16 bit float. The last parameter is an offset which is the first index to use in the index array. Maybe you have more than one object that your index buffer defines, but you only want to draw one. You can bind the index buffer with the offset of the start of that object before you draw the object. D3D11_SUBRESOURCE_DATA iinitData; iinitData.pSysMem = indices; d3d11Device->CreateBuffer(&indexBufferDesc, &iinitData, &squareIndexBuffer); d3d11DevCon->IASetIndexBuffer( squareIndexBuffer, DXGI_FORMAT_R32_UINT, 0); ##Index Buffer## Since we added another vertex to our vertex array, we need to update our buffer to be the size of 4 vertices instead of just 3 like in the last one. vertexBufferDesc.Usage = D3D11_USAGE_DEFAULT; vertexBufferDesc.ByteWidth = sizeof( Vertex ) * 4; vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vertexBufferDesc.CPUAccessFlags = 0; vertexBufferDesc.MiscFlags = 0; Don't forget to release COM objects! 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(); } ##Index Buffer## And finally we draw our square. We can draw indexed primitives by calling the D3D11DeviceContext::DrawIndexed() method. The first parameter is the number of indices to draw, the second is an offset from the start of the index buffer to start drawing. We could specify a "4" here if we only wanted to draw the second triangle. The third parameter might be a bit confusing, maybe not though. This is the offset from the start of the vertex buffer to start drawing. You might have two index buffers, one describing a sphere, and one describing a box. But maybe you have both in a single vertex buffer, where the sphere is the first set of vertices in the vertex buffer, and the box is the second set. So to draw the box, you would need to set this third parameter to the number of vertices in your sphere, so that the box would be the first thing to start drawing. void DrawScene() { //Clear our backbuffer float bgColor[4] = {(0.0f, 0.0f, 0.0f, 0.0f)}; d3d11DevCon->ClearRenderTargetView(renderTargetView, bgColor); //Draw the triangle d3d11DevCon->DrawIndexed( 6, 0, 0 ); //Present the backbuffer to the screen SwapChain->Present(0, 0); } We are now done with the indices lesson! ##Exercise:## 1. Make two index buffers. One to hold one triangle, and the other to hold two. Put all the vertices into one buffer, and draw only the second two triangles in the vertex buffer. HINT -->> (Third parameter of the DrawIndexed method) 2. Do the same thing as in the first exercise, but instead of having two index buffers, make two vertex buffers and one index buffer. HINT -->> (Second parameter of the DrawIndexed method) Here's 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; ///////////////**************new**************//////////////////// ID3D11Buffer* squareIndexBuffer; ID3D11Buffer* squareVertBuffer; ///////////////**************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 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(); //Set our Render Target d3d11DevCon->OMSetRenderTargets( 1, &renderTargetView, NULL ); return true; } void CleanUp() { //Release the COM Objects we created SwapChain->Release(); d3d11Device->Release(); d3d11DevCon->Release(); renderTargetView->Release(); ///////////////**************new**************//////////////////// squareVertBuffer->Release(); squareIndexBuffer->Release(); ///////////////**************new**************//////////////////// VS->Release(); PS->Release(); VS_Buffer->Release(); PS_Buffer->Release(); vertLayout->Release(); } 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); ///////////////**************new**************//////////////////// //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); ///////////////**************new**************//////////////////// //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; //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**************//////////////////// //Draw the triangle d3d11DevCon->DrawIndexed( 6, 0, 0 ); ///////////////**************new**************//////////////////// //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
For exercise 1 and 2 I used g_pDeviceContext->DrawIndexed(a, b, 0); to control which triangle to render. Never used the 3rd parameter (still 0). However, you have to call g_pDeviceContext->IASetVertexBuffers(0, 1, &g_pTriangleVertexBuffer, &stride, &offset); or g_pDeviceContext->IASetIndexBuffer(g_pSquareIndexBuffer, DXGI_FORMAT_R32_UINT, 0); to set the current used buffer (if there are more than 1 of each). After setting the correct buffer you can call g_pDeviceContext->DrawIndexed(a, b, c);.
on Feb 15 `16
wlasar64