본문 바로가기

Programming/OpenGL

Basic OpenGL programming in Windows_2

사실 저번 포스트가 그렇게 길어질지 모르고;; 이번에는 저번 코드를 조금 나눠서 설명 하려고 해요. 물론 하나하나가 세부적인 내용을 알아야 하지만 여기서는 간단하게(진짜 간단해 질지는 의문 :-])짚고만 넘어갈게요(후후..)
   
전체 코드 열기
   
프로그램의 흐름대로 가보겠습니다+ _+ 맞는지는 모르겠지만;;
   
윈도우 프로그램의 첫 진입 점은 WinMain(..) 함수 에요.
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);

 
   
윈도우 클래스를 등록하면서 등록 되지 않았다면 프로그램을 종료하게 합니다.
if(!RegisterClassEx(&windowClass))
	return 0;

 
   
실제로 메모리상에 해당 윈도우를 올려 놓는데, 이 때에 반환값은 window handle 입니다. 이건 메모리에는 올라가 있는데 아직 보여지지는 않는 상태입니다.
// 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

   
이곳이 윈도우에게 처음으로 발생하는 "event" 입니다. 그럼 이벤트 처리를 진행하는 곳으로 가게 됩니다. 메시지는
   
윈도우가 제대로 만들어 졌는지 체크하고 그게 아니면 프로그램을 종료 합니다.
// 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

   
여기서 부터 메시지의 메인 루프가 돌아가게 됩니다. 메시지를 받게 되면, 받아서 처리를 해야겠죠~ 하지만 윈도우(프로그램 하나)는 메시지를 받아서 message queue에 넣어주는 거지 처리는 운영체제가 하는 겁니다. 사용자가 어떤 입력(키보드, 마우스, 조이스틱 등등)을 주었을 때, 그것을 받아서 메시지 큐에 넣어주는 과정~ 중에 종료가 된다고 하면, 우리가 앞서 종료 플래그로 설정한 done을 true로 바꿔주면서 루프를 나오게 됩니다. 그럼 프로그램이 완전히 종료 되겠죠.
// 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
		
		// 잠시 생략
		
		TranslateMessage(&msg);	// translate and dispatch to event queue
		DispatchMessage(&msg);
	}

}

   
앞서 설명한 메시지를 처리하는 부분을 사용자가 설정해 줄 수 있는데 그 함수가 처음 부분에 나왔던 WndProc(..) 함수 입니다.
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)

   
일단적으로 구현한 부분을 하나씩 풀어보자면,
   
일단 변수 설정을 했습니다. static으로 rendering context와 device context를 잡아뒀고, 문자열은 앞선 예제에 있던 내용인데 여기서는 사용하지 않습니다 :-]. width, height는 윈도우 창의 크기인데 이 부분은 매번 화면의 크기가 어떻게 되는지 확인해야 하기 때문에 넣어뒀습니다. 화면이 변함에 따라서 설정을 해줘야 이상하게 보이는 것을 방지 할 수 있겠죠ㅎ
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

   
메시지의 종류에 따라서 아래와 같은 행동을 하게 됩니다.
   
