OpenGL (13.) - Nastavení OpenGL v jazyce C/C++

V dnešním článku Vám popíši, na co všechno je třeba myslet při psaní kódu využívajícího knihoven OpenGL v C++. Pokud jste četli články ze seriálu OpenGL v Delphi tak máte napůl vyhráno. Pokud jste je nečetli, tak Vám to také vysvětlím...

O OpenGL v Céčku bylo již na anglickém internetu popsáno hodně stránek, v češtině se ale většinou jedná jen přeložené články ze serveru NeHe Tutorials. Nechci nikoho kritizovat, protože i překládání do češtiny je také velmi záslužná práce.

Pozn.: Píši jen o nastavení OpenGL v operačních systémech Windows, protože funkce začínající na wgl pro nastavení render contextu (viz. dále) nejsou podporovány v ostatních operačních systémech!!!

Hlavičkové soubory OpenGL knihovny (gl.h, glu.h) jsou umístěny v adresáři s projektem ve složce gl.

#include <gl/gl.h>      //Knihovna OpenGL32
#include <gl/glu.h>     //Knivona GLu32

Musíme deklarovat proměnné:

HDC hDC;             //GDI Device Context
HGLRC hRC;           //Rendering Context
HWND hWnd;           //Handle okna

Když jsem začínal v Delphi, zahrnoval jsem do programu i hlavičkový soubor glaux.h (glaux.pas). Tato knihovna převážně slouží k vykreslení složitějších geometrických obrazců jako jsou koule, torus, cylindr, Icosahedron, Octahedron, Tetrahedron, Dodecahedron, Cone a Teapot. Dokáže je vykreslit jak plné tak i jako drátěný model. Protože jsem ale funkce z této knihovny (GLaux - OpenGL auxiliary library) ještě nikdy nepoužil, neuvádím ji ve výpisu. Pokud ji v projektu potřebujete, můžete ji zahrnout, předpokládám ale že většina čtenářů jsou začátečníci a knihovnu potřebovat nebudou.

Device context

Dalším krokem inicializace OpenGL v C++ je získat device context, který obsahuje informace potřebné pro GDI (graphics device interface - součást operačního systému). Pokud vykreslujeme do okna, můžeme použít WinAPI funkce GetDC s parametrem handle okna.

hWnd = Canvas->Handle;
hDC = GetDC( hWnd );

Jakmile budeme chtít ukončit vykreslování, zavoláme jednoduše funkci RelaseDC:

RelaseDC( hWnd, hDC );

Pixel Format

Teď, když už máme device context, musíme udělat ještě jednu věc. je třeba nastavit pixelový formát získanému device contextu. Tím oznamujeme systému, jak budeme používat device context. Zde na nejnižší úrovni specifikujeme vlastnosti jako double buffering, z-buffering, color format, alpha buffer atd.

  static PIXELFORMATDESCRIPTOR pfd
    ZeroMemory(&pfd, sizeof( pfd ));
    pfd.nSize = sizeof( pfd );         //velikost struktury pixelu
    pfd.nVersion = 1;                  //cislo verze
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | //vykreslovani do okna
                  PFD_SUPPORT_OPENGL | //přes OpenGL
                  PFD_DOUBLEBUFFER;    //double buffer
    pfd.iPixelType = PFD_TYPE_RGBA;    //barevný model RGBA
    pfd.cColorBits = 24;               //24-bit barva
    pfd.cDepthBits = 16;               //16-bit depth buffer
    pfd.iLayerType = PFD_MAIN_PLANE;   //typ vrstvy
	
  int pixelformat;

  if ((pixelformat = ChoosePixelFormat(hDC, &pfd)) == 0)
  {
    MessageBox(NULL, "Špatně zvolený PixelFormat.", "Error", MB_OK);
  }

  if (SetPixelFormat(hDC, pixelformat, &pfd) == false)
  {
    MessageBox(NULL, "Chybně nastavený PixelFormat", "Error", MB_OK);
  }

V tomto případě jsem nastavil podporu pro OpenGL a double buffer s vykreslováním do okna. To stačí pro většinu aplikací využívajících OpenGL. Funkce ChoosePixelFormat a SetPixelFormat vyberou a nastaví PixelFormat. Obě funkce jsem ošetřil pro případ chyby.

Pozn.: Když používáme double buffering, tak OpenGL vše vykresluje na neviditelné místo do paměti (back buffer). Scéna se nezobrazí v okně, dokud my nedáme OpenGL příkaz, který prohodí ukazatel na back a front buffer. To je velmi užitečné, protože se scéna vykresluje mimo obrazovku a celý obraz se objeví naráz a nebliká. Po vykreslení celé tedy stačí napsat:

SwapBuffers( hDC );

Render context

Už máme device context a také jsme nastavili vhodný pixel format. Můžeme tedy začít inicializovat OpenGL a render context. Render context (hRC) vytvoříme voláním funkce wglCreateContext:

hRC = wglCreateContext( hDC );

Jakmile už nebudeme pracovat s rendering contextem, měly bysme ho smazat. Šetří se tím paměť systému:

wglDeleteContext( hRC );

V některých případech může být spuštěno více render contextů naráz. Musíme tedy OpenGL říct, který render context má být aktivní. K tomu slouží funkce wglMakeCurrent:

wglMakeCurrent( hDC, hRC );

Teď jsou všechny OpenGL příkazy směřovány do aktivního render contextu. Nakonec je potřeba nastavit render context na neaktivní. Toho docílíme tak, že zavoláme funkci wglMakeCurrent s parametry NULL:

wglMakeCurrent( NULL, NULL );

Pokud budete chtít uchovat předchozí device context a render contex můžete použít funkce wglGetCurrentDC and wglGetCurrentContext:

