OpenGL v C++ Builderu 5



První trojúhelník

Příklad a zdroják ke stažení (226k)

V tomto tutorialu se dozvíte, jak vytvořit základní strukturu OpenGL aplikace a jak nakreslit trojúhelník.

Abychom mohli používat OpenGL příkazy, musíme do programu začlenit hlavičkové soubory gl.h a glu.h
#include <gl/gl.h>    //hlavičkový soubor pro užívání instrukcí z ovladače OpenGL32.dll
#include <gl/glu.h>    //hlavičkový soubor pro užívání instrukcí z ovladače GLu32.dll
Vytvoříme si formulář, do kterého budeme vykreslovat scénu.
void __fastcall TForm1::FormCreate(TObject *Sender)
{
    SetWindowStats(640, 480, 16);    //nastav rozlišení 640*480 16 bitovou hloubku barev

    m_hDC = GetDC(Handle);    //načti handle okna do m_hDC (Windows Device Context)
    SetPixelFormatDescriptor();    //nastav formát výstupniho pixelu
    m_hRC = wglCreateContext(m_hDC);    //vytvoř m_hRC (OpenGL Rendering Context)
    wglMakeCurrent(m_hDC, m_hRC);     //připoj m_hRC (OpenGL Rendering Context) do m_hDC (Windows Device Context), který komunikuje s wokenim GDI (Graphics Device Interface)

    FormResize(NULL);    //vyčisti okno před iniciací OpenGL

    InitGL();    //inicializuj OpenGL
}
m_hDC je proměnná typu HDC.
HDC (Windows Device Context), je kontext zařízení, který se připojuje do grafického rozhraní woken GDI (Graphics Device Interface).

m_hRC je proměnná typu HGLRC.
HGLRC (OpenGL Rendering Context) - připojuje OpenGL do HDC (Windows Device Context)
bool __fastcall TForm1::SetWindowStats(int iWidth_, int iHeight_, int iColor_)
{
    ClientWidth = iWidth_;    //přečti šířku okna
    ClientHeight = iHeight_;    //přečti výšku okna

    //zeptej se, jestli budu chtít okno přepnout do fullscreenu
    if(Application->MessageBox("Would You Like To Run In Fullscreen Mode?",
                               "Start FullScreen?",
                               MB_YESNO|MB_ICONQUESTION)==IDYES)
    {
      //pokud zadám Ano nastav tyto hodnoty
      DEVMODE dmScreenSettings;    //Device Mode - mód zařízení
      memset(&dmScreenSettings,0,sizeof(dmScreenSettings));    //vyčisti paměť
      dmScreenSettings.dmSize=sizeof(dmScreenSettings);    //velikost struktury
      dmScreenSettings.dmPelsWidth = iWidth_;    //nastav šířku obrazovky
      dmScreenSettings.dmPelsHeight = iHeight_;    //nastav výšku obrazovky
      dmScreenSettings.dmBitsPerPel = iColor_;    //nastav hloubku barev
      dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;

        //pokud jsou některé parametry špatně nastaveny
        if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
            {
        return false;    //tak opusť funkci
            }
      else    //jinak přepni na fullscreen a
      {
        BorderStyle = bsNone;    //nastav okno, aby bylo bez rámečků a tlačítek
        Left = 0;    //nastav levý okraj okna na 0
        Top = 0;    //nastav horní okraj okna na 0
        ShowCursor(FALSE);    //schovej kurzor myši
      }
    }

    return true;
}
Funkce SetWindowStats() nám vyplivne zprávu, ve které se nás zeptá, jestli budeme chtít aplikaci rozjet v okně, nebo ve fullscreenu. Podle volby, kterou si zvolíme pak změní rozlišení obrazovky a velikost formuláře.
void __fastcall TForm1::SetPixelFormatDescriptor()
{
    PIXELFORMATDESCRIPTOR pfd = {    //řekni woknům jak maj zobrazit obrazovkový pixel
      sizeof(PIXELFORMATDESCRIPTOR),    //velikost descriptoru
      1,    //číslo verze
      PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,    //formáty podporující wokna, OpenGL, nastavení doublebufferu
      PFD_TYPE_RGBA,    //používej barvy ve formátu RGBA
      24,    //buffer barev
      0,0,0,0,0,0,
      0,0,
      0,0,0,0,0,
      32,    //32Bit Z-Buffer (Depth buffer)
      0,    // Stencil buffer
      0,
      PFD_MAIN_PLANE,    //vykreslovací vrstva
      0,
      0,0,0
      };
    m_iPixelFormat = ChoosePixelFormat(m_hDC, &pfd);    //vyber nejvhodnější pixelový formát
    SetPixelFormat(m_hDC, m_iPixelFormat, &pfd);    //a tento pixelový formát nastav
}
Funkce SetPixelFormatDescriptor() nastavuje formát pixelu obrazovky
bool __fastcall TForm1::InitGL()
{
  wglMakeCurrent(m_hDC, m_hRC);    //připoj m_hRC (OpenGL Rendering Context) do m_hDC (Windows Device Context), který komunikuje s wokenim GDI (Graphics Device Interface)
    glClearColor(0.0f, 0.0f, 0.0f, 0.5f);    //nastav černé pozadí scény
  wglMakeCurrent(m_hDC, NULL);    //vynuluj m_hRC (OpenGL Rendering Context)
     return TRUE;
}
Funkce InitGL() vytvoří základní scénu s čeným pozadím. Barva pozadí se nastavuje ve funkci glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
Hodnoty barev jsou glClearColor(red, green, blue, alfa);
void __fastcall TForm1::Loop()
{
    wglMakeCurrent(m_hDC, m_hRC);    //připoj m_hRC (OpenGL Rendering Context) do m_hDC (Windows Device Context), který komunikuje s wokenim GDI (Graphics Device Interface)
    RenderGLScene();    //vykresli OpenGl scénu
    SwapBuffers(m_hDC);    //prohoď buffery (double burrefing)
    wglMakeCurrent(m_hDC, NULL);    //vynuluj m_hRC (OpenGL Rendering Context)
}
Funkce Loop() běží v nekonečné smyčce a volá funkci RenderGLScene(); ve které budeme vykreslovat objekty.

