카테고리 없음
[Link] OpenGL이란 무엇인가
벅스바니
2010. 2. 4. 22:48
Link:http://cs.sungshin.ac.kr/~hkim/LECTURE/CG/OpenGL/open_gl_context.txt
한국과학기술연구원 기전연구부 김 현석 E-Mail : flute@chopin.kist.re.kr
OpenGL이란 무엇인가 한국과학기술연구원 기전연구부 김 현석 E-Mail : flute@chopin.kist.re.krOpenGL이란OpenGL은 본래 Silicon Graphics Incorporated( SGI )에서 그들의 Workstation을 위하여 개발되었다. 이는 SGI가 처음에 개발한 IRIS GL을 윈도우시스템이나 운영체제 또는 하드웨어에 관계없이 고품질의 칼라이미지를 만들어낼 수 있도록 수정한 것이다. 현재는 OpenGL Architectural Review Board( ARB )라는 컨소시움에서 OpenGL라이브러리 정의에 관한 문제를 책임지고 있으며, 구현된 시스템에서의 검증도 시행하고 있다. 이 그룹은 SGI, Microsoft, Intel, IBM 그리고 DEC이 참여하고 있다. 이들의 정의에 관한 문서는 Addison-Wesley에서 출판된 OpenGL Reference Manual에 명시되어 있으며, OpenGL Programming Guide에서는 사용자가 OpenGL을 사용하기 위한 지침서적인 내용을 담고 있다. 그리고 이렇게 정의된 OpenGL은 SGI에서 자사의 Workstation을 위하여 완전하게 구현되어 있고, Microsoft에서는 자사의 운영체제에서 사용될 수 있도록 구현하고 있으며, 그 외의 다른 시스템에서는 Template Graphics Software( TGS )라는 회사에서 구현하고 있다. OpenGL의 구성OpenGL에는 일단 115개의 기본적인 함수를 가지고 있다. 이들은 언제나 'gl'로 시작되는 함수명을 가지고 있으며, OpenGL을 이용하여 고품질의 이미지를 만들고자 하는 경우 사용된다. 또한 43개의 유틸리티 함수를 가지고 있으며 이는 'glu'로 시작되는 함수명을 갖는다. 그리고 마지막으로 31개의 보조 라이브러리를 가지고 있는데 이는 'aux'로 시작된다. 짐작하였듯이 보조 라이브러리는 함수명에 차이를 가지고 있는데, 이 함수의 기본 목적이 OpenGL Programming Guide에 담겨있는 각종 예제 프로그램을 기종에 관계없이 컴파일하여 실행할 수 있도록 하기 위하여 개발된 라이브러리이다. 하지만 OpenGL에는 원하는 종류의 윈도우를 만들고 사용자의 입력을 받아들이는 구체적인 함수는 제공하지 않고 있다. 이들 함수는 각각의 운영체제에 따라서 다르게 구현되고 있으며, 이들에 관한 자료는 해당 운영체제의 OpenGL Porting Guide를 참고하면 된다. 그러면 이들 각각의 라이브러리에 대하여 좀더 구체적으로 알아보도록 하자.OpenGL 기본 함수OpenGL를 이용하여 사용자가 이미지를 만들고자 하는 경우 사용되는 함수이다. 이 함수들은 크게 다음과 같이 구분될 수 있다. 1. View를 설정하는 함수 2. OpenGL의 상태를 바꾸는 함수 3. 실제로 이미지를 만들기 위하여 해당되는 기하학적인 정보를 제공하는 함수 4. 화면의 이미지 중에서 일부를 선택하는 함수 5. 고속으로 이미지를 만들기 위하여 기하학적인 정보를 컴파일하고 실행하는 함수 일반적으로 처음의 세 가지는 간단한 3차원 그래픽스 라이브러리에서는 모두 제공되는 함수이다. 하지만 끝의 두 가지는 고기능의 라이브러리들에서만 제공되는 함수들인데, 이미지를 만들고 이들을 이용하여 특정한 작업을 하기위하여서는 필수적인 함수이다. View의 설정에 관한 문제는 3차원 라이브러리에서 가장 중요시되는 사항이므로 뒤에 좀더 구체적으로 다루도록 하고 나머지 네가지 함수 그룹에 대하여 알아보도록 한다.OpenGL은 철저히 순차적인 라이브러리라고 생각하면 틀린 이야기가 아니다. 그렇게 때문에 사용자가 이미지를 만들기 위하여 그림을 생성하는 함수를 호출하는 과정에서 입력되는 인수는 기하학적인 정보 이외에는 아무것도 없다. 이렇다는 이야기는 특정한 상태변수를 통하여 현재 이미지를 생성하는데 필요한 정보를 제공한다고 짐작할 수 있으리라. OpenGL에서는 200개에 가까운 상태변수를 가지고 있으며 이는 특정한 함수를 통하여 변경되고 설정된 값을 참조할 수 있다. 이들에는 특정 다각형의 Normal을 지정하고, Light와 Material 그리고 Texture를 설정하는 함수 등 아주 많은 종류의 OpenGL대부분 기능을 포함하는 변수들이 포함되어 있다. 그러므로 사용자가 이미지를 생성하는 함수를 호출하기 전에 현재의 상태가 어떠한 상태에 있는가를 충분히 머리 속에 고려를 한 후에 프로그램을 하여야 원하는 이미지를 생성할 수 있다.이렇게 상태 변수들이 필요한 만큼 설정되고 나면 사용자는 실제 이미지를 만드는 함수를 호출하게 되는데 이들 함수들은 'glVertex' 함수에서 모두 해결된다. 즉 그림을 그리기 위해서 필요한 정보는 특정한 도형의 꼭지점 위치만을 넘겨줌으로써 층분하다고 할 수 있다. OpenGL에서는 꼭지점의 위치를 주는 함수만을 제공하고 이 꼭지점이 어떠한 도형을 그리기 위하여 사용되는가는 'glBegin'에서 설정하게 된다. 그러므로 다음과 같은 순서에 의하여 도형을 그리게 된다. glBegin( 도형 종류 ); glVertex( 꼭지점 정보 ); ... glEnd();위와 같은 형식에서 glBegin과 glEnd사이의 꼭지점 정보는 glBegin에서 지정한 도형을 그리기 위한 정보로 사용된다. 참고로 OpenGL에서 만들 수 있는 도형의 종류는 다음과 같다. {{ 그림 . OpenGL에서 사용할 수 있는 도형의 종류}}이 외에도 곡선과 곡면을 그리는 함수가 제공되고 있다.이제 화면에 그림을 그렸으니 이를 이용하여 특정한 입력을 받아보자. 이러한 기능을 일반적으로 Picking이라고 한다. 이는 현재 화면에 그려진 함수를 사용자가 선택을 하는 기능을 일컫는다. 이를 위하여 일단 마우스의 위치를 입력받아야 하는데, 이는 윈도우 함수에서 제공되므로 이를 이용하기로 하자. 마우스의 위치를 입력받으며, 이 값을 이용하여 OpenGL에서는 이 점 근처에 사용자가 지정한 크기의 사각형을 만들게 되고, 화면을 메모리 상에 다시 그려서 이 사각형 내에 그려진 도형을 찾게 된다. 이러한 과정을 거치면 해당되는 도형을 돌려받을 수 있게 된다. 이 기능에 관한 구체적인 이야기는 이미지를 만들기만 하는 경우 필요하지 않고, 또한 아주 고급기능이므로 지면 관계상 생략하기로 한다.마지막으로 기하정보를 컴파일하는 기능을 보기로 하자. OpenGL에서는 사용자가 특정한 도형을 그리기 위하여 여러 개의 꼭지점들을 넘겨주게 된다. 하지만 이러한 꼭지점 정보를 만들어 내는데 만약 많은 시간이 소요되고 이들이 여러 개 또는 여러 번 그려져야 하는 경우 이를 위하여 꼭지점 정보를 재생성한다는 것은 매우 비효율적이다. 그래서 OpenGL에서는 이들 꼭지점 정보와 몇 가지 상태 변수 변경에 관련된 정보를 하나로 묶어 실행할 수 있는 기능을 제공하고 있다. 이러한 기능은 3차원 그래픽에서는 'Display List'라고 한다. OpenGL에서는 다음과 같은 과정으로 이를 만들 수 있다. glNewList( list이름, .. ); 여러 개의 특정한 상태 변경 함수; glBegin( .. ); 여러 개의 꼭지점 정보; glEnd(); ... glEndList();이와 같은 과정을 거쳐 만들어진 리스트는 glCallList함수를 이용하여 화면에 나타내어진다. 물론 이러한 기능은 매우 빠르게 동작되도록 설계되어 있으므로 효율적이다. 지금까지 OpenGL의 기본적인 함수들에 관하여 살펴보았다. 하지만 이들 함수가 OpenGL함수의 전부는 아니다. 이 외에도 특정한 Pixel을 다루는 함수, Frame Buffer를 다루는 함수 등 다양하게 제공된다. 이들 115개의 함수는 대부분이 상태 변수와 관계있는 함수들로서 OpenGL을 정복한다는 것은 OpenGL의 상태변수를 정복한다는 것과 그리 틀린 말이 아니므로 OpenGL을 이용하여 프로그램을 하는 경우 우선 상태변수에 관한 공부를 충분히 하는 것이 좋다.OpenGL 유틸리티 함수OpenGL에는 사용자가 특정한 작업을 쉽게 하기 위하여 OpenGL의 기본 함수를 이용하여 매크로 형식으로 새로운 함수를 만들어 놓았는데, 이들이 유틸리티 함수이다. 이들에는 다음과 같은 함수들의 그룹이 포함되어 있다. 1. Texture에 관련된 이미지를 다루는 함수 2. 좌표계를 변환하는 함수 3. 다각형을 나누는 함수 4. 간단한 도형을 그리는 함수 5. NURB를 다루기 위한 함수 위의 함수 그룹을 보면 어떠한 함수인가 모두 짐작할 수 있을 것이다. 하지만 세번째의 경우는 낳익지 않은 함수들이라 생각된다. 이 함수들이 등장한 이유는 Hardware를 고려한 실시간 렌더링에서는 피할 수 없는 문제 때문이다. 이는 모든 다각형은 삼각형으로 나누어져서 그려진다는 것이다. 이러한 이유는 일단 하드웨어를 설계하기가 쉽고 가장 빠르게 만들 수 있는 구조를 가지고 있기 때문이다. 모든 다각형이 삼각형의 조합으로 만들어질 수 있는 것이 아니므로 OpenGL에서 그릴 수 있는 다각형도 볼록한 다각형으로 한정되게 된다. 하지만 볼록하지 않은 다각형, 또는 구멍이 있는 다각형을 그리기 위해서는 어떻게 할 것인가. 이는 전적으로 사용자의 몫이지만 OpenGL에서는 이러한 불편을 없애기 위하여 임의의 모양을 갖는 다각형을 삼각형으로 나누어주는 함수를 제공하고 있다. 물론 완전하지는 않지만 이를 이용하면 표현할 수 있는 모양이 훨씬 다양해진다.OpenGL에서 Graphic Pipeline지금까지 OpenGL을 구성하는 라이브러리에 관하여 살펴보았다. 그러면 특정한 3차원 세계의 도형을 화면에 나타내기 위하여 여러가지 과정을 거치게 되는데 이를 Graphics Pipeline이라고 한다. OpenGL 역시 일반적으로 3차원 그래픽스 라이브러리에서 통상적으로 사용되는 방식과 비슷하다. Graphic Pipeline은 아래와 같은 과정을 거친다. {{ 그림 . OpenGL에서의 Graphics Pipeline}}이와 같은 과정을 거치면 특정한 모델좌표계의 점이 화면좌표계의 점으로 변환이 되게 된다. 이 과정에서 보면 사용자 프로그램에서 입력으로 들어오는 꼭지점은 모델좌표계라는 각각의 형상마다 정의되어 있는 좌표계 상에서 정의가 된다는 것을 알 수 있다. 이 점들은 Modelview행렬에 의하여 Eye좌표계의 값으로 변환되고 다시 Projection변환행렬을 이용하여 화면에 나타내어질 물체의 크기가 조정되고, 다시 Clipping에 관련된 Perspective Division과정을 거쳐 정규화된 좌표계로 변화된 후 최종적으로 Viewport변환을 통하여 화면에 투영되게 된다.일반적으로 Modelview행렬은 모델좌표계 상의 특정 점을 World좌표계상의 점으로 이동시키는 역할을 하게 된다. 통상 다른 라이브러리의 경우 이 행렬의 역할을 담당하는 행렬이 Model행렬과 View행렬, 두 가지로 나누어져 있지만 OpenGL의 경우는 하나로 해결을 하고 있다. 사실 View행렬 자체가 Model변환행렬의 조합으로 만들어지므로 이러한 접근방식이 더 효율적일 수도 있다. 그러면 그래픽 파이프라인을 따라서 각각의 행렬에 대하여 구체적으로 알아보도록 하자.Modelview행렬의 경우는 회전, 이동, 크기변환등의 기능을 제공하게 된다. 물론 뷰를 바꾸고자 하는 경우에도 사용될 수 있으며, 이들 함수는 OpenGL유틸리티 함수를 통하여 제공되고 있다. OpenGL에서 View의 설정은 우선 카메라를 연상하면 된다. 즉, 한장의 사진을 만들기 위해서는 먼저 카메라를 원하는 위치에 놓고, 피사체를 결정한 후 그것에 초점을 맞추듯이 OpenGL에서도 눈의 위치와 눈이 바라보는 위치를 지정하게 된다. 물론 여기에 눈의 위쪽이 향한 방향( upvector )도 입력하여야 한다. 이러한 사항을 지정하기 위하여 gluLookAt이라는 함수가 제공되고 있다. 이 함수는 두번의 회전모델행렬을 이용하여 만들 수 있다.이제 3차원 그래픽에서 가장 중요시 되고 있는 Projection변환행렬에 대하여 살펴보면, 이 행렬은 특정 도형을 화면에 나타낼 때 원근감을 어떻게 표현할 것인가를 나타내고 있다. 일반적으로 Orthographic프로젝션과 Perspective프로젝션의 두 가지 방법이 있는데, 전자의 경우는 거리에 관계없이 화면 상에는 항상 같은 길이로 표현이 되는 것을 말하고, 후자의 경우는 거리가 먼 것은 작게 나타내어지는 방법을 말한다. 이에 관하여서는 다음 그림을 참조하면 쉽게 이해가 될 것이다. {{ 그림 . OpenGL에서의 Projection방법}}glFrustum을 이용한 변환의 경우 화면에 나타내어지는 부피 자체가 피라미드형식으로 만들어지게 되고, 이 피라미드가 화면에 투영되는 것이므로 거리가 먼 넓은 영역을 차지하고 있는 피라미드의 오른쪽의 경우, 동일한 크기의 선분을 그렸다 하더라도 피라미드의 윗면에 있는 선분보다 화면 상에는 작게 표현이 되는 점을 이용한 것이다. 이러한 방식을 이용하면 쉽게 원근감이 있는 그림을 그릴 수 있다. 마지막으로 Viewport변환의 경우를 살펴보자. Viewport라 함은 화면 상에서 그림이 실제로 그려질 영역을 나타낸다. 즉, 윈도우의 전체에 그림을 나타낼 것인가 아니면 일부분에 나타낼 것인가를 지정하는 것으로 glViewport함수를 이용하여 지정한다. 다음은 위의 기능들을 이용하여 화면에 직육면체를 표현하는 프로그램이다.#include <GL/gl.h>#include <GL/glu.h>#include "aux.h"void display (void){ glClear(GL_COLOR_BUFFER_BIT); 화면을 지운다. glColor3f (1.0, 1.0, 1.0); 현재 펜 색상을 흰색으로 glLoadIdentity (); 현재 행렬스택에 단위행렬을 로드한다. glTranslatef (0.0, 0.0, -5.0); 물체를 -5만큼 z축으로 이동 glScalef (1.0, 2.0, 1.0); 물체의 y축 크기를 2배로 auxWireCube(1.0); 와이어프레임 정육면체를 그린다. glFlush(); 현재까지의 내용을 실제로 그린다.}void myinit (void) { glShadeModel (GL_FLAT); 렌더링 모드를 플랫쉐이딩으로}void myReshape(int w, int h){ glMatrixMode (GL_PROJECTION); 수정할 대상행렬을 Projection행렬로 한다. glLoadIdentity (); 단위행렬을 로드한다. glFrustum (-1.0, 1.0, -1.0, 1.0, 1.5, 20.0); Perspective프로젝션을 지정 glMatrixMode (GL_MODELVIEW); 다시 대상행렬을 ModelView행렬로 glViewport (0, 0, w, h); Viewport를 지정}int main(int argc, char** argv){ auxInitDisplayMode (AUX_SINGLE | AUX_RGB); 이미지를 만들기 위한 윈도우 모드 설정 auxInitPosition (0, 0, 500, 500); 윈도우의 위치를 지정 auxInitWindow (argv[0]); 윈도우를 초기화 한다. myinit (); auxReshapeFunc (myReshape); 윈도우의 크기가 바뀌거나, 윈도우의 위치, 깊이가 바뀌는 경우에 실행될 함수 지정 auxMainLoop(display); 실제로 화면에 그림을 그리는 함수 지정}위 프로그램의 결과는 다음과 같다. {{ 그림 . Cube.c의 출력}}기타 OpenGL함수지금까지 OpenGL에서 이미지를 생성하는 과정에서 중요한 함수를 살펴보았다. OpenGL에는 이러한 함수 이 외에도 특수한 기능을 위하여 필요한 여러가지 함수를 가지고 있다. 특히 가장 많이 사용되고 중요한 함수로는 doublebuffer모드( 이 모드는 Animation등에서 주로 사용되며, 화면에 껌벅임을 없앨 수 있다. )에서 Backbuffer와 Frontbuffer사이에 전환을 시켜주는 함수가 있다. 이 함수는 기본 OpenGL에는 포함되어 있지 않으며, NT의 경우 Swapbuffers()가 제공되고 X-window의 경우는 glXSwapBuffers()가 제공되고 있다. 그리고 각종 Light에 관련된 사항을 정의할 수 있는 'glLight'라는 함수가 제공되며, 이와 같이 'glMaterial'함수를 이용하여 물체의 빛에 관한 성질을 정의할 수 있다. 이러한 기능 이외에도 Fog, DepthCue와 같은 특수 효과를 담당하는 함수와 Stencil Buffer, Accumulation Buffer를 다루는 함수도 제공되고 있다. 한 마디로 RayTracing에서 제공되는 반사광이나 그림자 등과 같은 기능을 하기 위한 함수를 제외하고는 거의 모든 함수가 제공된다고 생각하면 된다.Windows NT에서의 OpenGL지금까지는 운영체제와 관계없이 OpenGL을 사용할 수 있는 부분에 관하여 다루었는데, 과연 특정 운영체제에서 사용하기 위해서는 어떠한 과정들이 덧붙여지는 가에 관하여 윈도우 NT를 예로 살펴보자. 물론 OpenGL을 사용하기 위한 환경으로는 Unix를 운영체제로 하는 시스템을 선정하는 것이 좋겠지만 이에 관한 자료는 여기 저기에 아주 많이 있으므로 지면 관계상 생략하기로 한다. 지금까지 고품질의 컴퓨터 그래픽은 Unix를 탑재한 Workstation의 전유물이었다는 것이 사실이다. 하지만 근래에 Windows NT 3.5에서 OpenGL을 지원하게 되었고, 이로 인하여 많은 하드웨어 제작 회사들에 의하여 전용 Acceleration보드가 출시되고 있다. 이들 보드들은 기존의 워크스테이션에 비하여 약간 처지는 능력을 가지고 있지만, 나름대로 이를 극복하기 위한 다른 방법들( 예를 들어 병렬로 보드를 설치함으로써 보드의 수에 비례하여 속도를 증가시키는 등 )을 제공함으로써 많은 프로그래머들에게 해결책을 제시하여주고 있다. 그러면 NT에서 OpenGL이 어떻게 구현되었고 어떻게 이를 이용하여 프로그램을 할 수 있는가에 관하여 간략히 알아보자.아래 그림은 NT에서의 OpenGL 구조에 대하여 간략히 표현하고 있다. 그림에서와 같이 OpenGL은 기존의 GDI함수들과 엄격히 구분되고 있다. 이러한 이유에서 OpenGL함수와 GDI함수를 같이 사용하는데는 특별한 프로그램 기술이 필요하며 불가능한 기능들도 많이 존재하고 있다. 참고로 doublebuffer모드에서 backbuffer에는 전혀 GDI함수를 사용할 수 없는 것은 매우 불편한 기능 중에 하나이다( 물론 기존의 Workstation보다는 훨씬 편리하지만 ). 그러면 NT에서 OpenGL이 사용할 수 있는 윈도우를 어떻게 만들 것인가. {{ 그림 . Windows NT에서의 OpenGL구현}}NT에서 OpenGL윈도우를 만드는 작업은 매우 간단하다. 먼저 윈도우를 만드는 과정에서 보면 윈도우의 특성에서 'WS_CLIPCHILDREN | WS_CLIPSIBLING'가 반드시 포함되어야 한다. 일단 윈도우가 위와 같이 만들어지고 나면 윈도우의 Pixel형식을 OpenGL에서 사용할 수 있도록 맞추어야 한다. 먼저 사용자는 PIXELFORMATDESCRIPTOR라는 C structure에 원하는 값들을 채워 넣어야 한다. 기본적인 OpenGL Pixel형식은 24가지가 제공되고 있다. 이들만을 이용하는 경우 고품질의 프로그램을 생성하기에는 어려운 문제점들을 많이 가지고 있다. 하지만 전용보드를 사용하는 경우에는 이들 보드에서 지원하는 여러가지 형식이 추가적으로 더 제공되므로 고품질의 이미지를 생성할 수 있다. 구체적인 내용은 지면 관계상 생략하기로 한다.이렇게 Pixel형식이 지정되고 나면 이것으로 윈도우에 해당 형식을 등록하는 절차가 남게 된다. pixelFmtId = ChoosePixelFormat( hdc, &pfd ); SetPixelFormat( hdc, PixelFmtId, &pfd ); hrc = wglCreateContext( hdc );위 과정은 윈도우에 해당 정보를 등록하는 절차를 보여주고 있다. 자세한 자료는 메뉴얼을 참고하면 된다. 일단 이렇게 되면 OpenGL함수를 사용할 수 있는 만반의 준비가 되었다고 볼 수 있다. 마지막 남은 과정은 이제 OpenGL명령어들이 해당 윈도우로 흘러들어 가도록 지정하는 일이다. 이는 'wglMakeCurrent( hdc, hrc )'를 이용하면 된다. 이 과정까지 끝나고 나면 이제 OpenGL의 각종 함수의 결과가 윈도우에 표시되게 된다. 주의 해야 할 점은 이러한 윈도우에 관한 Pixel형식은 처음 한 번만 지정이 되므로 다시한번 지정하는 일이 없도록 주의하여야 한다. 마지막으로 이를 이용한 프로그램과 그 결과를 보이도록 하자. 이 함수는 opengl32.lib, glu32.lib, glaux.lib를 추가하여 링크하여야 한다는 것을 잊으면 안된다./* * Teapot uses double buffering to make a rotation in space. */#include <windows.h>#include <GL/gl.h>#include <GL/glu.h>#include <GL/glaux.h>LONG WINAPI WndProc (HWND, UINT, WPARAM, LPARAM);void SetDCPixelFormat (HDC);void InitializeRC (void);void DrawScene (HDC, UINT);HPALETTE hPalette = NULL;int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { static char szAppName[] = "Teapot"; WNDCLASS wc; HWND hwnd; MSG msg; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC) 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) (COLOR_WINDOW + 1); wc.lpszMenuName = NULL; wc.lpszClassName = szAppName; RegisterClass (&wc); hwnd = CreateWindow (szAppName, szAppName, WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|WS_CLIPSIBLINGS, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP, NULL, hInstance, NULL); ShowWindow (hwnd, nCmdShow); UpdateWindow (hwnd); while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg); DispatchMessage (&msg); } return msg.wParam;}LONG WINAPI WndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { static HDC hdc; static HGLRC hrc; PAINTSTRUCT ps; GLdouble gldAspect; GLsizei glnWidth, glnHeight; static UINT nAngle = 0; static UINT nTimer; int n; switch (msg) { case WM_CREATE: /* OpenGL에서 사용하도록 픽셀형식을 맞춘다. */ hdc = GetDC (hwnd); SetDCPixelFormat (hdc); hrc = wglCreateContext (hdc); wglMakeCurrent (hdc, hrc); InitializeRC (); nTimer = SetTimer (hwnd, 1, 50, NULL); return 0; case WM_SIZE: glnWidth = (GLsizei) LOWORD (lParam); glnHeight = (GLsizei) HIWORD (lParam); gldAspect = (GLdouble) glnWidth / (GLdouble) glnHeight; glMatrixMode (GL_PROJECTION); glLoadIdentity (); gluPerspective (30.0, gldAspect, 1.0, 10.0); glViewport (0, 0, glnWidth, glnHeight); return 0; case WM_PAINT: BeginPaint (hwnd, &ps); DrawScene (hdc, nAngle); EndPaint (hwnd, &ps); return 0; case WM_TIMER: nAngle += 2; if (nAngle >= 90) nAngle = 0; InvalidateRect (hwnd, NULL, FALSE); return 0; case WM_QUERYNEWPALETTE: if (hPalette != NULL) { if (n = RealizePalette (hdc)) InvalidateRect (hwnd, NULL, FALSE); return n; } break; case WM_PALETTECHANGED: if ((hPalette != NULL) && ((HWND) wParam != hwnd)) { if (RealizePalette (hdc)) UpdateColors (hdc); return 0; } break; case WM_DESTROY: wglMakeCurrent (NULL, NULL); wglDeleteContext (hrc); ReleaseDC (hwnd, hdc); if (hPalette != NULL) DeleteObject (hPalette); KillTimer (hwnd, nTimer); PostQuitMessage (0); return 0; } return DefWindowProc (hwnd, msg, wParam, lParam);}/* Windows NT에서 OpenGL을 사용하기 위해서는 꼭 필요한 함수 */void SetDCPixelFormat (HDC hdc){ static PIXELFORMATDESCRIPTOR pfd = { sizeof (PIXELFORMATDESCRIPTOR), // Size of this structure 1, // Version number PFD_DRAW_TO_WINDOW | // Flags PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, // RGBA pixel values 24, // 24-bit color 0, 0, 0, 0, 0, 0, // Don't care about these 0, 0, // No alpha buffer 0, 0, 0, 0, 0, // No accumulation buffer 32, // 32-bit depth buffer 0, // No stencil buffer 0, // No auxiliary buffers PFD_MAIN_PLANE, // Layer type 0, // Reserved (must be 0) 0, 0, 0 // No layer masks }; int nPixelFormat; nPixelFormat = ChoosePixelFormat (hdc, &pfd); SetPixelFormat (hdc, nPixelFormat, &pfd); DescribePixelFormat (hdc, nPixelFormat, sizeof (PIXELFORMATDESCRIPTOR), &pfd);}/* 이미지를 생성하기 위하여 필요한 각종 설정을 담당 */void InitializeRC (void){ GLfloat glfLightAmbient[] = { 0.0f, 0.0f, 0.0f, 1.0f }; GLfloat glfLightDiffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f }; GLfloat glfLightSpecular[] = { 0.9f, 0.9f, 0.9f, 1.0f }; GLfloat glfLightPos[] = { 0.0f, 0.0f, 1000.0f, 1.0f }; /* Depth test, Culling 등을 가능하게 하며, 꼭지점이 어떤 방향으로 돌 때 앞면인가를 지정 */ glEnable (GL_DEPTH_TEST); glEnable (GL_CULL_FACE); glFrontFace (GL_CW); glDepthFunc(GL_LESS); /* 라이트의 지정 */ glLightfv (GL_LIGHT0, GL_AMBIENT, glfLightAmbient); glLightfv (GL_LIGHT0, GL_DIFFUSE, glfLightDiffuse); glLightfv (GL_LIGHT0, GL_SPECULAR, glfLightSpecular); glLightfv (GL_LIGHT0, GL_POSITION, glfLightPos); glEnable (GL_LIGHTING); glEnable (GL_LIGHT0); /* 보조 라이브러리의 teapot을 그리는 기능은 Normal이 주어지지 않으므로 자동적으로 Normal을 생성하도록 한다. */ glEnable (GL_AUTO_NORMAL); glEnable (GL_NORMALIZE);}/* 실제로 주전자를 그리는 부분으로 주전자의 Material을 지정 하고 그림을 그린다. */void DrawScene (HDC hdc, UINT nAngle){ GLfloat glfMaterialColor[] = { 0.69f, 0.13f, 0.33f, 1.0f }; GLfloat glfAmbColor[] = { 0.17f, 0.03f, 0.08f, 1.0f }; GLfloat glfEmiColor[] = { 0.0f, 0.0f, 0.0f, 1.0f }; GLfloat glfSpeColor[] = { 0.22f, 0.15f, 0.15f, 1.0f }; GLfloat glfShiColor[] = { 0.0f }; /* 화면을 지운다. */ glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* Modelview변환을 위한 값 설정 */ glMatrixMode (GL_MODELVIEW); glLoadIdentity (); glTranslatef (0.0f, 0.0f, -8.0f); glRotatef (30.0f, 1.0f, 0.0f, 0.0f); glRotatef ((GLfloat) nAngle, 0.0f, 1.0f, 0.0f); /* 물체의 Material지정 */ glMaterialfv (GL_FRONT, GL_DIFFUSE, glfMaterialColor); glMaterialfv (GL_FRONT, GL_AMBIENT, glfAmbColor); glMaterialfv (GL_FRONT, GL_EMISSION, glfEmiColor); glMaterialfv (GL_FRONT, GL_SPECULAR, glfSpeColor); glMaterialfv (GL_FRONT, GL_SHININESS, glfShiColor); auxSolidTeapot( 1.5 ); /* Back Buffer의 그림을 화면에 나타낸다. */ SwapBuffers (hdc);} {{ 그림 . Windows NT에서 생성한 이미지}}