This tutorial is part of a Collection: 02. DirectX 10 - Braynzar Soft Tutorials
1952
views
03. Initializing Direct3D
This is where we finally initialize direct3d. All lessons after this will build off of this base code. The final product of this lessons will be a screen which changes colors! Lets get started!
We have one more thing to do to start displaying our crazy minds using DirectX. And that is to Initialize Direct3D. By the end of this lesson, you should have a nice little window with a blue background.We will initialize direct3d here. After we initialize direct3d, all the boring preparation stuff will be over, and we can start drawing things onto the screen! You will need to download the directx sdk from microsofts website. .[http://www.microsoft.com/downloads/en/details.aspx?displaylang=en&FamilyID=3021d52b-514e-41d3-ad02-438a3ba730ba][here]. Ok, lets get on this thing. To get the Functions and structures and all that other stuff necessary to compile a Direct3D Program, you need to include the DirectX SDK library and header directories. Follow these steps: Visual Studio 2010 took out the VC++ directories from where it was in 2008, so it's different how to get there now. **Visual Studio 2008** 1. After you open your project (Remember this lesson builds off the Creating a Win32 Window Code), In the Tools menu at the top, go down to options... 2. Expand the'Projects and Solutions' in the menu on the left. 3.Then click 'VC++ Directories' 4.On the right side at the top where it says 'Show directories for:', pull down that menu and select 'Include Files' 5.Click on the little folder button right below that, or press Ctrl+Insert 6.It should create a new line. Click on the button at the right of the line that has the three periods(...). 7.A window will pop-up. Find 'Microsoft DirectX SDK (version)', then the includes directory. Mine was at C:\Program Files\Microsoft DirectX SDK (March 2008)\Include. 8.After you have your DirectX includes directory, go to the top again where it says 'Show directories for:'. 9.Drop down the menu and select 'Library Files'. 10.Click on the folder icon again, or press Ctrl+Insert 11.Click on the Button with the three periods on the right side of the line again. 12.Find the 'Microsoft DirectX SDK (version)' again, but this time instead of the includes directory, find the 'lib' folder then the 'x86' folder. Mine was C:\Program Files\Microsoft DirectX SDK (March 2008)\Lib\x86. **Visual Studio 2010** 1. Right Click the solutions name in the 'Solution Explorer' (menu on the left). 2.Then click 'Properties' at the bottom of the menu. 3.Expand the 'Configuration Properties' on the left. 4.Click on the 'VC++ Directories' 5.On the right side, where it says 'Include Directories', pull the drop-down menu on the right and click edit. 6.A window will pop-up. Click on the Folder icon at the top or press 'ctrl + insert. Find 'Microsoft DirectX SDK (version)', then the includes directory. Mine was at C:\Program Files\Microsoft DirectX SDK (February 2010)\Include. Then click OK 7.After you have your DirectX includes directory,do the same thing in 'Library Directories' 8.Find the 'Microsoft DirectX SDK (version)' again, but this time instead of the includes directory, find the 'lib' folder then the 'x86' folder. Mine was C:\Program Files\Microsoft DirectX SDK (Febrary 2010)\Lib\x86. That should be all the setting up you need to do to compile this DirectX app. The first line just links to the d3d10 library. #pragma comment(lib,"d3d10.lib") The next three new lines are declared here so they can be global, in other words used anywhere in our application. The first line is the pointer to the direct3d interface which controls the hardware device. ID3D10Device is basically the software controller to our graphics device. From now on, whenever we want to do something with our graphics card, we will use this controller, d3dDevice ID3D10Device* d3dDevice; The next new line is creating a pointer to the swap chain interface. The IDXGISwapChain interface holds the front buffer and back buffer. the front and back buffer are essentially 2d textures which our scene is drawn on. the front buffer is what appears on the screen, and the back buffer is what is waiting to appear on the screen. When you display the scene onto the screen at the same time it is being calculated, the screen appears to flicker. To avoid the flickering effect, we draw our scene onto the back buffer first while it is being calculated, then after it is done being drawn onto the back buffer, we swap the front buffer and the back buffer, so the front buffer becomes the new back buffer, and the back buffer becomes the new front buffer. This way, we only display completed pictures onto the screen, so we do not watch while the scene is being calculated and drawn, which we call flickering. IDXGISwapChain* SwapChain; ID3D10RenderTargetView is the interface which gets the resource view and sends it to the pipeline stage we want. To bind the back buffer to the output merger stage so direct3d can render onto it, we need to create a render target view for the back buffer. ID3D10RenderTargetView* RenderTargetView; The next couple variable declarations are just for the fun of making the screen change colors, they are not actually needed in the initialization of direct3d, so after this lesson we will be removing them. float red = 0.0f; float green = 0.0f; float blue = 0.0f; int colormodr = 1; int colormodg = 1; int colormodb = 1; These Lines are just declaring the InitializeDirect3dApp, InitScene, and DrawScene functions globaly so we can use them in other function. bool InitializeDirect3dApp(HINSTANCE hInstance); bool InitScene(); void DrawScene(); Here we are calling the InitializeDirect3dApp() function from winmain. If the function did it's job correctly without errors or problems, we move onto the message loop, otherwise a message box pops up and lets us know something went terribly wrong, and exits the program. if(!InitializeDirect3dApp(hInstance)) { MessageBox(0, L"Direct3D Initialization - Failed", L"Error", MB_OK); return 0; } Here we are calling the InitScene() function from winmain. If the function did it's job correctly without errors or problems, we move onto the message loop, otherwise a message box pops up and lets us know something went terribly wrong, and exits the program. if(!InitScene()) { MessageBox(0, L"Scene Initialization - Failed", L"Error", MB_OK); return 0; } This is the InitializeDirect3dApp() function. It is the heart of our direct3d initialization, all initailization is done here! bool InitializeDirect3dApp(HINSTANCE hInstance) { Here is where we describe our swap chain by filling out a DXGI_SWAP_CHAIN_DESC structure. The structure looks like this: typedef struct DXGI_SWAP_CHAIN_DESC { DXGI_MODE_DESC BufferDesc; DXGI_SAMPLE_DESC SampleDesc; DXGI_USAGE BufferUsage; UINT BufferCount; HWND OutputWindow; BOOL Windowed; DXGI_SWAP_EFFECT SwapEffect; UINT Flags; } DXGI_SWAP_CHAIN_DESC; DXGI_SWAP_CHAIN_DESC scd; First we start out by filling out the BufferDesc, which is a DXGI_MODE_DESC structure. This structure defines the general properties of our back buffer. Here is the structure: typedef struct DXGI_MODE_DESC { UINT Width; UINT Height; DXGI_RATIONAL RefreshRate; DXGI_FORMAT Format; DXGI_MODE_SCANLINE_ORDER ScanlineOrdering; DXGI_MODE_SCALING Scaling; } DXGI_MODE_DESC, *LPDXGI_MODE_DESC; 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; The second part of the structure is the SampleDesc, which is the number of samples or the quality. This has to do with antializing, wich we are not worried about right now. scd.SampleDesc.Count = 1; scd.SampleDesc.Quality = 0; Next line is the BufferUsage structure. We specify DXGI_USAGE_RENDER_TARGET_OUTPUT since our back buffer will be the target to render to. scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; BufferCount is the number of back buffers to use in our swap chain. We are using one back buffer for double buffering, but we could use two for triple buffering. scd.BufferCount = 1; OutputWindow is the handle to our window we initialized in the last lesson. scd.OutputWindow = hwnd; We give windowed a true or false, true for if we want our window to be just a window, or false if we want fullscreen. scd.Windowed = true; SwapEffect is how we want to swap our front and back buffers. DXGI_SWAP_EFFECT_DISCARD will let the display driver the most efficient presentation technique for the swap chain. Then next line is any extra flags we might want, look it up if your interested in learning more about it, we don't need it for our lessons. scd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; scd.Flags = 0; Here we actually create the device and swap chain by filling out a D3D10CreateDeviceAndSwapChain structure. This is the function: HRESULT D3D10CreateDeviceAndSwapChain( __in IDXGIAdapter *pAdapter, __in D3D10_DRIVER_TYPE DriverType, __in HMODULE Software, __in UINT Flags, __in UINT SDKVersion, __in DXGI_SWAP_CHAIN_DESC *pSwapChainDesc, __out IDXGISwapChain **ppSwapChain, __out ID3D10Device **ppDevice ); The first parameter, pAdapter, spcifies the display adapter we want the created device to represent. a 0 or NULL uses the primary display adapter. D3D10CreateDeviceAndSwapChain(0, The DriverType can be set to either D3D10_DRIVER_TYPE_HARDWARE or D3D10_DRIVER_TYPE_REFERENCE. Hardware is our graphics card, and reference is software. We only use reference if our graphics card cannot use certain code. using software implementation is MUCH MUCH slower, so you would only use software implementation for testing purposes. D3D10_DRIVER_TYPE_HARDWARE, Software is set to NULL as we are using hardware rasterizing. You need to have a software rasterizer available in order to use one. 0, Flags are optional device creation flags. Usually you will keep this as 0 for release builds. 0, SDKVersion is always set to D3D10_SDK_VERSION. D3D10_SDK_VERSION, pSwapChainDesc is a pointer to our filled out DXGI_SWAP_CHAIN_DESC structure which describes the swap chain we want to make. &scd, ppSwapChain returns the created swap chain. &SwapChain, The last parameter, ppDevice returns the created device. &d3dDevice); We create a 2d texture for the back buffer to render our scene onto. ID3D10Texture2D* backBuffer; This line is where we give our back buffer to the swap chain. The back buffer is a COM object, which is why it is released at the end of the code. SwapChain->GetBuffer(0, _uuidof(ID3D10Texture2D), reinterpret_cast(&backBuffer)); Here we create the render target view by calling the CreateRenderTargetView method of the our d3dDevice structure. The first parameter specifies which resource will be used as the render target. The second is a pointer to a D3D10_RENDER_TARGET_VIEW_DESC. If the resource was created with with a typed format, then this can be NULL, if not, we must specify the format type of the render target here. The third parameter returns a pointer to the created render target view object. After that, we release the backBuffer COM object. d3dDevice->CreateRenderTargetView(backBuffer, 0, &RenderTargetView); backBuffer->Release(); We have finished making the back buffer and depth/stencil buffer so we need to send it to the output merger stage of the pipeline to make the resources the render target and depth/stencil buffer of the pipeline! The first parameter is the number of render targets we are sending to the pipeline. We are only using one, but there is an advanced technique to bind more than one simultaneously to several render targets. The second parameter is a pointer to the first element in an array of render target view pointers to bind to the pipeline. The third is a pointer to the depth/stencil view to bind to the pipeline. d3dDevice->OMSetRenderTargets(1, &RenderTargetView, NULL); Next we create the viewport. The first line initializes our viewport. This is the structure: typedef struct D3D10_VIEWPORT { INT TopLeftX; INT TopLeftY; UINT Width; UINT Height; FLOAT MinDepth; FLOAT MaxDepth; } D3D10_VIEWPORT; The first four descripe the rectangle of how big and where our viewport is drawn onto the back buffer. Usually we will cover the entire back buffer, but it's possible you only want to cover a section of the back buffer. I encourage you to play with these setting and see exactly what they do. The fifth specifies the minimum depth value in the buffer, and the last one specifies the maximum depth value in the buffer. D3D10_VIEWPORT vp; vp.TopLeftX = 0; vp.TopLeftY = 0; vp.Width = Width; vp.Height = Height; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; We have created our viewport, now we need to set the viewport with direct3d using the ID3D10Device::RSSetViewports method. First parameter is the number of viewports, and the second is the pointer to our viewport. d3dDevice->RSSetViewports(1, &vp); The next line creates a black D3DXCOLOR which we will use to clear the back buffer with. The line after that clears the back buffer to the color we created. The first parameter is the target view we are clearing, and the second is the color we are clearing it to. The line after that presents our back buffer. Then of course if the program has made it this far, direct3d is finally initialized and we can exit this function safely and without errors using the return true thing. Direct3d is fully initailized now, and we can start doing the fun parts! D3DXCOLOR bgColor( 0.0f, 0.0f, 0.0f, 1.0f ); d3dDevice->ClearRenderTargetView(RenderTargetView, bgColor); SwapChain->Present(0, 0); return true; } The InitScene function is where we will prepare the necessary things before we draw our scene. Nothing Goes in it for now, but later we will be using it. bool InitScene() { return true; } Here is the function we will be calling to draw our scene. Right now nothing is in it because I added it after I initially made the lesson. but we could put everything in the message loop into this function, then call this function from the message loop by simply adding this line: DrawScene(); void DrawScene() { //Draw Scene Here } I'm not going to go through each line here, as it's somewhat self-explanatory if you've made it this far into the lesson, but essencially what it does is first ads or subtracts a value to each color, depending on what colormod is. then it checks to see if any of the colors is over or under the color limit (1 or 0). after that, we create the color using a D3DXCOLOR. Then we clear the back buffer to the color, then we finally present the color. This is in the message loop of course, where all the fun lies! red += colormodr * 0.00005f; green += colormodg * 0.00002f; blue += colormodb * 0.00001f; if(red >= 1.0f || red <= 0.0f) colormodr *= -1; if(green >= 1.0f || green <= 0.0f) colormodg *= -1; if(blue >= 1.0f || blue <= 0.0f) colormodb *= -1; D3DXCOLOR bgColor( red, green, blue, 1.0f ); d3dDevice->ClearRenderTargetView(RenderTargetView, bgColor); SwapChain->Present(0, 0); That's it! We can finally get onto the less boring parts of game programming with DirectX! Oh yeah, if you get an error like this 1>main.obj : error LNK2019: unresolved external symbol _D3D10CreateDeviceAndSwapChain@32 referenced in function "bool __cdecl InitializeDirect3dApp(struct HINSTANCE__ *)" (?InitializeDirect3dApp@@YA_NPAUHINSTANCE__@@@Z) 1>C:\Users\Halcyon\documents\visual studio 2010\Projects\Base Code\Debug\Base Code.exe : fatal error LNK1120: 1 unresolved externals then you need to add D3D10.lib to the linker. go to Project properties -> Linker -> Input -> Additional Dependencies Also try to add the line #pragma comment(lib, "D3D10.lib") to the top of your code. Here's the final code: main.cpp #pragma comment(lib,"d3d10.lib") #include <Windows.h> #include <d3dx10.h> #include <string> LPCTSTR WndClassName = L"firstwindow"; HWND hwnd = NULL; const int Width = 300; const int Height = 300; bool InitializeWindow(HINSTANCE hInstance, int ShowWnd, int width, int height, bool windowed); ///////////////new//////////////////// ID3D10Device* d3dDevice; IDXGISwapChain* SwapChain; ID3D10RenderTargetView* RenderTargetView; float red = 0.0f; float green = 0.0f; float blue = 0.0f; int colormodr = 1; int colormodg = 1; int colormodb = 1; bool InitializeDirect3dApp(HINSTANCE hInstance); bool InitScene(); void DrawScene(); ///////////////new//////////////////// int messageloop(); LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); 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; } ///////////////new//////////////////// 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; } ///////////////new//////////////////// messageloop(); 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; } ///////////////new//////////////////// bool InitializeDirect3dApp(HINSTANCE hInstance) { 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); D3D10_VIEWPORT vp; vp.TopLeftX = 0; vp.TopLeftY = 0; vp.Width = Width; vp.Height = Height; vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; d3dDevice->RSSetViewports(1, &vp); D3DXCOLOR bgColor( 0.0f, 0.0f, 0.0f, 1.0f ); d3dDevice->ClearRenderTargetView(RenderTargetView, bgColor); SwapChain->Present(0, 0); return true; } bool InitScene() { return true; } void DrawScene() { //Draw Scene Here } ///////////////new//////////////////// 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 red += colormodr * 0.00005f; green += colormodg * 0.00002f; blue += colormodb * 0.00001f; if(red >= 1.0f || red <= 0.0f) colormodr *= -1; if(green >= 1.0f || green <= 0.0f) colormodg *= -1; if(blue >= 1.0f || blue <= 0.0f) colormodb *= -1; D3DXCOLOR bgColor( red, green, blue, 1.0f ); d3dDevice->ClearRenderTargetView(RenderTargetView, bgColor); SwapChain->Present(0, 0); } } 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); }
Sign in to comment