Funkce Loop() běží v odděleném procesu TThread. Priorita vlákna TThread je nastavena na maximum Priority = tpTimeCritical;, aby jiná činnost systému nezpomalovala překreslování OpenGL objektů.
//---------------------------------------------------------------------------
TMyThread *RenderThread;    //definice proměnné RenderThread
//---------------------------------------------------------------------------

__fastcall TMyThread::TMyThread(bool CreateSuspended): TThread(CreateSuspended)
{
    //Priority = tpIdle;    //vlákno je prováděno, když systém je nečinný
    //Priority = tpLowest;    //priorita vlákna je dva body pod normálem
    //Priority = tpLower;    //priorita vlákna je jeden bod pod normálem
    //Priority = tpNormal;    //vlákno má normálni prioritu
    //Priority = tpHigher;    //priorita vlákna je jeden bod nad normálem
    //Priority = tpHighest;    //priorita vlákna je dva body nad normálem
    Priority = tpTimeCritical;    //vlákno získá nejvyšší prioritu
}
//---------------------------------------------------------------------------

void __fastcall TMyThread::Render()
{
    Form1->Loop();    //volej funkci pro překreslování scény
}

//---------------------------------------------------------------------------

void __fastcall TMyThread::Execute()
{
   while (!Terminated) Synchronize(Render);    //pokud neni vlákno ukončeno volej funkci Render
}

//---------------------------------------------------------------------------

void __fastcall TMyThread::Run()
{
  Resume();    //rozjeď vlákno
}

//---------------------------------------------------------------------------

void __fastcall TMyThread::Done()
{
  RenderThread->~TMyThread();    //volej destruktor vlákna
}

//---------------------------------------------------------------------------

__fastcall TMyThread:: ~TMyThread()
{
   RenderThread->Terminated;    //ukonči vlákno
}

