Inleiding

Welkom bij mijn eerste OpenGL handleiding ik hoop dat je hier wat aan hebt en dus vaak terug zult komen voor meer. Ik zelf hou niet zo erg van theorie dus zul je dat hier ook niet zo veel vinden, in de praktijk leer je uiteraard het meest. Je zult in deze handleidingen allemaal (info) links tegenkomen waar je meer informatie over een bepaald onderwerp zult vinden. Op deze manier hoop ik het voor jou wat makkelijker te maken om snel met OpenGL te kunnen werken. In deze eerste handleiding laat ik je meteen zien hoe je OpenGL window maakt zodat je er metteen goed inkomt. Maar voordat we hiermee bezig gaan wil ik je eerst nog een klein beetje over OpenGL vertellen.

Zoals je waarschijnlijk al wel weet is OpenGL een graphics API (application programming interface) en houdt zich dus ook alleen met de graphics bezig. Om er voor te zorgen dat een gebruiker interactief met de OpenGL omgeving kan omgaan is er daarom een WGL (wiggle) extensie ontwikkeld. Deze wgl functies zorgen ervoor dat OpenGL met Windows kan communiceren en zo interactie mogelijk maakt. De wgl functies zijn herkenbaar aan de prefix wgl.

Het window wat we voor OpenGL gaan gebruiken is hetzelfde als het window dat ik heb besproken in de eerste windows handleiding met een aantal extra's. Ik zal de hele sourcecode hier neerzetten maar bespreek alleen de nieuwe code dus als je niet meer precies weet hoe een window werkt kun je altijd de windows code nakijken.


#include <windows.h>
#include <gl/gl.h>
#include <gl/glu.h>

HDC g_HDC;		 //globale device context

///////////////////////////////////////////////////////////////////////////
//Pixelformatdescriptor
///////////////////////////////////////////////////////////////////////////
void setuppfd(HDC hDC)
{
  int nPixelFormat;

  static PIXELFORMATDESCRIPTOR pfd={
		sizeof(PIXELFORMATDESCRIPTOR),
		1,
		PFD_DRAW_TO_WINDOW|
		PFD_SUPPORT_OPENGL|
		PFD_DOUBLEBUFFER,
		PFD_TYPE_RGBA,
		32,
		0,0,0,0,0,0,
		0,
		0,
		0,
		0,0,0,0,
		16,
		0,
		0,
		PFD_MAIN_PLANE,
		0,
		0,0,0};

		nPixelFormat = ChoosePixelFormat(hDC, &pfd);
		SetPixelFormat(hDC,nPixelFormat, &pfd);
}

/////////////////////////////////////////////////////////////////////////////
//Windows Procedure
/////////////////////////////////////////////////////////////////////////////

LRESULT CALLBACK WndProc(HWND	hwnd,
			 UINT	message,
			 WPARAM	wParam,	
			 LPARAM	lParam)	
{
  static HGLRC hRC;
  static HDC hDC;

  switch (message)
  {
     case WM_CREATE:
	hDC = GetDC(hwnd);
	g_HDC = hDC;
	setuppfd(hDC);
	hRC = wglCreateContext(hDC);
	wglMakeCurrent(hDC,hRC);
	break;

     case WM_CLOSE:
	wglMakeCurrent(hDC,NULL);
	wglDeleteContext(hRC);
	PostQuitMessage(0)		
	break;
			
     case WM_KEYDOWN:
	switch (wParam)
	{
	  case VK_ESCAPE:
	  wglMakeCurrent(hDC,NULL);
	  wglDeleteContext(hRC);
	  PostQuitMessage(0);
	  break;
	}

  }
  return DefWindowProc(hwnd,message,wParam,lParam);
}

