Hlavní navigace

OpenGL a Direct3D II

11. 8. 2004
Doba čtení: 4 minuty

Sdílet

Knihovny jako OpenGL nebo DirectX nejsou pro uživatele až tak důležité, pro ně jsou to jenom dvě prázdná jména. Může jim být naprosto jedno, jak program uvnitř funguje. Návrh a hlavně celková použitelnost API je důležitá primárně pro programátory. Právě oni s ním pracují, nikdo jiný. V tomto dílu budeme porovnávat především délku kódu, který se musí napsat, aby program něco udělal.

Programátorské hledisko

Na rozdíl od strukturovaného OpenGL je Direct3D objektově orientované. Jaký je mezi tím rozdíl? V případě OpenGL se na začátku programu vytvoří okno s jeho podporou a pak je možné odkudkoli volat jakékoli OpenGL specifické funkce. V případě Direct3D se musí vytvořit objekt Direct3D (IDirect3D9), s jeho pomocí zařízení Direct3D (IDirect3DDevice9) a přes volání jejich metod programátor pracuje. Objektově orientované programování sice preferuji také, ale v případě DirectX mi připadá hodně těžkopádné – alespoň v porovnání s OpenGL.

Jedním z nejdůležitějších charakteristik jakéhokoli API je pro programátora délka zdrojového kódu, který musí napsat, aby program něco udělal. John Carmack, autor her Quake, uvádí [3], že kód programu plnící stejnou funkci je v Direct3D i čtyřikrát delší než v OpenGL. Můžeme si to bez problémů ověřit. Z učebnice Direct3D [1], str. 180, 181 uvedu jednoduchou renderovací funkci, která na černém pozadí vykresluje jeden obarvený trojúhelník – bez textur, světel či jakýchkoli maticových operací (translace, rotace). Následně vše přepíši do OpenGL.

///////////////////////////////////////////
// Direct3D 9.0
///////////////////////////////////////////

struct VLASTNIVERTEX
{
  FLOAT x, y, z, rhw;
  DWORD barva;
}

void Vykresleni()
{
  VLASTNIVERTEX vrchTrojuhl[] =
  {
    {400.0, 180.0, 0.0, 1.0, D3DCOLOR_XRGB(255,0,0)},
    {500.0, 380.0, 0.0, 1.0, D3DCOLOR_XRGB(0,255,0)},
    {300.0, 380.0, 0.0, 1.0, D3DCOLOR_XRGB(0,0,255)},
  };

  IDirect3DVertexBuffer9* pVertexBuffer = NULL;
  HRESULT hVysledek = g_pZarizeniDirect3D->CreateVertexBuffer(
    3*sizeof(VLASTNIVERTEX), 0, D3DFVF_XYZRHW | D3DFVF_DIFFUSE,
    D3DPOOL_DEFAULT, &pVertexBuffer, NULL);

  if (FAILED(hVysledek))
  {
    DXTRACE_ERR("Chyba při vytv. vertex bufferu.", hVysledek);
  }

  VOID* pVrcholy;
  hVysledek = pVertexBuffer->Lock(0, 0, (viod**)&pVrcholy, 0);
  if (FAILED(hVysledek))
  {
    DXTRACE_ERR("Chyba při zamyk. vertex bufferu.", hVysledek);
  }
  memcpy(pVrcholy, vrchTrojuhl, sizeof(vrchTrojuhl));
  pVertexBuffer->Unlock();

  g_pZarizeniDirect3D->Clear(0, NULL, D3DCLEAR_TARGET,
    D3DCOLOR_XRGB(0,0,0), 1.0, 0);
  g_pZarizeniDirect3D->SetStreamSource(0, pVertexBuffer, 0,
    sizeof(VLASTNI_VERTEX));
  g_pZarizeniDirect3D->SetFVF(D3DFVF_XYZRHW | D3DFVF_DIFFUSE);
  g_pZarizeniDirect3D->BeginScene();
  g_pZarizeniDirect3D->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);
  g_pZarizeniDirect3D->EndScene();

  g_pZarizeniDirect3D->Present(NULL, NULL, NULL, NULL);

  if (pVertexbuffer)
    pVertexBuffer->Release();
} 

