03 - Starta och Avsluta DirectGraphics

Starta och Avsluta DirectGraphics


Nu har vi vår fina lilla header-fil, innehållande vår "grafik-motor" klass. Då är det väl dax att börja implementera funktionerna.

Skapa källkodsfilen 'Graphics.cpp' och så kan vi ju börja lite enkelt och implementera konstruktorn och destruktorn först.
  1.  
  2. #include "Graphics.h"
  3.  
  4. GraphicsEngine::GraphicsEngine()
  5. {
  6. m_pD3D = NULL;
  7. m_pDevice = NULL;
  8. }
  9.  
  10. GraphicsEngine::~GraphicsEngine()
  11. {
  12. Shutdown();
  13. }

Vi börjar med att inkludera vår header-fil som vi gjorde i föregående kapitel. Konstruktorn nollställer bara pekarna så att dom inte pekare på nått. Det gör vi för att när programmet avslutas ska kunna avgöra om pekarna pekar på något och isåfall städa upp det. Destruktorn kör bara medlemsfunktionen 'Shutdown()'.

Ok, då var det dax att implementera själv huvudfunktionen, 'Initialize()'. Den tar en parameter och det är ett 'handle' till det skapade Windows fönstret.
  1.  
  2. bool GraphicsEngine::Initialize(HWND hWnd)
  3. {
  4. if((m_pD3D = Direct3DCreate9(D3D_SDK_VERSION)) == NULL)
  5. {
  6. return false;
  7. }

'Direct3DCreate9()' försöker skapa en instans av huvud-DirectGraphics objektet. Detta objekt används för att skapa sk. devices och mycket annat. Om den lyckas kommer 'm_pD3D' pekaren att peka på den nya instansen av DirectGraphics objektet. Annars returnera den ett false, vilket innebär att funktionen misslyckades.

DirectGraphics Device


En 'device' i DirectGraphics är ett objekt som representerar grafikkortet i din maskin. För att starta upp DirectGraphics måste vi fylla i en struct med massa parametrar, likt wndclass structen för Windows fönster. Den skickar vi sedan som parameter till en medlemsfunktion i 'm_pD3D' objektet som i sin tur skapar en 'device' (förutsatt att alla parametrar är ok).
  1.  
  2. D3DPRESENT_PARAMETERS d3dpp;
  3. ZeroMemory(&d3dpp, sizeof(d3dpp));
  4.  
  5. d3dpp.Windowed = true;
  6. d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
  7. d3dpp.EnableAutoDepthStencil = true;
  8. d3dpp.AutoDepthStencilFormat = D3DFMT_D24X8;
  9. d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
  10. d3dpp.hDeviceWindow = hWnd;
  11. d3dpp.BackBufferCount = 1;
  12. d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
  13. d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
  14.  
  15. if(FAILED(m_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &m_pDevice)))
  16. {
  17. return false;
  18. }
  19.  
  20. return true;
  21. }

För att kunna förstå vad varje utav dessa parametrar gör så bör jag nog förklara ett par koncept först.

Backbuffer och Swapchain


En backbuffer är en minnesbuffer på grafikkortets minne. En swapchain är en kedja utav dessa buffrar. En utav buffrarna i kedjan kallas för front buffern och är den som är främst i kedjan. Det är även den buffern som syns på skärmen. När ditt program ritar saker så ritas dom först till en backbuffer. Sen presenteras backbuffern till front buffer med hjälp av en sk. swap effekt. Denna effekt kan vara att buffrarna byter plats med varandra t.ex.

Bufferformat


Buffrarna är ju egentligen enbart allokerade minnesareor och bufferformat talar helt enkelt om hur mycket minne varje pixel in en buffer ska ta upp. Ett vanlig format är 'D3DFMT_A8R8G8B8' som allokerar 8-bit per färgkomponent (RGB) samt 8-bit för en alpha-kanal, totalt 32-bit per pixel.

Depth och Stencil buffer


En depth buffer (även kallad Z buffer) används för att bestämma på vilket djup i scenen som varje pixel har blivit utritad på. När varje pixel för ett 3d objekt ritas ut som färger i backbuffern så sätts även varje pixels z-djup (förutsatt att man använder en depth buffer) i depthbuffern. Varför vill man använda detta då? Jo, för då kan man effektivt veta om en pixel skall ritas ut eller inte. Eftersom 3d objekt kan ligga framför eller bakom varandra i 3d-rymden. Så har pixeln redan ett z-värde som är framför det den tänker rita ut så struntar den i det.

Stencilbuffrar liknar depthbuffrar en smula. Med en stencilbuffer kan sätta upp ett eget "mönster" som den ska leta efter och antingen neka eller tillåta utritning av pixlar. Detta kan man använda sig utav till effekter bland annat. Jag kommer gå in djupare på stencilbuffrar i senare delar av den här serien.

Nu tänkte jag förklara vad parametrarna betyder. Jag sätter enbart nåra av dom parametrar som finns i 'D3DPRESENT_PARAMETERS', resten får du läsa om i SDK dokumentationen.

Sådär, nu är det bara själva 'CreateDevice()' anropet kvar. Jag ska förklara vad parametrarna till funktion, men först bör ni veta vad HAL och REF är.

HAL och REF


HAL står för 'Hardware Abstraction Layer' och är lagret som DirectGraphics använder för att komma åt ditt grafikkort genom. På så sätt kan DirectGraphics använda sig utav dess kraft för utritning och kalkylering av 3D grafik.
REF står för 'Reference Rasterizer' och emulerar alla DirectGraphics funktioner i mjukvara. Detta är väldigt slött, men allt som DirectGraphics är kapabel till kan emuleras via REF. Används mest för att debugga.

  1.  
  2. HRESULT CreateDevice(UINT Adapter,
  3. D3DDEVTYPE DeviceType,
  4. HWND hFocusWindow,
  5. DWORD BehaviorFlags,
  6. D3DPRESENT_PARAMETERS *pPresentationParameters,
  7. IDirect3DDevice9 **ppReturnedDeviceInterface
  8. );

Så ser funktionshuvudet ut för 'CreateDevice()'.


Sådär, inte så farligt svårt va? Nu återstår det bara att rensa upp efter DirectGraphics. Det gör vi i funktionen 'Shutdown()'.

  1.  
  2. void GraphicsEngine::Shutdown()
  3. {
  4. if(m_pD3D) { m_pD3D->Release(); m_pD3D = NULL; }
  5. if(m_pDevice) { m_pDevice->Release(); m_pDevice = NULL; }
  6. }


Detta är grunden för vår motor. Denna kod sätter igång DirectGraphics och städar sedan upp efter sig. Inte så upphetsande kanske, men det måste ju göras.

Det var allt jag tänkte gå igenom nu. I nästa artikel så tänkte jag ta upp vertex och index buffrar, alltså hur man kan få ut trianglar på skärmen.

Ha det så bra tills dess!

Källa: http://blinkenlights.se/