wglGetCurrentContext.
HDC hOldDC = wglGetCurrentDC();
HGLRC hOldRC = wglGetCurrentContext();
wglMakeCurrent( hDC, hRC );

//OpenGL příkazy

wglMakeCurrent( hOldDC, hOldRC );

Vše v jednom v C:

Už umíme získat device context, nastavit pixel format, vytvořit a nastavit render context. To vše můžeme dát dohromady ve dvou funkcích, které zapnou a vypnou OpenGL:

void EnableOpenGL(HWND hWnd, HDC * hDC, HGLRC * hRC)
{
  PIXELFORMATDESCRIPTOR pfd;
  int pixelformat;
  
  ZeroMemory(&pfd, sizeof( pfd ));
  pfd.nSize = sizeof( pfd );         //velikost struktury pixelu
  pfd.nVersion = 1;                  //cislo verze
  pfd.dwFlags = PFD_DRAW_TO_WINDOW | //vykreslovani do okna
                PFD_SUPPORT_OPENGL | //přes OpenGL
                PFD_DOUBLEBUFFER;    //double buffer
  pfd.iPixelType = PFD_TYPE_RGBA;    //barevný model RGBA
  pfd.cColorBits = 24;               //24-bit barva
  pfd.cDepthBits = 16;               //16-bit depth buffer
  pfd.iLayerType = PFD_MAIN_PLANE;   //typ vrstvy
	
  if ((pixelformat = ChoosePixelFormat(hDC, &pfd)) == 0)
  {
    MessageBox(NULL, "Špatně zvolený PixelFormat.", "Error", MB_OK);
  }

  if (SetPixelFormat(hDC, pixelformat, &pfd) == false)
  {
    MessageBox(NULL, "Chybně nastavený PixelFormat", "Error", MB_OK);
  }

  //Vytvoříme a nastavíme render context 
  *hRC = wglCreateContext( *hDC );
  wglMakeCurrent( *hDC, *hRC );
}

void DisableOpenGL(HWND hWnd, HDC hDC, HGLRC hRC)
{
  wglMakeCurrent( NULL, NULL );
  wglDeleteContext( hRC );
  ReleaseDC( hWnd, hDC );
}

Nyní můžeme mezi funkcemi EnableOpenGL a DisableOpenGL volat funkce OpenGL které potřebujeme. Pamatujte ale, že při zapnutém double bufferu musíte volat i funkci SwapBuffers;

Vše v jednom v C++:

Zde je ten samý kód pro C++. Zavoláním funkce Init se v aktuálním okně vytvoří nový render context. Potom můžete volat ostatní funkce OpenGL. Před ukončením okna by se ještě měla zavolat funkce Free. Výhodou je snadná inicializace při vytvoření okna.

class GLContext
{
public:
  GLContext()
  {
    reset();
  }
  
  ~GLContext()
  {
    Free();
  }

  void init(HWND mhWnd)
  {
    hWnd = mhWnd;
    hDC = GetDC( hWnd );

    PIXELFORMATDESCRIPTOR pfd;
  
    ZeroMemory(&pfd, sizeof( pfd ));
    pfd.nSize = sizeof( pfd );         //velikost struktury pixelu
    pfd.nVersion = 1;                  //cislo verze
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | //vykreslovani do okna
                  PFD_SUPPORT_OPENGL | //přes OpenGL
                  PFD_DOUBLEBUFFER;    //double buffer
    pfd.iPixelType = PFD_TYPE_RGBA;    //barevný model RGBA
    pfd.cColorBits = 24;               //24-bit barva
    pfd.cDepthBits = 16;               //16-bit depth buffer
    pfd.iLayerType = PFD_MAIN_PLANE;   //typ vrstvy
	
    if ((pixelformat = ChoosePixelFormat(hDC, &pfd)) == 0)
    {
      MessageBox(NULL, "Špatně zvolený PixelFormat.", "Error", MB_OK);
    }

    if (SetPixelFormat(hDC, pixelformat, &pfd) == false)
    {
      MessageBox(NULL, "Chybně nastavený PixelFormat", "Error", MB_OK);
    }
	
    //Vytvoříme a nastavíme render context 
    hRC = wglCreateContext( hDC );
    wglMakeCurrent( hDC, hRC );
  }

  void Free()
  {
    if ( hRC )
    {
      wglMakeCurrent( NULL, NULL );
      wglDeleteContext( hRC );
    }
    if ( hWnd && hDC )
    {
      ReleaseDC( hWnd, hDC );
    }
    reset();
  }

private:
  void reset()
  {
    hWnd = NULL;
    hDC = NULL;
    hRC = NULL;
  }

  HWND hWnd;
  HDC hDC;
  HGLRC hRC;
};

To je pro dnešek vše. V příštím se budeme přepínat do fullscreen módu a také napíšu i o jiných zajímavých vlastnostech OpenGL. Pokud máte otázku, komentář nebo připomínku, napište ji prosím do diskuse k tomuto článku a podělte se s ostatními o své zkušenosti. Hodně úspěchů při psaní ;-)

Vyšlo 20.07.2002, v blogu: 0 1 2 3 4 5 6 7 8

Děkuji, že jste se rozhodl(a) přečíst tento článek. Budu rád i za komentář. Pokud Vás tento článek zaujal a rádi byste jej doporučili ostatním, podpořte mně prosím tím, že věnujete minutku svého času a uděláte mi reklamu na linkuj.cz, vybrali.sme.sk či jagg.cz. Přeji příjemné čtení

Poslední články

Diskuse k blogu

Zatím nikdo nevložil komentář. Chcete být první? Přidání příspěvku
©PC-guru.cz 2000-2008 | Optimalizováno pro 1024*768