To samé v OpenGL…

///////////////////////////////////////////
// OpenGL
///////////////////////////////////////////

void Vykresleni()
{
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glLoadIdentity();

  glBegin(GL_TRIANGLES);
    glColor3ub(255,0,0); glVertex2i(400,480);
    glColor3ub(0,255,0); glVertex2i(500,380);
    glColor3ub(0,0,255); glVertex2i(300,380);
  glEnd();

  glFlush();
  SDL_GL_SwapBuffers();
} 

Pozn.: Při inicializaci se nesmí definovat perspektiva, ale pravoúhlá projekce (glOrtho()) o rozměrech 800×600. Funkce glClearColor() se obvykle dává také do inicializace, ve vykreslení je jen kvůli tomu, aby kód plně odpovídal Direct3D. Pokud provozujete OpenGL ve Win32 API, určitě používáte místo SDL_GL_SwapBuf­fers() funkci SwapBuffers(), SDL má tu výhodu, že lze kód bez větších problémů přenášet mezi Linuxem, Windows a dalšími operačními systémy.

RGB trojúhelník
Screenshot OpenGL verze programu

Vrátím se ještě k té délce kódu – v případě, že byste je chtěli porovnat číselně, pak D3D verze měla celkem 47 řádků, 108 slov a 1367 písmen, OpenGL pouze 14 řádků, 22 slov a 335 písmen. V poměru to činí u řádků 47 / 14 = 3.35, slov 108 / 22 = 4.9 a znaků 1367 / 335 = 4.08 …čtyřnásobná délka kódu tedy plus mínus platí. Zčásti je to i tím, že v D3D sekci byly použity dlouhé identifikátory, ale za to já nemůžu, jak jsem napsal výše, jedná se o doslovný přepis z učebnice D3D. Dokonce jsem identifikátor vrcholyTrojuhelniku musel zkrátit pouze na vrchTrojuhl, aby se kód vešel na řádek.

[woq@localhost tmp]$ wc *txt
  47  108 1367 dx.txt
  14   22  335 gl.txt

Uživatele programu zdrojové kódy absolutně nezajímají a hlavně jim nerozumí, takže nepředpokládám, že jste předchozí výpisy zkoumali podrobně. Jenom tak jimi v rychlosti prolétněte a zkuste říci, který z nich je přehlednější. Dále si všimněte v D3D sekci kódu řádku if(FAILED(hVys­ledek)), který se provede při chybě ve vytváření/zamykání vertex bufferu. V OpenGL se toto dělat nemusí/nelze, a to ani při použití vertex arrays, které více odpovídají D3D stylu programování.

Nic podobného, jako je samostatné volání funkcí glVertex*(), Direct3D neumí. Vertex arrays jsou sice výhodnější při renderování velkých množství souvisejících vertexů, které jsou vždy na konstantní pozici (3D světy, výškové mapy, 3D modely ap.), protože odpadají ztráty na výkonu při mnoha volání funkcí, nicméně u pohyblivých nesouvisejících trojúhelníků, kde často ani nebývá dopředu znám jejich počet (typicky částicové systémy), bývá použití vertex arrays těžkopádné a spíše méně vhodné, navíc se zbytečně alokuje systémová paměť. V OpenGL si může programátor zvolit, co považuje za výhodnější. Pokud je chytrý a používá extensiony, může všechna data dokonce uložit v paměti grafické karty jako tzv. VBO, tím se eliminuje vliv „délky drátů“ při posílání dat na grafickou kartu. To ale pravděpodobně jde i v D3D.

Literatura

Autor článku

Backend programátor ve společnosti Avast, kde vyvíjí a spravuje BigData systém pro příjem a analýzu událostí odesílaných z klientských aplikací založený na Apache Kafka a Apache Hadoop.