Voorwerpen Je hebt nu je OpenGL window klaar, compleet met een fullscreen optie en kunt nu beginnen met je 3D wereld. In dit hoofdstuk laat ik je eerst zien hoe je voorwerpen kunt maken in OpenGL en er kleur aan toe kunt kennen. Wat we hier gaan doen is een aantal 3D voorwerpen tekenen die draaien en verschillende kleuren hebben. ``` #include #include #include HDC g_HDC; bool fullscreen = FALSE; float hoek; /////////////////////////////////////////////////////////////////////////////// //Voorwerp Box /////////////////////////////////////////////////////////////////////////////// void box(float xpos,float ypos,float zpos) { glPushMatrix(); glTranslatef(xpos,ypos,zpos); glBegin(GL_QUADS); // Front Face glVertex3f(-1.0f, -1.0f, 1.0f); glVertex3f( 1.0f, -1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Back Face glVertex3f(-1.0f, -1.0f, -1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // Top Face glVertex3f(-1.0f, 1.0f, -1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // Bottom Face glVertex3f(-1.0f, -1.0f, -1.0f); glVertex3f( 1.0f, -1.0f, -1.0f); glVertex3f( 1.0f, -1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // Right face glVertex3f( 1.0f, -1.0f, -1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Left Face glVertex3f(-1.0f, -1.0f, -1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glEnd(); glPopMatrix(); } ////////////////////////////////////////////////////////////////////////////// //Voorwerp triangle ////////////////////////////////////////////////////////////////////////////// void triangle(float xpos,float ypos,float zpos) { glPushMatrix(); glTranslatef(xpos,ypos,zpos); glBegin(GL_TRIANGLES); glColor3f(1.0f,0.0f,0.0f); glVertex3f(0.0f,1.0f,0.0f); glColor3f(0.0f,1.0f,0.0f); glVertex3f(1.0f,0.0f,0.0f); glColor3f(0.0f,0.0f,1.0f); glVertex3f(-1.0f,0.0f,0.0f); glEnd(); glPopMatrix(); } ///////////////////////////////////////////////////////////////////////////// //triangle 1 ///////////////////////////////////////////////////////////////////////////// void triangle1(float xpos,float ypos,float zpos) { glPushMatrix(); glTranslatef(xpos,ypos,zpos); glScalef(1.0f,2.0f,1.0f); triangle(0.0f,0.0f,0.0f); glPopMatrix(); } //////////////////////////////////////////////////////////////////////////// //triangle 2 //////////////////////////////////////////////////////////////////////////// void triangle2(float xpos,float ypos,float zpos) { glPushMatrix(); glTranslatef(xpos,ypos,zpos); glScalef(1.0f,2.0f,1.0f); triangle(0.0f,0.0f,0.0f); glPopMatrix(); } /////////////////////////////////////////////////////////////////////////// //triangle 3 /////////////////////////////////////////////////////////////////////////// void triangle3(float xpos,float ypos,float zpos) { glPushMatrix(); glTranslatef(xpos,ypos,zpos); glScalef(1.0f,2.0f,1.0f); triangle(0.0f,0.0f,0.0f); glPopMatrix(); } //////////////////////////////////////////////////////////////////////////// //box1 //////////////////////////////////////////////////////////////////////////// void box1(float xpos,float ypos,float zpos) { glPushMatrix(); glColor3f(1.0f,0.0f,0.0f); glTranslatef(xpos,ypos,zpos); glScalef(1.0f,1.0f,1.0f); glRotatef(hoek,1.0,1.0,1.0); box(0.0f,0.0f,0.0f); glPopMatrix(); } /////////////////////////////////////////////////////////////////////////// //box2 /////////////////////////////////////////////////////////////////////////// void box2(float xpos,float ypos,float zpos) { glPushMatrix(); glColor3f(0.0f,1.0f,0.0f); glTranslatef(xpos,ypos,zpos); glScalef(1.0f,1.0f,1.0f); glRotatef(hoek,1.0,1.0,1.0); box(0.0f,0.0f,0.0f); glPopMatrix(); } /////////////////////////////////////////////////////////////////////////// //box3 /////////////////////////////////////////////////////////////////////////// void box3(float xpos,float ypos,float zpos) { glPushMatrix(); glColor3f(0.0f,0.0f,1.0f); glTranslatef(xpos,ypos,zpos); glScalef(2.0f,1.0f,1.0f); box(0.0f,0.0f,0.0f); glPopMatrix(); } ////////////////////////////////////////////////////////////////////////// //Rendering ////////////////////////////////////////////////////////////////////////// void render() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glPushMatrix(); glLoadIdentity(); glTranslatef(0.0f,0.0f,-10.0f); box3(0.0f,-2.0f,0.0f); triangle1(-4.0f,-3.0f,0.0f); triangle2(4.0f,-3.0f,0.0f); triangle3(0.0f,1.0f,0.0f); box1(-3.0f,2.0f,0.0f); box2(3.0f,2.0f,0.0f); glPopMatrix(); hoek +=1.0; glFlush(); SwapBuffers(g_HDC); } //////////////////////////////////////////////////////////////////////////// //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; int width, height; 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_SIZE: height = HIWORD(lParam); width = LOWORD(lParam); if (height==0) { height=1; } glViewport(0,0,width,height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,1.0f, 1000.0f); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); 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; DWORD dwExStyle, dwStyle; bool done=FALSE; RECT windowRect; int width=800,height=600,bits=32; windowRect.top = 0; windowRect.left = 0; windowRect.bottom = height; windowRect.right = width; 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 = (HBRUSH)GetStockObject(BLACK_BRUSH); wc.lpszMenuName = NULL; wc.lpszClassName = "OpenGL"; wc.hIconSm = LoadIcon(NULL,IDI_WINLOGO); RegisterClassEx(&wc); if(fullscreen) { DEVMODE devModeScreen; memset(&devModeScreen, 0,sizeof(devModeScreen)); devModeScreen.dmSize = sizeof(devModeScreen); devModeScreen.dmPelsWidth = width; devModeScreen.dmPelsHeight = height; devModeScreen.dmBitsPerPel = bits; devModeScreen.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL; ChangeDisplaySettings(&devModeScreen, CDS_FULLSCREEN); dwExStyle = WS_EX_APPWINDOW; dwStyle = WS_POPUP; ShowCursor(FALSE); } else { dwExStyle = NULL; dwStyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_SYSMENU; } AdjustWindowRectEx(&windowRect, dwStyle, FALSE, dwExStyle); hwnd=CreateWindowEx(dwExStyle, "OpenGL", "glwindow", dwStyle | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0,0, windowRect.right - windowRect.left, windowRect.bottom - windowRect.top, 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 { render(); TranslateMessage(&msg); DispatchMessage(&msg); } } return (msg.wParam); } ``` Het is veel code maar het is niet zo moeilijk als het lijkt. Het eerste wat je ziet is dat er een box en een triangle functie bij is gekomen waar eerst een box een een driehoek wordt getekend. Ik bespreek alleen de functie van de driehoek aangezien deze bijna gelijk is aan de box functie. ``` void triangle(float xpos,float ypos,float zpos) { glPushMatrix(); glTranslatef(xpos,ypos,zpos); glBegin(GL_TRIANGLES); glColor3f(1.0f,0.0f,0.0f); glVertex3f(0.0f,1.0f,0.0f); glColor3f(0.0f,1.0f,0.0f); glVertex3f(1.0f,0.0f,0.0f); glColor3f(0.0f,0.0f,1.0f); glVertex3f(-1.0f,0.0f,0.0f); glEnd(); glPopMatrix(); } ``` Deze functie ontvangt 3 parameters waarmee je de positie van het voorwerp kunt veranderen. Het eerste wat je binnen de functie ziet is glPush- en glPopMatrix(); om te begrijpen hoe dit werkt moet je eerst weten wat een matrix stack is. In OpenGL worden matrices voor transformaties gebruikt en veranderen de matrices dus elke keer als je een voorwerp verplaatst of verandert. Om er dan toch voor te zorgen dat je niet met matrices werkt die verandert zijn gebruik je popmatrix. Als je deze functie oproept wordt er een nieuwe matrix op een matrix stack geplaatst (dit kun je vergelijken met een stapel borden waar er 1 bovenop wordt gezet) waardoor de 1ste matrix niet meer current is en niet meer verandert wordt. Nu kun je dus een voorwerp maken en veranderen zoveel je wilt zonder dat de originele matrix gewijzigd wordt. Als je eenmaal klaar bent met het transformeren kun je de matrix weer weggooien zodat je de originele matrix weer krijgt. Wat het nut hiervan is is dat je de huidige coordinaten behoud waardoor je voorwerpen makkelijk ten opzichte van elkaar kunt verplaatsen kijk maar naar het volgende voorbeeldje. Stel ik wil 2 blokken op het scherm zetten, het ene blok verplaats ik 2 naar links en het andere blok verplaats ik vervolgens 2 naar rechts. Nu is de vraag op welke coordinaten staan de 2 blokken ? Het eerste blok staat op (-2,0) omdat de matrix nog onverandert was maar het 2de blok is vanaf de positie (-2,0) weer 2 naar rechts geplaatst en staat nu dus op (0,0). Als je dit met pop- en pushmatrix had gedaan dan was het wel goed gegaan en waren de blokken ten opzichte van de scene wel goed verplaatst. Nu we een matrix hebben kunnen we dus een driehoek maken door de glBegin(GL_TRIANGLES) functie, dit kan ook voor vierkanten GL_QUADS en polygonen GL_POLYGON. Bij de triangle functie heb ik metteen de kleur aangegeven wat ik bij de box niet heb gedaan waardoor de boxen 1 kleur hebben en de driehoeken voor elk punt een kleur hebben die in elkaar overlopen. De kleur toekennen gebeurt door glColor3f() die 3 parameters ontvangt voor de RGB kleuren (rood, groen, blauw). De maximum waarde is hier altijd 1 voor maximale kleur en de minimum waarde is altijd 0 voor geen kleur. Vervolgens geef je het hoekpunt van de aan driehoek door glVertex3f() die de x,y, en z waarde ontvangt en waaraan de kleur waarde wordt toegekend. Dit doen we dan ook voor de andere 2 punten van de driehoek, sluiten we af met glEnd() en gooien we de matrix weg door glPopMatrix(). Let wel op dat je de punten van de driehoek tegen de klok in maakt hierdoor wordt het namelijk mogelijk dat OpenGL de voor en achterkant van het voorwerp kan bepalen. Het voordeel hiervan is dat de achterkant die je toch niet ziet ook niet gerenderd hoeft te worden wat natuurlijk weer snelheid oplevert. Hier moet je zelf maar een beetje mee gaan experimenteren en je zult merken dat het heel simpel is. Nu hebben we dus een box en een driehoek die we kunnen gebruiken om complexe voorwerpen te maken. Als voorbeeld heb ik dat hier niet gedaan maar ik denk dat je het wel snapt. Ik heb hier nu 3 boxen gemaakt met de box functie en 3 driehoeken gemaakt met de triangle functie je ziet dat het veel code bespaard om van 1 voorwerp telkens een copie te maken en daarna appart te bewerken. Ook de box1, box2, en box3 functies ontvangen weer coordinaten om je box te verplaatsen en is er weer een glPush en glPopmatrix functie. Het eerste wat ik hier heb gedaan is de kleur van de hele box bepaald, dus niet als bij de triangle waar elk punt een bepaalde kleur heeft. De translatef() functie moet je nu wel begrijpen en dan kun je met glScalef() de grootte van de box bepalen voor de x,y, en z-as (try it). De volgende functie zorgt ervoor dat de box gaat draaien met de variabele hoek die de draaihoek aangeeft en telkens verhoogt wordt in de render functie, en de as waar het voorwerp omheen draait (hier om alle 3 de assen). Als laatste moeten we dan nog even de box maken en we zijn klaar. ``` //////////////////////////////////////////////////////////////////////////////// //box1 //////////////////////////////////////////////////////////////////////////////// void box1(float xpos,float ypos,float zpos) { glPushMatrix(); glColor3f(1.0f,0.0f,0.0f); glTranslatef(xpos,ypos,zpos); glScalef(1.0f,1.0f,1.0f); glRotatef(hoek,1.0,1.0,1.0); box(0.0f,0.0f,0.0f); glPopMatrix(); } ``` We hebben nu onze boxen en triangles maar nu moeten deze nog op het scherm komen, om hier voor te zorgen heb ik een render functie gemaakt die wordt opgeroepen in winMain. ``` /////////////////////////////////////////////////////////////////////////////// //Rendering /////////////////////////////////////////////////////////////////////////////// void render() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glPushMatrix(); glLoadIdentity(); glTranslatef(0.0f,0.0f,-10.0f); box3(0.0f,-2.0f,0.0f); triangle1(-4.0f,-3.0f,0.0f); triangle2(4.0f,-3.0f,0.0f); triangle3(0.0f,1.0f,0.0f); box1(-3.0f,2.0f,0.0f); box2(3.0f,2.0f,0.0f); glPopMatrix(); hoek +=1.0; glFlush(); SwapBuffers(g_HDC); } ``` Hier wordt eerst de collor en diepte buffer leeg gemaakt zodat het scherm zwart wordt gemaakt, waarna de identity matrix wordt geladen. Dan gaan we weer een matrix maken en resetten naar de identity matrix en gaan we 10 naar achteren. En nu kunnen we dan onze boxen en triangles laten zien en de matrix weer verwijderen. Als alles op het scherm staat maken we de variabele hoek groter waardoor de boxen gaan draaien (glRotatef zorgt hiervoor). Dan flushen we de buffer, swappen de buffers, en we zijn klaar met ons eerste programma. Succes Vampire,