2010. február 7., vasárnap

Tao Start 03 - Vetítések

Múltkor ott hagytuk abba, hogy sikerült egy szép sínes háromszöget kirajzolnunk a képernyőre. A kezdeti öröm mára már biztos elmúlott, így ideje továbblépnünk. Ehhez viszont újabb fogalmakkal kell megismerkednünk. Kezdjünk is hozzá.

A viewport definiálása

A viewport az az ablakon belüli rész, ahová rajzolunk, tehát a viewport transzformációval adjuk meg a létrejövő kép méretét. A viewport általában maga az ablak, de elképzelhető az is, hogy a viewport az ablaknak csupán egy része. A következő két ábra közül az egyik olyan viewportot ábrázol, amely az egész ablakot lefedi, a másiknál a viewport a létrehozott ablak bal alsó sarkánál helyezkedik el:

Az aktuális viewportot a:

void glViewport (int x , int y , GLsizei width , GLsizei height);

eljárással specifikálhatjuk. Az eljárás egy téglalapot definiál az OpenGL ablakba, ahová rajzolni szeretnénk. Az (x, y) paraméterek specifikálják a viewport bal alsó sarkát, width és height pedig a viewport téglalap méretét. Alapértelmezésben a paraméterek a (0, 0, winWidth, winHeight) értékeket veszik fel, ahol winWidth és winHeight az ablak méretei. pl.: glViewport(0, 0, w, h) esetén ha az ablak szélessége w, magassága pedig h, akkor a viewport az ablak lesz.

Vetítési mátrixok

Ahhoz, hogy kockát tudjunk rajzolni, hogy szépen látszódjon vetítést kell megadni. Erre az OpenGL-ben kétfajta lehetőség van: a merőleges és a centrális vetítés. Itt némi kiegészítésre van szükség a vetítésekkel kapcsolatosan: a két alapvető vetítési fajtán belül még igen sokféle különböző vetítést meg lehet adni; perspektív vetítésnél a vetítéseket az elsődleges távlatpontok száma szerint osztályozzuk, párhuzamos vetítés esetén pedig a vetítési irány és a vetítési sík egymáshoz való viszonya szerint. A vetítéseknek az a lényege, hogy a teret síkra képezik le (a képernyőre). Egy vetítés beállításához meg kell adni egy térrészt, amit a képernyőre képez (az ezen kívül eső részt a rendszer egyszerűen levágja). Ez merőleges vetítésnél egy téglatest párhuzamos vetítésnél csonka gúla. Alapértelmezés szerint merőleges vetítés van és a már említett origó középpontú 2x2x2-es kocka az a térrész, amit leképez. Merőleges vetítésnél úgy képez a rendszer, hogy a távolság nem érzékelhető, tehát egyszerűen elhagyja a mélységi (z) koordinátáját, majd a képernyőre méretezi a tartományt. Ez a vetítés általában a szerkesztőprogramokban hasznos, meg, ha csak egyszerű 2D-s dolgokat szeretnénk rajzolni. Ebből már sejthető is, hogy mi többnyire nem ezt fogjuk használni. A centrális vetítésnél már érzékelhető a távolság: a közeli dolgok nagyobbak a távoliak kisebbek. Ebből következik, hogy a távolabbi dogokból több fér egy képernyőre, mint közelikből. Sőt, ha a szemet egy pontnak vesszük, akkor, ha közvetlen a szem elé rajzolunk egy pontot, az óriásinak tűnhet (akár az egész képet kitakarhatja). Ezért kell megadni egy közeli vágósíkot, aminél közelebbi dolgok már nem látszanak. A merőleges és a centrális vetítés megadására az alábbi függvények szolgálnak:
  1. centrális - gluPerspective()
  2. merőleges - glOrtho()
Akkor most nézzük át a paramétereket. Először a glOrtho, itt 6 db lebegőpontos értéket kell megadni, ami meghatározza a vetítési téglatestet:

void glOrtho(double left, double right, double bottom, double top, double near, double far);

eljárással a párhuzamos vetítés látóterét specifikálhatjuk.

A párhuzamos vetítés paraméterei

A left és right adják meg a baloldai és jobboldali függőleges vágósíkok koordinátáit. A bottom és top az alsó és felső vízszintes vágósíkok koordinátái. A near és far adják meg a közeli és távoli vágósíkok távolságát a szemtől. A (left, bottom, -near) és a (right, top, -near) specifikálják tehát a közeli vágósík pontjait, amelyek ráfeszülnek az ablak bal alsó és jobb felső sarkaira ( feltételezve, hogy a szem a (0, 0, 0) pontban van). A far adja meg a távoli vágósík távolságát a szemtől. Tehát a közeli vágósík ablakba eső részének bal alsó sarka: (left, bottom, -near), a jobb felső sarka: (right, top, -near). A távoli vágósík ablakba eső részének bal alsó sarka: (left, bottom, far), jobb felső sarka: (right, top, far). Ha kétdimenziós objektumokat akarunk rajzolni, akkor a