//---------------------------------------------------------------------------
Vlákno se vytváří při vytváření formuláře Form1
__fastcall TForm1::TForm1(TComponent* Owner)
     : TForm(Owner)
{
   TMyThread *RenderThread = new TMyThread(true);    //vytvoř renderovací vlákno
   RenderThread->Run();   //rozjeď renderovací vlákno

    _control87(MCW_EM, MCW_EM);    //zamezení dělení nulou
}
Funkce Loop() volá v nekonečné smyčce funkci RenderGLScene(), ve které budeme vykrelovat všechny objekty.
void __fastcall TForm1::RenderGLScene()
{
    glClear(GL_COLOR_BUFFER_BIT);    //vyčisti obrazovkový buffer
    glLoadIdentity();    //resetuj současnou modelovou matici
    DrawTriangles();    //vykresli trojúhelník
}
Ve funkci RenderGLScene() budeme volat funkce pro vytváření OpenGL objektů. Nejzákladnější objekt je trojúhelník, ze kterého lze vytvořit jakýkoli složitější objekt. Všechny grafické karty podporující OpenGL mají instrukce, které urychlují vykreslování trojúhelníků. Pokud tedy budeme chtít, aby naše aplikace běžela opravdu plynule, je nejlepší všechny objekty tvořit z trojúhelníků glBegin(GL_TRIANGLES);, nebo ještě rychleji se překreslují trojúhelníky glBegin(GL_TRIANGLE_STRIP);, nebo glBegin(GL_TRIANGLE_FAN);, avšak u těchto dvou funkcí jsou menší omezení v případě texturování, ale to už moc předbíhám.
void DrawTriangles(void)
{
    glTranslatef(-1.5f,0.0f,-6.0f);    //pohni trojúhelníkem o 1.5 doleva o 6.0 od kamery
    glBegin(GL_TRIANGLES);    //začni vykreslovat trojúhelník
      glVertex3f( 0.0f, 1.0f, 0.0f);    //horní bod
      glVertex3f(-1.0f,-1.0f, 0.0f);    //dolní levý bod
      glVertex3f( 1.0f,-1.0f, 0.0f);    //dolní pravý bod
    glEnd();  //ukonči vykreslování trojúhelníka
}
glTranslatef(-1.5f,0.0f,-6.0f); pohybuje celým objektem ve směru x,y,z. Hodnoty funkce jsou: glTranslatef(x,y,z);

glVertex3f( 0.0f, 1.0f, 0.0f); určuje bod objektu. Hodnoty jsou glVertex3f(x,y,z);
void __fastcall TForm1::FormResize(TObject *Sender)
{
    wglMakeCurrent(m_hDC, m_hRC);    //připoj m_hRC (OpenGL Rendering Context) do m_hDC (Windows Device Context), který komunikuje s wokenim GDI (Graphics Device Interface)

    glViewport(0, 0, ClientWidth, ClientHeight);    //nastav velikost zobrazovací plochy
    glMatrixMode(GL_PROJECTION);    //nastav matici zobrazení prostoru
    glLoadIdentity();    //použij tuto matici

    //vypočti velikost zobrazovací plochy. Nastav úhel pohledu kamery, poměr výšky a šířky, nejbližši a nejvzdálenější viditelný bod
    gluPerspective(45.0, static_cast<double>(ClientWidth) / static_cast<double>(ClientHeight),0.1,100.0);

    glMatrixMode(GL_MODELVIEW);    //nastav matici zobrazení objektu
    glLoadIdentity();    //použij tuto matici

    wglMakeCurrent(m_hDC, NULL);    //vynuluj m_hRC (OpenGL Rendering Context)
}
Funkce FormResize(TObject *Sender) nastavi velikost renderovací scény. Při změně velikosti formuláře, se automaticky změní velikost renderovací scény.

Hodnoty funkce glViewport(0, 0, ClientWidth, ClientHeight); jsou:
glViewport(x,y,šířka,výška);
x,y je levý-dolní roh zobrazovací plochy

Hodnoty funkce gluPerspective(45.0, static_cast<double>(ClientWidth) / static_cast<double>(ClientHeight),0.1,100.0); jsou:
gluPerspective(zorný úhel kamery, poměr šířky a výšky projekčního plátna (formuláře), nejbližší bod, nejvzdálenější bod);
void __fastcall TForm1::FormKeyPress(TObject *Sender, char &Key)
{
    if(Key == VK_ESCAPE)    //jestliže klepnu na klávesu Esc
    Close();    //ukonči aplikaci
}
Ve funkci FormKeyPress(TObject *Sender, char &Key) budeme volat funkce, které se provedou po klepnutí na určitou klávesu. Zatím tu máme klávesu Esc, která nám ukončí aplikaci.
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
    if(BorderStyle == bsNone)    //pokud je fullscreen
    ChangeDisplaySettings(NULL,0);    //nastav původní hodnotu rozlišení obrazovky

    ReleaseDC(Handle, m_hDC);    //uvolni handle okna m_hDC (Windows Device Context)
    wglMakeCurrent(m_hDC, NULL);    //vynuluj m_hRC (OpenGL Rendering Context)
    wglDeleteContext(m_hRC);    //vymaž m_hRC (OpenGL Rendering Context)

    RenderThread->Done();    //ukonči renderovací vlákno
}
Při uzavírání aplikace se uvolní handle okna, vymaže OpenGL Rendering Context z paměti a ukončí se renderovací vlákno.

Stejný tutorial pro Delphi najdete zde


Barvy




Home