윈도우가 처음 만들어 질 때 한번만 호출되게 되며 초기화와 관련된 부분입니다. 윈도우 핸들로 부터 device context를 얻고, 이를 global device context에 할당해 줍니다. pixel format을 지정한 후 등록해 주고 openGL을 랜더링 도구로 등록 할 수 있게 해줍니다.
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;

 
윈도우가 닫히는 부분인데, 이 부분의 차이는 저도 아직은 잘 모르겠어요.(T^T) 닫힌다, 종료된다, 메모리 해제 된다, 의 조건 == 어째든 윈도우 끝낸다. 얘기이므로 wgl의 메모리를 반환, 해제 하는 일을 진행하고 윈도우를 종료시킨다는 PostQuitMessage(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;

 
   
윈도우의 크기가 바뀌게 되었을 때의 처리를 어떻게 하라라는 내용입니다.(openGL 부분은 아래에서 설명 하겠습니다.)
case WM_SIZE:
	// retrieve width and height;
	height = HIWORD(lParam);
	width = LOWORD(lParam);

 
   
여기 까지가 윈도우에 관련된 내용이었고, WinMain에 있는 부분의 openGL 부터 설명 할게요.
   
openGL이 그림을 그리기 이전에 종이가 있어야 하는데, 그 부분을 buffer라고 합니다. 종이긴 한데 일종의 빈 공간이라고 해야하나? 일단 이부분을 깨끗이 비워 주는게 딱 봐도 알다시피 Clear!(:-]) 그리고 openGL은 행렬에 의한 계산을 해서 object를 그려주는데 그 행렬을 modelview matrix라고 하며, 처음에는 modelview matrix를 단위행렬(Identity Matrix)로 만들어 줘야 처음 계산이 제대로 진행 되겠죠?ㅎ(이 부분을 통해서 화면을 외곡 시킨다든지 하는 효과도 낼 수 있다고 가물가물 배웠어요.
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();		// reset modelview matrix

 
 
다음은 전역 변수로 지정한 angle을 한번 while 문이 반복 될 때마다 0.1씩 증가하며, 360.0이 넘으면 다시 0.0으로 되도록 한 부분 입니다.
angle += 0.1f;			// increase your rotation angle count
if(angle >= 360.0f)		// reset angle counter
	angle = 0.0f;

 
   
object를 z축으로 -5.0 만큼 이동(Translate) 시키고 z축을 기준으로 angle 만큼 회전(Rotate) 시킵니다. 이 부분은 뒤에 3D에 관련된 이론을 할 때 더 풀어볼게요. 내용만 보고 넘어가면 될 거 같아요.
glTranslatef(0.0f, 0.0f, -5.0f);	// move back 5 units
glRotatef(angle, 0.0f, 0.0f, 1.0f);	// rotate along z-axis

 
   
앞으로 그리는 Object는 빨강색으로 그릴 거고, 그리는 방식은 삼각형(Triangles), 그리고 각 점은 아래와 같이 한다~ 라는 내용 입니다. 완전 막 이해되고 그러면 여기 포스팅은 안봐도 될거 같아요(ㅋㅋ). 그리고 원래는 탭구분을 안둬도 되지만, 저는 저렇게 하는 게 버릇이라 일단 begin(..)과 end() 사이를 구분 뒀거든요.
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();

 
   
만약에 각 점마다 색을 달리하고 싶으면 각 점 앞에 glColor3f(..) 함수에 원하는 값을 입력해서 삽입하시면 되요. "각 점의 색상 포인트를 세팅한 색으로 한다"가 개념이고, 면의 색을 정하는 것은 아닙니다. 실제로 돌려보면 무슨 의미 인지 아실 거에요(음? 안돌려보고 날로 배우실 생각 아니셨죠?:-])
glBegin(GL_TRIANGLES);				// draw the triangle
	glColor3f(1.0f, 0.0f, 0.0f);	// set all NEXT vertexts color is red
	glVertex3f(0.0f, 0.0f, 0.0f);	// so, this vertext's color is red 

	glColor3f(0.0f, 1.0f, 0.0f);	// set all NEXT vertexts color is green
	glVertex3f(1.0f, 0.0f, 0.0f);	// so, this vertext's color is green

	glColor3f(0.0f, 0.0f, 1.0f);	// set all NEXT vertexts color is blue
	glVertex3f(1.0f, 1.0f, 0.0f);	// so, this vertext's color is blue
glEnd();

 
 
도화지에 다 그렸다면 그걸 뿌려줘야 겠죠? 근대 만약에 처리가 빨리 됐다면, 구지 기다릴 필요없이 다음 도화지를 바로 받으면 좋잖아요~ 그걸 해주는 부분입니다. 그렸던 도화지는 주고 빈 도화지를 받는 거죠.
SwapBuffers(g_HDC);		// bring back buffer to foreground

 
   
여기 까지가 실제로 object를 그렸던 거고 그전에 눈이 있어야 화면을 볼 수 있잖아요?(너님 눈 말구요 +_+ㅋ) openGL이 가지고 있는 camera인데, 대략적인 설명만 할 테지만, 아까전에 WinProc에서 생략한 부분! 윈도우사이즈가 바뀔 때 처리하는 부분(WM_SIZE)! 여기서만 세팅하면 되는데, 화면의 크기가 바뀌지 않더라도, 언제 바뀔지는 모르니까 그때 그때 마다 확인을 해줘야 하겠죠?
   
Viewport는 보이는 부분의 크기를 세팅 하는 겁니다. 그리고 카메라는 projection matrix를 사용하게 되는데, 그 행렬을 맹기작 하겠다는 함수가 glMatrixMode(행렬이름); 으로 사용하게 됩니다. "어? 그럼 아까 object 맹기작 하는 행렬이 modelview matrix인데 그건 손 안댔는데?"에 대한 답변은 밑에서 다시 바꾸게 됩니다.ㅎ 한 프레임 마다 하면 되는 거니까. 그리고 처음 사용한다고 하면 glLoadIdentity()로 단위행렬로 세팅하게 되죠. 현재 불러온 projection matrix는 gluPerspective(..)에서 세팅한 값에 의해 조정되게 됩니다. 자세한 내용은 openGL reference document를 참고 하시면 되구요.
// 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
참조 : 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_1  (0) 2011.11.30
OpenGL 설치하기(windows)  (2) 2011.11.29
gluUnProject function  (0) 2011.01.27