////////////////////////////////////////////////////////////////////////////
//Main Functie
////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE	hInstance,			
		   HINSTANCE	hPrevInstance,		
		   LPSTR	lpCmdLine,			
		   int		nShowCmd)			
{
  WNDCLASSEX	wc;	
  HWND 		hwnd;
  MSG		msg;  
  bool		done=FALSE;
					
  wc.cbSize		=sizeof(WNDCLASSEX);	
  wc.style		= CS_HREDRAW | CS_VREDRAW;
  wc.lpfnWndProc	= WndProc;	
  wc.cbClsExtra		= 0;	
  wc.cbWndExtra		= 0;
  wc.hInstance		= hInstance;
  wc.hIcon		= LoadIcon(NULL, IDI_APPLICATION);
  wc.hCursor		= LoadCursor(NULL, IDC_ARROW);	
  wc.hbrBackground	= NULL;
  wc.lpszMenuName	= NULL;	
  wc.lpszClassName	= "OpenGL";
  wc.hIconSm		= LoadIcon(NULL,IDI_WINLOGO);

  RegisterClassEx(&wc);


  hwnd=CreateWindowEx(NULL,							
		      "OpenGL",							
		      "glwindow",								
		      WS_OVERLAPPEDWINDOW |WS_VISIBLE | 
		      WS_SYSMENU | WS_CLIPCHILDREN | 
		      WS_CLIPSIBLINGS, 			
		      100,100,
		      400,400,
		      NULL,							
		      NULL,							
		      hInstance,							
		      NULL);
	
  ShowWindow(hwnd, SW_SHOW);
  UpdateWindow(hwnd);
	
  while(!done)
  {
    PeekMessage(&msg,NULL,0,0,PM_REMOVE);
			
    if (msg.message==WM_QUIT)
    {
	done=TRUE;	
    }
    else		
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();

	glTranslatef(0.0f,0.0f,0.0f);
	glColor3f(1.0f,0.0f,0.0f);
	glBegin(GL_TRIANGLES);
	 glVertex3f(0.0f,0.5f,0.0f);
	 glVertex3f(-0.5f,-0.5f,0.0f);
	 glVertex3f(0.5f,-0.5f,0.0f);
	glEnd();

	SwapBuffers(g_HDC);

	TranslateMessage(&msg);	
	DispatchMessage(&msg);
    }
  }
  return (msg.wParam);	
}
Dit is dus het OpenGL window dat je kunt gebruiken als basis voor de rest van de handleidingen. Zorg er wel voor dat je de goede libraries linkt (opengl32.lib en glu32.lib) anders krijg je errors.

De pixelformatdescriptor


void setuppfd(HDC hDC)
{
  int nPixelFormat;

  static PIXELFORMATDESCRIPTOR pfd={
	sizeof(PIXELFORMATDESCRIPTOR),  // grootte van pixelformatdescriptor
	1,				// versie nummer (alijd 1)
	PFD_DRAW_TO_WINDOW|		// buffer kan in een window tekenen
	PFD_SUPPORT_OPENGL|		// buffer ondersteund OpenGL
	PFD_DOUBLEBUFFER,		// dubble buffering 
	PFD_TYPE_RGBA,			// kleur mode (rood, groen, blauw, alpha)
	32,				// kleur diepte (32, 24, 16, 8 bit)
	0,0,0,0,0,0,			// negeer kleur bits
	0,				// geen alpha buffer
	0,				// negeer shift bit
	0,				// geen accumulation buffer
	0,0,0,0,			// negeer accumulation bits
	16,				// grootte van de z-buffer (16 bits)
	0,				// geen stencil buffer
	0,				// geen auxiliary buffer
	PFD_MAIN_PLANE,			// teken in het hoofd vlak
	0,				// gereserveerd
	0,0,0};                         // negeer layer masks
		
	// kies en passend pixelformaat op basis van de pixelformatdescriptor
	nPixelFormat = ChoosePixelFormat(hDC, &pfd);

	//stel dit pixelformaat in
	SetPixelFormat(hDC,nPixelFormat, &pfd);
}
De pixelformatdescriptor is een extensie van de Win32 API om het gebruik van OpenGL in windows mogelijk te maken. In de pixelformatdescriptor worden pixel gegevens opgegeven zoals de kleur mode, het bits per pixel, en de diepte buffer. Ik neem aan dat je de betekenis van de parameters wel begrijpt door de bovenstaande structuur te bekijken.

De window procedure

De window procedure zorgt ervoor dat het window gemaakt wordt je er OpenGL in kunt gebruiken, en weer goed afgesloten wordt.

Rendering context

