OpenGL 자체를 하기 전에 알아야 하는 것은 크게 두 가지가 있네요. 첫째로는 3D를 위한 이론, 둘째로 프로그래밍이 Windows 기반으로 일어나기 때문에 윈도우 프로그래밍.
이론은 너무 너무 너무 커서(나중에 다 하는걸 목표로 할거에요 :-]) 윈도우 프로그래밍을 먼저 해보려고 해요.
완전 이론적 인건 패스 할 거에요. 뭐 워낙 잘 만들 었기도 했을테고(직접 확인 할 수 없지만 저는 윈도우 같은 거 못 만드니까ㅋㅋ), 사용하는데 당장 지장 있는 건 아니니까요. 그냥 운영체제라는 큰 테두리 안에서 Process(프로그램, 어플리케이션,..)가 있고 그 안에서 여러 개의 thread들이 돌아가고, 운영체제는 그 Processes중에서 priority 에 따라서 프로그램을 동작 시키는데 너무 빨리 여러 개의 Processes들을 돌리니까 동시에 동작 하는 거처럼 보이게 하는 게 가장 큰 뼈대의 이야기 입니다:-]
아래 그림은 단일 윈도우 기반의 기본적인 흐름도 입니다.
프로그램은 절차적으로 동작하는 것이기 때문에, 마치 그때 그때 맞춰서 행동하는 것처럼 보이려면 언제나 사용자(혹은 다른 프로그램이지만, 일단은 단일 프로그램을 만드는 것이므로 :-])의 입력을 기다려야 합니다. 그래서 반복문을 통해 사용자에게 메시지를 받게 되고 그 메시지를 받게 되는 함수가 WinProc(..) 입니다. 즉, 반복문의 메인인 것이죠.
그리고 사용하게 될 기본적인 윈도우 프로그램 코드 입니다.
#include <Windows.h> LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT paintStruct; HDC hDC; // device context char string[] = "Hello, world!"; // text to be displayed switch(message) { case WM_CREATE: // window is being created return 0; case WM_CLOSE: // window is closing case WM_QUIT: case WM_DESTROY: PostQuitMessage(0); return 0; case WM_PAINT: // window needs updating hDC = BeginPaint(hwnd, &paintStruct); // set text color to blue SetTextColor(hDC, COLORREF(0x00FF0000)); // display text in middle of window TextOut(hDC, 150, 150, string, sizeof(string)-1); EndPaint(hwnd, &paintStruct); return 0; default: break; } return DefWindowProc(hwnd, message, wParam, lParam); } // The application entry point int WINAPI WinMain(HINSTANCE hInstance // , HINSTANCE hPrevInstace // , LPSTR lpCmdLine // , int nShowCmd) // { WNDCLASSEX windowClass; // window class HWND hwnd; // window handle MSG msg; // message bool done; // flag saying when your app is complete // fill out the window class structure windowClass.cbSize = sizeof(WNDCLASSEX); windowClass.style = CS_HREDRAW | CS_VREDRAW; windowClass.lpfnWndProc = WndProc; windowClass.cbClsExtra = 0; windowClass.cbWndExtra = 0; windowClass.hInstance = hInstance; windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); windowClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); windowClass.lpszMenuName = NULL; windowClass.lpszClassName = "MyClass"; windowClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO); // register the window class if(!RegisterClassEx(&windowClass)) return 0; // class registered, so now create your window hwnd = CreateWindowEx(NULL // extended style , "MyClass" // class name , "A Basic Windows Appication" // app name , WS_OVERLAPPEDWINDOW | // window style WS_VISIBLE | WS_SYSMENU , 100, 100 // x, y coordinate , 400, 400 // width, height , NULL // handle to parent , NULL // handle to menu , hInstance // application instance , NULL); // no extra params // check if window creation failed (hwnd would equal NULL) if(!hwnd) return 0; done = false; // initialize the loop condition variable // main message loop while(!done) { PeekMessage(&msg, hwnd, NULL, NULL, PM_REMOVE); if(msg.message == WM_QUIT) // do you receive a WM_QUIT message? done = true; // if so, time to quit the application else { TranslateMessage(&msg); // translate and dispatch to event queue DispatchMessage(&msg); } } return msg.wParam; }
이제 OpenGL을 이용해서 윈도우에 궁극적으로 원하는 것을 올려 놔야 하는데, OpenGL은 단순히 그래픽API이며 운영체제에 독립적 입니다. 일단 저는 Windows 기반에서 작업을 할 것이고, 그것을 지원해 주는 것이 WGL 이라는 겁니다.(뭐 인터넷에서 본 내용은 "Windows openGL" 이라고 하는데, 제가 보고 있는 책에서는 wiggle 이라는 용어를 사용하는 군요. 줄여서 wgl 이라고 하는데;; 책이 워낙 옛날 거라:-]) UNIX 시스템에서 openGL을 지원해 주는 것은 GLX 이구요.
윈도우에서 그래픽적인 요소를 보여주기 위해서는 DC(Device Context)라는 것을 사용하게 되는데, 윈도우 관련 포스트는 아니기 때문에 설명은 생략 할게요. 위의 코드 중에서 Hello, world! 라는 문자열을 출력하기 위해서 사용한 라인입니다.
//(생략) PAINTSTRUCT paintStruct; HDC hDC; // device context char string[] = "Hello, world!"; // text to be displayed //(생략) case WM_PAINT: // window needs updating hDC = BeginPaint(hwnd, &paintStruct); // set text color to blue SetTextColor(hDC, COLORREF(0x00FF0000)); // display text in middle of window TextOut(hDC, 150, 150, string, sizeof(string)-1); EndPaint(hwnd, &paintStruct); return 0; //(생략)
출력이라 함은 화면에 뿌려야 하는데, 일상 생활에 예를 들어보자면 "쓰던", "그리던" 간에 표현할 공간이 필요하게 되죠. 종이든, 벽이든, 칠판이든... 위의 코드를 보자면 HDC(Handle of Device Context)가 역할을 하는 것입니다. 그럼 애초에 그 종이를 누가 줘야 되잖아요?(구입을 하든 친구꺼 가져오든..;;) 그 역할을 HWND(Window Handle : 거꾸로 표기한 듯ㅋ)을 해주는 거에요. 그리고 실제 그리는게 TextOut(..) 이라는 함수에서 해주는 거구요. 표현을 하는 도구는 windows 에서는 PAINTSTRUCT 이지만, 이제 우리는 openGL을 사용 할거 잖아요. 신경쓰지 마세요 :-]
휴... 설명이 좀 길어지긴 했지만, window가 생성되고 종료 될 때 openGL을 사용 할 수 있는 짧은 코드를 추가 했습니다.
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HGLRC hRC; // rendering context static HDC hDC; // device context switch(message) { case WM_CREATE: // window is being created hDC = GetDC(hwnd); // get device context for window hRC = wglCreateContext(hDC); // create rendering context wglMakeCurrent(hDC, hRC); // make rendering context current return 0; case WM_CLOSE: // window is closing case WM_QUIT: case WM_DESTROY: wglMakeCurrent(hDC, NULL); // deselect rendering context wglDeleteContext(hRC); // delete rendering context PostQuitMessage(0); // send WM_QUIT return 0; case WM_PAINT: // window needs updating return 0; default: break; } return DefWindowProc(hwnd, message, wParam, lParam); }
이제 우리는 Windows에서 정의하는 Pixel Format에 대해서 알아봐야 해요. Pixel은 화면에 표시되는 한 개의 점을 의미하는데 이게 가로로 몇 개, 세로로 몇 개 있냐에 따라서 해상도가 결정되죠. 모니터에서 결정되는 것은 이렇기는 하지만 내부적으로 더욱 많은 정보를 가지고 있어요.
이건 다른 포스트에서 언급하게 되면 링크로 바꾸겠습니다. 적으려고 보니 너무 많네요 :-]
그렇게 해서 간단하게 윈도우에서 돌아가게 되는 프로그램 코드 입니다. 주석으로 달아 놓긴 했습니다만, 각각이 별도의 설명을 요하는 내용들이라서 한 포스트에서 보면 토나오잖아요:-] "아 이런식으로 돌아가는 거구나~" 만 보시면 될 거 같아요.
앞선 포스팅(OpenGL 설치하기(windows))과 다른 점은 glutCreateWindow("HelloOpenGL"); 가 없다는 점이죠. 어차피 windows 기반의 프로그래밍을 하는 것이기 때문에 그러한 함수도 구현해 놓은 거 같습니다. 저도 물론 전에 배웠을 때는 윈도우 프로그래밍을 하지 않았지만 더 다양한 부분을 구현하고 싶을 때힘들었었구요. 아직 내용이 나오지 않았지만 그래서 윈도우가 가진 속성에 openGL로 그리는 부분만 넣는 듯 싶습니다.(추측ㅋ)
#include <Windows.h> // standard Windows app include #include <gl/glut.h> //// Global variables float angle = 0.0f; // current angle of the rotating triangle HDC g_HDC; // global device context // function to set the pixel format for the device context void SetupPixelFormat(HDC hDC) { int nPixelFormat; // pixel format index static PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), // size of the structre 1, // version, always set to 1 PFD_DRAW_TO_WINDOW | // support window PFD_SUPPORT_OPENGL | // support OpenGL PFD_DOUBLEBUFFER, // support double buffering PFD_TYPE_RGBA, // RGBA color mode 32, // go for 32 bit color mode 0, 0, 0, 0, 0, 0, // ignore color bits, not used 0, // no alpha buffer 0, // ignore shift bit 0, // no accumulation buffer 0, 0, 0, 0, // ignore accumulation bits 16, // 16-bit z-buffer size 0, // no stencil buffer 0, // no auxiliary buffer PFD_MAIN_PLANE, // main drawing plane 0, // reserved 0, 0, 0 // layer masks ignored }; // choose best matching pixel format, return index nPixelFormat = ChoosePixelFormat(hDC, &pfd); // set pixel format to device context SetPixelFormat(hDC, nPixelFormat, &pfd); } // The Windows Procedure event handler LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static HGLRC hRC; // rendering context static HDC hDC; // device context char string[] = "Hello, world!"; // text to be displayed int width, height; // window width and height switch(message) { case WM_CREATE: // window is being created hDC = GetDC(hwnd); // get current window's device context g_HDC = hDC; SetupPixelFormat(hDC); // call my pixel format setup function // create rendering context and make it current hRC = wglCreateContext(hDC); wglMakeCurrent(hDC, hRC); return 0; case WM_CLOSE: // window is closing case WM_QUIT: case WM_DESTROY: // deselect rendering context and delete it wglMakeCurrent(hDC, NULL); wglDeleteContext(hRC); // send WM_QUIT to message queue PostQuitMessage(0); return 0; case WM_SIZE: // retrieve width and height; height = HIWORD(lParam); width = LOWORD(lParam); if(height == 0) // don't want a divde by zero { height = 1; } // reset the viewport to new dimensions glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); // set projection matrix glLoadIdentity(); // reset projection matrix // calculate aspect ratio of window gluPerspective(45.0f, (GLfloat)width/(GLfloat)height, 1.0f, 1000.0f); glMatrixMode(GL_MODELVIEW); // set modelveiw matrix glLoadIdentity(); // reset modelview matrix return 0; default: break; } return DefWindowProc(hwnd, message, wParam, lParam); } // the main Windows entry point int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstace, LPSTR lpCmdLine, int nShowCmd) { WNDCLASSEX windowClass; // window class HWND hwnd; // window handle MSG msg; // message bool done; // flag saying when your app is complete // fill out the window class structure windowClass.cbSize = sizeof(WNDCLASSEX); windowClass.style = CS_HREDRAW | CS_VREDRAW; windowClass.lpfnWndProc = WndProc; windowClass.cbClsExtra = 0; windowClass.cbWndExtra = 0; windowClass.hInstance = hInstance; windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); windowClass.hbrBackground = NULL; windowClass.lpszMenuName = NULL; windowClass.lpszClassName = "MyClass"; windowClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO); // register the window class if(!RegisterClassEx(&windowClass)) return 0; // class registered, so now create your window hwnd = CreateWindowEx(NULL // extended style , "MyClass" // class name , "A Basic Windows Appication" // app name , WS_OVERLAPPEDWINDOW | // window style WS_VISIBLE | WS_SYSMENU , 100, 100 // x, y coordinate , 400, 400 // width, height , NULL // handle to parent , NULL // handle to menu , hInstance // application instance , NULL); // no extra params // check if window creation failed (hwnd would equal NULL) if(!hwnd) return 0; ShowWindow(hwnd, SW_SHOW); // display the window UpdateWindow(hwnd); // update the window done = false; // initialize the loop condition variable // main message loop while(!done) { PeekMessage(&msg, hwnd, NULL, NULL, PM_REMOVE); if(msg.message == WM_QUIT) // do you receive a WM_QUIT message? done = true; // if so, time to quit the application else { // do rendering here // clear screen and depth buffer glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); // reset modelview matrix angle += 0.1f; // increase your rotation angle count if(angle >= 360.0f) // reset angle counter angle = 0.0f; glTranslatef(0.0f, 0.0f, -5.0f); // move back 5 units glRotatef(angle, 0.0f, 0.0f, 1.0f); // rotate along z-axis glColor3f(1.0f, 0.0f, 0.0f); // set color to red glBegin(GL_TRIANGLES); // draw the triangle glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(1.0f, 0.0f, 0.0f); glVertex3f(1.0f, 1.0f, 0.0f); glEnd(); SwapBuffers(g_HDC); // bring back buffer to foreground TranslateMessage(&msg); // translate and dispatch to event queue DispatchMessage(&msg); } } return msg.wParam; }
실행 화면
참조 : OPENGL GAME PROGRAMMING(Foreword by Mark J.Kilgard)
'Programming > OpenGL' 카테고리의 다른 글
OpenGL states (0) | 2011.12.22 |
---|---|
Basic OpenGL programming Full-Screen (0) | 2011.12.12 |
Basic OpenGL programming in Windows_2 (0) | 2011.12.01 |
OpenGL 설치하기(windows) (2) | 2011.11.29 |
gluUnProject function (0) | 2011.01.27 |