This tutorial is part of a Collection: 02. DirectX 10 - Braynzar Soft Tutorials
1469
views
07. Index Buffer
Index buffers are used to tell direct3d how to put the vertices together to form primitives.
An index buffer is used to describe the order of vertices to draw a primitive. The order is important, as only the "front" face of each primitive is usually drawn. By default, Direct3D uses clockwise winding order, so if the primitives triangles were drawn clockwise to the camera, then the camera is looking at the "front face", which is the face that will be drawn. This is called Culling, it is possible to change the winding order or to disable culling altogether. Another thing about the index buffer is you can use the same vertice twice to draw two different primitives. Using an index buffer to draw a quad takes only 4 vertices, but not using an index buffer you will need to define 6 vertices, 3 for each triangle. This makes for less memory usage and quicker processing, as you do not need to store the same vertice twice. In an index list, every 3 indices define a triangle. Take a look at this example: Vertex v[4] = {v0, v1, v2, v3}; DWORD indexList[6] = {0, 1, 2, //triangle 1 0, 2, 3}; //triangle 2 This code says draw triangle 1 with v0, v1, and v2. Then draw triangle 2 with v0, v2, v3. The first thing we do in our code is declare a new buffer which we will use as our index buffer using the ID3D10Buffer interface. ID3D10Buffer* IndexBuffer; Now lets jump down to the InitScene function where you will notice we have added a new Vertex to our list. Nothing too new. Vertex vertices[] = { {D3DXVECTOR3(-0.5f, -0.5f, 0.5f), D3DXCOLOR (1.0f, 0.0f, 0.0f, 1.0f)}, {D3DXVECTOR3(-0.5f, 0.5f, 0.5f), D3DXCOLOR (0.0f, 1.0f, 0.0f, 1.0f)}, {D3DXVECTOR3( 0.5f, 0.5f, 0.5f), D3DXCOLOR (0.0f, 0.0f, 1.0f, 1.0f)}, {D3DXVECTOR3( 0.5f, -0.5f, 0.5f), D3DXCOLOR (1.0f, 1.0f, 0.0f, 1.0f)} }; This isn't a new line, just a modified one. We change the "3" from our last lesson, since we only had a triangle before, to a "4" since we have a quad now, and we are using 4 vertices. bd.ByteWidth = sizeof( Vertex ) * 4; This is our index list. This will use the vertices we defined above to create two triangles. DWORD indices[] = { 0, 1, 2, 0, 2, 3, }; Next we jump down to our initScene function. Here we updated our vertex buffer content and defined colors along with the position of our vertices. We covered describing a buffer in the begin drawing lesson, so i'll just go through whats different here. The first is the size we need to declare. Were taking the size of type DWORD, since our index buffer is of type DWORD, then we times it by the number of faces, or triangles, which there is two for a quad, then times it by the number of vertices in each face. Or since our program is simple, we could have just timesed DWORD by 6, since we have 6 elements in our index buffer array. The line after that says this will be used as an index buffer. Then for pSysMem, we are filling this buffer's contents with our index buffer we described above. after that, we create the buffer using the CreateBuffer method of our device interface. The created buffer is returned to IndexBuffer, so we can use it to bind to the pipeline next. D3D10_BUFFER_DESC ibd; ibd.Usage = D3D10_USAGE_IMMUTABLE; ibd.ByteWidth = sizeof(DWORD) * 2 * 3; ibd.BindFlags = D3D10_BIND_INDEX_BUFFER; ibd.CPUAccessFlags = 0; ibd.MiscFlags = 0; D3D10_SUBRESOURCE_DATA iinitData; iinitData.pSysMem = indices; d3dDevice->CreateBuffer(&ibd, &iinitData, &IndexBuffer); Here we are binding the index buffer to the pipeline. The first parameter is our created index buffer, the second is the format of our buffer, which is an unsigned 32 bit integer format, the third parameter is an offset measured in bytes from the beginning of our index buffer we will start binding to the pipeline. d3dDevice->IASetIndexBuffer(IndexBuffer, DXGI_FORMAT_R32_UINT, 0); Here we release the index buffer to prevent memory leaks. if( IndexBuffer ) IndexBuffer->Release(); Last but not least, we go to the DrawScene function, where we changed a line. In the previous lessons, we used the Draw() method of our device interface, now we are using the DrawIndexed() method of our device interface. We use this method to draw primitives with an index buffer. The first parameter is the number of indices we want to draw, second parameter is the offset of indices from the beginning of our vertex buffer we binded to the pipeline. If we only wanted to draw the second triangle, we would specify a "4" for this parameter, since our second triangle starts on the 4th index in our index buffer. The third parameter might sound a little confusing. Keeping a 0 here will start using the vertices in the vertex buffer with the very first one. But if you put a box and a cylinder in one vertex buffer starting with the box, but you have two different index buffers. You will need to change the third parameter here for the cylinder because the cylinder does not start at 0 in the vertex buffer, like it starts at 0 in the index buffer, you will need to change the third parameter here to the size of the box, since the cylinder is after the box. d3dDevice->DrawIndexed(6, 0, 0); Thats all for the Index Buffer, I hope you are able to understand and use the index buffer now! Here's the final code: main.cpp #include <Windows.h> #include <d3d10.h> #include <d3dx10.h> #include <string> #pragma comment(lib, "D3D10.lib") #pragma comment(lib, "D3Dx10d.lib") LPCTSTR WndClassName = L"firstwindow"; HWND hwnd = NULL; const int Width = 800; const int Height = 600; bool InitializeWindow(HINSTANCE hInstance, int ShowWnd, int width, int height, bool windowed); HRESULT hr; ID3D10Device* d3dDevice; IDXGISwapChain* SwapChain; ID3D10RenderTargetView* RenderTargetView; ID3D10DepthStencilView* DepthStencilView; ID3D10Effect* FX; ID3D10InputLayout* VertexLayout; ID3D10Buffer* VertexBuffer; /////////new//////////////////////////////////////////////// ID3D10Buffer* IndexBuffer; /////////new//////////////////////////////////////////////// ID3D10EffectTechnique* Technique; bool InitializeDirect3dApp(HINSTANCE hInstance); bool InitScene(); void DrawScene(); bool ReleaseObjects(); int messageloop(); LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); struct Vertex { D3DXVECTOR3 pos; D3DXCOLOR color; }; 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(!InitializeDirect3dApp(hInstance)) { MessageBox(0, L"Direct3D Initialization - Failed", L"Error", MB_OK); return 0; } if(!InitScene()) { MessageBox(0, L"Scene Initialization - Failed", L"Error", MB_OK); return 0; } messageloop(); if(!ReleaseObjects()) { MessageBox(0, L"Object Releasing - Failed", L"Error", MB_OK); return 0; } 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"Window Title", 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 InitializeDirect3dApp(HINSTANCE hInstance) { UINT createDeviceFlags = 0; D3D10_DRIVER_TYPE driverTypes[] = { D3D10_DRIVER_TYPE_HARDWARE, D3D10_DRIVER_TYPE_REFERENCE, }; UINT numDriverTypes = sizeof( driverTypes ) / sizeof( driverTypes[0] ); DXGI_SWAP_CHAIN_DESC scd; scd.BufferDesc.Width = Width; scd.BufferDesc.Height = Height; scd.BufferDesc.RefreshRate.Numerator = 60; scd.BufferDesc.RefreshRate.Denominator = 1; scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; scd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; scd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; //no multisampling scd.SampleDesc.Count = 1; scd.SampleDesc.Quality = 0; scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; scd.BufferCount = 1; scd.OutputWindow = hwnd; scd.Windowed = true; scd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; scd.Flags = 0; D3D10CreateDeviceAndSwapChain(0, D3D10_DRIVER_TYPE_HARDWARE, 0, 0, D3D10_SDK_VERSION, &scd, &SwapChain, &d3dDevice); ID3D10Texture2D* backBuffer; SwapChain->GetBuffer(0, _uuidof(ID3D10Texture2D), reinterpret_cast<void**>(&backBuffer)); d3dDevice->CreateRenderTargetView(backBuffer, 0, &RenderTargetView); backBuffer->Release(); d3dDevice->OMSetRenderTargets(1, &RenderTargetView, NULL); // Setup the viewport D3D10_VIEWPORT vp; vp.Width = Width; vp.Height = Height; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; vp.TopLeftX = 0; vp.TopLeftY = 0; d3dDevice->RSSetViewports( 1, &vp ); return true; } bool InitScene() { /////////new//////////////////////////////////////////////// Vertex vertices[] = { {D3DXVECTOR3(-0.5f, -0.5f, 0.5f), D3DXCOLOR (1.0f, 0.0f, 0.0f, 1.0f)}, {D3DXVECTOR3(-0.5f, 0.5f, 0.5f), D3DXCOLOR (0.0f, 1.0f, 0.0f, 1.0f)}, {D3DXVECTOR3( 0.5f, 0.5f, 0.5f), D3DXCOLOR (0.0f, 0.0f, 1.0f, 1.0f)}, {D3DXVECTOR3( 0.5f, -0.5f, 0.5f), D3DXCOLOR (1.0f, 1.0f, 0.0f, 1.0f)} }; /////////new//////////////////////////////////////////////// D3D10_BUFFER_DESC bd; bd.Usage = D3D10_USAGE_IMMUTABLE; /////////new//////////////////////////////////////////////// bd.ByteWidth = sizeof( Vertex ) * 4; /////////new//////////////////////////////////////////////// bd.BindFlags = D3D10_BIND_VERTEX_BUFFER; bd.CPUAccessFlags = 0; bd.MiscFlags = 0; D3D10_SUBRESOURCE_DATA InitData; InitData.pSysMem = vertices; d3dDevice->CreateBuffer( &bd, &InitData, &VertexBuffer ); /////////new//////////////////////////////////////////////// DWORD indices[] = { 0, 1, 2, 0, 2, 3, }; D3D10_BUFFER_DESC ibd; ibd.Usage = D3D10_USAGE_IMMUTABLE; ibd.ByteWidth = sizeof(DWORD) * 2 * 3; ibd.BindFlags = D3D10_BIND_INDEX_BUFFER; ibd.CPUAccessFlags = 0; ibd.MiscFlags = 0; D3D10_SUBRESOURCE_DATA iinitData; iinitData.pSysMem = indices; d3dDevice->CreateBuffer(&ibd, &iinitData, &IndexBuffer); /////////new//////////////////////////////////////////////// UINT stride = sizeof( Vertex ); UINT offset = 0; d3dDevice->IASetVertexBuffers( 0, 1, &VertexBuffer, &stride, &offset ); /////////new//////////////////////////////////////////////// d3dDevice->IASetIndexBuffer(IndexBuffer, DXGI_FORMAT_R32_UINT, 0); /////////new//////////////////////////////////////////////// D3D10_INPUT_ELEMENT_DESC layout[] = { {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0} }; ID3D10Blob* compilationErrors = 0; HRESULT hr = 0; hr = D3DX10CreateEffectFromFile( L"vertex.fx", NULL, NULL, "fx_4_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, d3dDevice, NULL, NULL, &FX, &compilationErrors, NULL ); if(FAILED(hr)) { MessageBoxA(0, (char*)compilationErrors->GetBufferPointer(), 0, 0); compilationErrors->Release(); return false; } Technique = FX->GetTechniqueByName( "Tech" ); D3D10_PASS_DESC PassDesc; Technique->GetPassByIndex( 0 )->GetDesc( &PassDesc ); d3dDevice->CreateInputLayout( layout, 2, PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &VertexLayout ); d3dDevice->IASetInputLayout( VertexLayout ); d3dDevice->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST ); return true; } bool ReleaseObjects() { if( d3dDevice ) d3dDevice->ClearState(); if( VertexBuffer ) VertexBuffer->Release(); /////////new//////////////////////////////////////////////// if( IndexBuffer ) IndexBuffer->Release(); /////////new//////////////////////////////////////////////// if( VertexLayout ) VertexLayout->Release(); if( FX ) FX->Release(); if( RenderTargetView ) RenderTargetView->Release(); if( SwapChain ) SwapChain->Release(); if( d3dDevice ) d3dDevice->Release(); return true; } void DrawScene() { //Draw Scene Here D3DXCOLOR bgColor( 0.0f, 0.0f, 0.0f, 1.0f); d3dDevice->ClearRenderTargetView( RenderTargetView, bgColor ); D3D10_TECHNIQUE_DESC techDesc; Technique->GetDesc( &techDesc ); for( UINT p = 0; p < techDesc.Passes; ++p ) { Technique->GetPassByIndex( p )->Apply( 0 ); /////////new//////////////////////////////////////////////// d3dDevice->DrawIndexed(6, 0, 0); /////////new//////////////////////////////////////////////// } 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 DrawScene(); } } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch( msg ) { case WM_KEYDOWN: if( wParam == VK_ESCAPE ){ if(MessageBox(0, L"Are you sure you want to exit?", L"Really?", MB_YESNO | MB_ICONQUESTION) == IDYES) DestroyWindow(hwnd); } return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, msg, wParam, lParam); } vertex.fx struct VS_OUTPUT //output structure for vertex shader { float4 Pos : SV_POSITION; float4 Color : COLOR0; }; // Vertex Shader VS_OUTPUT VS(float4 inPos : POSITION, float4 inColor : COLOR) { VS_OUTPUT output = (VS_OUTPUT)0; output.Pos = inPos; //No calculations done here, just returns the position of the vertices output.Color = inColor; return output; //send color and position to pixel shader } // Pixel Shader float4 PS(VS_OUTPUT input) : SV_Target { return input.Color; // Set the color of the pixel to what we defined for the vertex. } technique10 Tech { pass P0 { SetVertexShader( CompileShader( vs_4_0, VS() ) ); SetPixelShader( CompileShader( ps_4_0, PS() ) ); } }
Sign in to comment