Om OpenGL te gebruiken moet elk OS de functionaliteit aangeven van het window waarin gerendert wordt. In Windows is het de device context die de gegevens hiervoor onthoud. OpenGL heeft echter een rendering context die deze gegevens onthoud. Bovendien zijn er meerdere rendering context's toegestaan, anders dan een device context waar er maar 1 van mag zijn. Let er altijd op dat je eerst een device context moet maken voordat je een rendering context kunt maken.

We gebruiken in de windows procedure static variabelen voor de device en rendering context zodat ze niet elke keer weer opnieuw gemaakt hoeven te worden als de window procedure wordt opgeroepen.

static HGLRC hRC;
static HDC hDC;

Als het window gemaakt wordt zorgen we er eerst voor dat we een device context van ons window krijgen door de GetDC(hwnd); functie. Vervolgens kennen we deze hDC toe aan onze globale g_hDC voor als we de buffer moeten swappen, we hebben immers double buffering. Hierna komt de setuppfd() functie die we gemaakt hebben die de pixel instellingen in de device context instelt. Als de device context is ingesteld kun je door hDC door te geven aan de wglCreateContext() functie een hRC maken. En als laatste moet je dan de rendering context current maken zodat dit de rendering context is waar OpenGL in zal renderen.


case WM_CREATE:
	hDC = GetDC(hwnd);
	g_HDC = hDC;
	setuppfd(hDC);
	hRC = wglCreateContext(hDC);
	wglMakeCurrent(hDC,hRC);
	break;
Als je een rendering context hebt gemaakt moet je deze ook weer verwijderen, het makkelijkst is het om dit te doen als het window gesloten wordt. Eerst moeten we de rendering context losmaken van de device context door aan wglMakeCurrent() null aan te geven in plaats van de hRC. Als de RC eenmaal los is gemaakt kunnen we deze verwijderen uit het geheugen en een quitmessage posten.

case WM_CLOSE:
	wglMakeCurrent(hDC,NULL);
	wglDeleteContext(hRC);
	PostQuitMessage(0)		
	break;
Als laatste zorgen we er voor dat we met escape het programma kunnen sluiten voor als we in fullscreen mode zitten. De code is hetzelfde als voor de WM_CLOSE message.

case WM_KEYDOWN:
	switch (wParam)
	{
	  case VK_ESCAPE:
	  wglMakeCurrent(hDC,NULL);
	  wglDeleteContext(hRC);
	  PostQuitMessage(0);
	  break;
	}

WinMain

WinMain behoeft ook niet zo veel uitleg aangezien hier alles gelijk is aan een gewoon window. Alleen de in de windowclass is iets verandert, wc.hbrBackground kan "NULL" worden gemaakt aangzien OpenGL de achtergrond regelt. Verder is er alleen in de game loop wat code toegevoegd zodat er een rode driehoek in het window verschijnt.

else		
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity();

	glTranslatef(0.0f,0.0f,0.0f);
	glColor3f(1.0f,0.0f,0.0f);
	glBegin(GL_TRIANGLES);
	 glVertex3f(0.0f,0.5f,0.0f);
	 glVertex3f(-0.5f,-0.5f,0.0f);
	 glVertex3f(0.5f,-0.5f,0.0f);
	glEnd();

	SwapBuffers(g_HDC);

	TranslateMessage(&msg);	
	DispatchMessage(&msg);
    }
Hier wordt eerst de kleur en diepte buffer leeg gemaakt door de glClear() functie waardoor je een zwarte achtergrond krijgt. Vervolgens reset de glLoadIdentity de modelview matrix, en ben je klaar om te tekenen in je window. Met glTranslatef() functie kun je de camera positie bepalen (x,y,z). Vervolgens geef je de kleur aan van het voorwerp met glColor3f(R,G,B) waar 1 het maximum is en 0 het minimum. Als laatste kun je dan aangeven met glBegin wat je wilt tekenen (GL_TRIANGLES, GL_QUADS, GL_POLYGON). Vervolgens kun je dan met glVertex3f de punten aangeven van het voorwerp en kun je het tekenen afsluiten met glEnd(). Als je een animatie maakt moet je natuurlijk niet vergeten je backbuffer met de voorste buffer te swappen anders heb je er nog niet veel aan.

Succes

Vampire,