본문 바로가기

Programming/OpenGL

Basic OpenGL programming in Windows_1

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