void gluOrtho2D(double left , double right, double bottom, double top);

eljárással is specifikálhatjuk a vetítési mátrixot. Ekkor a vágási téglalap egyszerűen a (left, bottom, right, top) koordinátákkal megadott téglalap. A glOrtho / gluOrtho2D megszorozza az aktuális projekciómátrixot a specifikált mátrixszal és ez lesz az új projekciómátrix. A perspektív vetítési mátrix specifikációja a

void gluPerspective(double fovy, double aspect, double near , double far);

eljárás segítségével adható meg, amely szimmetrikus látóteret specifikál.

A perspektivikus vetítés paraméterei

A fovy adja meg a látótér szögét az x-z sík irányában amit szögben és nem radiánban kell megadni (45-60-90 fok körülire szokás állítani), az aspect a vágási téglalap szélességének és magasságának arányát (aspect=ablakszélesség/ablakmagasság), a near és far pedig a vágósíkok távolságát.
Nyugi nem olyan bonyolult ez, mint amilyen bonyolultan most leírtam. Nézzünk egy kis kódot, hátha úgy érthetőbb lesz: van ugye az előző leckében létrehozott egyszer programocskánk. A következő metódussal kell kiegészítenünk a programunkat:
  private void Reshape()
  {
      if (glControl.Height == 0)
      {
          ClientSize = new Size(glControl.Width, 1);
      }

      float w = glControl.Width;
      float h = glControl.Height;

      Gl.glMatrixMode(Gl.GL_PROJECTION);
      Gl.glLoadIdentity();

      Gl.glViewport(0, 0, (int)w, (int)h);
      Glu.gluPerspective(45, w / h, 0.1, 1000);

      Gl.glMatrixMode(Gl.GL_MODELVIEW);
      Gl.glLoadIdentity();
  }
A Reshape() metódus segítségével mindig beállítjuk a viewport-ot és az aspect ratio értékét. Ezért majd a Form OnResize metódusába is meghívjuk. Mivel az aspect=w/h ezért ellenőrizni kell, hogy a h ne vegyen fel soha nulla értéket. Ezután már csak a gluPerspective az egyetlen ismeretlen parancs. Először nézzük akkor ezt. Ahogy látszik 45 fok a látószög, a képarány w/h, a közeli vágósík a nézőponttól (szem) 0.1 távolságra van ennél közelebbi dolgok nem látszanak, a távoli vágósík 1000 távolságra van (jó messze). Ezután meg kell hívnunk a Render() metódust a Reshape() után az OnResize metódusban. A Render eljárást egy sorral kell még egészítenünk:

Gl.glTranslatef(0.0f, 0.0f, -5.0f);

Nos ezek a mátrixos dolgok azok, amik az említett szükséges kevés matematikai tudás egy részét képezik. Róluk részletesen a transzformációkról szóló részben írok (következő lecke, nem lesz nehéz, de nagyon fontos). Csak, hogy tudjátok:
  • glLoadIdentity - betölti az egységmátrixot (ezt teszi aktívvá)
  • glMatrixMode - kijelöli az aktuális mátrixmódot (amin majd operálunk)
  • glTranslatef - a megadott irányba eltolja az egész "világot"
erről részletesebben a következő leckében. Most azért kell, mert amit rajzoltunk téglalapot az a közeli vágósík mögött volt és ezért a világot eltoltuk 5 egységgel, hogy beleessen a látóterünkbe a megjelenítendő színes téglalapunk. Most ha lefuttatjátok a kódot láthatjátok, hogy a téglalap összement sőt most már négyzet (hisz a koordinátái alapján valóban négyzetnek kell lennie). És, ha a világeltolást változtatjátok, úgy változik a mérete. Ennyi lenne ez a lecke. A példa teljes forrása itt tölthető le:


A fenti tutorial PowR által írt a http://free-pascal.extra.hu/ oldalon megtalálható OGL tutorial alapján készült el. Köszönet érte PowR-nek, valamint Kuba Attila - OGL Programozása jegyzetének!

1 megjegyzés :

  1. Szia!
    Először is szeretném megköszönni munkádat, az itteni cikkek rengeteget segítenek. Hogy őszinte legyek, nem a köszönetnyílvánítás miatt írtam elsősorban, hanem azért, mert akadt egy kis gondom a vetítésekkel. Egész pontosan a GL.Translate() függvénnyel. OpenTK-t használok Visual Studio 2010-zel. A lényeg az, hogy ha 1-nél nagyobb számot írok akár az x,y, vagy z koordinátához, nem jelenik meg a színes négyzetem, illetve a 'z' értékének módosításával látszólag semmi nem történik. Nem igazán látom mi a probléma. Linkelek egy forráskódot is: Forrás az itt kikommentelt függvénnyel van problémám.
    Segítséged előre is köszönöm.
    Üdv: Attila

    VálaszTörlés