CAFE

프로그래밍 팁

[MFC] Visual C++에서 OpenGL 사용하기

작성자아름다운 연꽃|작성시간06.03.05|조회수688 목록 댓글 0
Visual C++에서 OpenGL 사용하기

■ 개 요

window NT계열에서는 OpenGL은 윈도우 운영시스템의 한 부분이 되었습니다. 현재 window95,98에서 OpenGL을 사용하기 위해서는 따로, 이에 해당되는 라이브러리를 설치해야 합니다.


■ 시 작

우선 알아야할점은, OpenGL을 윈도우에 세팅하는 최소한의 지식입니다. GDI가 이미지를 다루기 위해서 Device Context(DC)가 필요하듯이 OpenGL은 Rendering Context(RC)가 필요합니다. 하지만, GDI는 각각의 명령들이 DC를 통하는것과는 다르게 OpenGL는 그 개념이 다릅니다. 하나의 스레드 안에 RC가 세팅되면, 그 스레드안의 모든 OpenGL은 현재의 RC를 사용하게 됩니다. 만약 하나의 윈도우에 여러개의 RC가 존재할경우, 하나의 스레드에는 현재 적용되는 RC만이 적용되게 됩니다.

다음은 RC를 사용하기위해서 세 개의 단계로 설명됩니다.

1. 윈도우 픽셀형식을 설정함

2. RC를 생성함

3. RC current를 만듬


Visual C++에서 프로젝트를 생성하는 단계는 다음과 같습니다.

1. MFC AppWizard(exe)의 새로운 프로젝트 워크스페이스를 선택하고, 생성시킬 디렉토리와 프로젝트명을 지정합니다. 그리고, Create를 클릭합니다.
2. 알맞은 Document형식을 선택합니다.(여기서는 Single Document Interface)
3. Database support = None
4. Compond Document Support = None
5. Docking Toolbar = Off

   Initial Status Bar = Off
   Printing an Print Preview = Off
   Context-Sensitive Help = Off
   3D controls = On

6. 나머지는 사용측면에 맞도록 설정하면됩니다.




■ 준 비

우선, OpenGL파일과 라이브러리를 프로젝트에 포함시키는 작업이 필요합니다.
Project-Settings을 선택하고 Link에서 General 카테고리내에 Object/Library모듈 부분에 다음과 같은 내용을 포함합니다.
opengl32.lib glu32.lib glaux.lib
그리고, stdafx.h 파일을 선택한뒤 다음과 같은 부분을 추가합니다.

#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows
// headers


#include // MFC core and standard components
#include // MFC extensions
#include
#include
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include // MFC support for Windows 95 Common Controls
#endif // _AFX_NO_AFXCMN_SUPPORT

OpenGL은 WS_CLIPCHILDREN과 WS_CLIPSIBLINGS 스타일이 필요로 하므로, OnPreCeate를 다음과 같이 수정합니다.

BOOL CSampleView::PreCreateWindow(Createstruct& cs){

        cs.style |= (WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
        return CView::PreCreateWindow(cs);

}

다음으로, RC를 생성하기위해 해야할 첫번째 세팅은 윈도우 픽셀형식을 설정하는것입니다. 픽셀형식은 메모리상에 윈도우가 표현하는 그래픽형태를 어떤식으로 묘사하는가에 대한 설정으로 볼수 있습니다. 파라메터는 색상, 버퍼방식, 그리고, 인터페이스를 포함하게 됩니다.

우선 Project Workspace에서 View class(CSampleView)를 선택한뒤 마우스의 오른쪽 버튼을 눌러 “Add Function"을 선택하고, 아래와 같은 함수를 추가합니다.

BOOL CSampleView::SetWindowPixelFormat(HDC hDC)

{
        PIXELFORMATDESCRIPTOR pixelDesc;
        pixelDesc.nSize = sizeof(PIXELFORMATDESCRIPTOR);
        pixelDesc.nVersion = 1;
        pixelDesc.dwFlags = PFD_DRAW_TO_WINDOW |
        PFD_DRAW_TO_BITMAP |
        PFD_SUPPORT_OPENGL |
        PFD_SUPPORT_GDI |
        PFD_STEREO_DONTCARE;
        pixelDesc.iPixelType = PFD_TYPE_RGBA;
        pixelDesc.cColorBits = 32;
        pixelDesc.cRedBits = 8;
        pixelDesc.cRedShift = 16;
        pixelDesc.cGreenBits = 8;
        pixelDesc.cGreenShift = 8;
        pixelDesc.cBlueBits = 8;
        pixelDesc.cBlueShift = 0;
        pixelDesc.cAlphaBits = 0;
        pixelDesc.cAlphaShift = 0;
        pixelDesc.cAccumBits = 64;
        pixelDesc.cAccumRedBits = 16;
        pixelDesc.cAccumGreenBits = 16;
        pixelDesc.cAccumBlueBits = 16;
        pixelDesc.cAccumAlphaBits = 0;
        pixelDesc.cDepthBits = 32;
        pixelDesc.cStencilBits = 8;
        pixelDesc.cAuxBuffers = 0;
        pixelDesc.iLayerType = PFD_MAIN_PLANE;
        pixelDesc.bReserved = 0;
        pixelDesc.dwLayerMask = 0;
        pixelDesc.dwVisibleMask = 0;
        pixelDesc.dwDamageMask = 0;
        m_GLPixelIndex = ChoosePixelFormat( hDC, &pixelDesc);
        if (m_GLPixelIndex==0) // Let's choose a default index.
        {
                m_GLPixelIndex = 1;
                if (DescribePixelFormat(hDC, m_GLPixelIndex,
                        sizeof(PIXELFORMATDESCRIPTOR), &pixelDesc)==0){
                        return FALSE;
                }
        }
       if (SetPixelFormat( hDC, m_GLPixelIndex, &pixelDesc)==FALSE)
        {
                return FALSE;
        }
        return TRUE;
}

그런뒤 다시 Workspace의 view class에서 오른쪽 버튼을 누른뒤, "Add Variable"을 선택하여 다음과 같은 변수를 생성합니다.

int m_GLPixelIndex; // protected

마지막으로, WM_CREATE의 함수인 OnCreate()함수에 다음과 같은 코드를 삽입합니다.

int CSampleView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
        if (CView::OnCreate(lpCreateStruct) == -1)
                return -1;
        HWND hWnd = GetSafeHwnd();
        HDC hDC = ::GetDC(hWnd);
        if (SetWindowPixelFormat(hDC)==FALSE)
                return 0;
        return 0;
}

위와 같은 작업이 끝나면 OpenGL을 하는 최소한의 작업을 마친것입니다. 일단 한번 컴파일을 해봐서 syntex 에러를 잡고 다음으로 넘어가시기바랍니다.

□ PIXELFORMATDESCRIPTOR 부분을 설명합니다. 이부분을 알경우는 넘어가셔도 됩니다.
- dwFlags는 devices와 인터페이스를 pixel format부분에 조합에대해 설정하는부분입니다.
PFD_DRAW_TO_WINDOW : 윈도우나 장치표면에 묘화가능
PFD_DRAW_TO_BITMAP : 메모리상의 비트맵형식으로 묘화가능
PFD_SUPPORT_GDI : GDI를 사용함(단, PFD_DOUBLEBUFFER를 사용시는 무시됨)
PFD_SUPPORT_OPENGL : OPENGL 사용가능
PFD_GENERIC_FORMAT : pixel format이 GDI라이브러리가 지원될경우 명시됨
PFD_NEED_PALETTE
PFD_NEED_SYSTEM_PALETTE
PFD_DOUBLEBUFFER : 더블버퍼링의 사용여부를 결정(단 GDI와 공유못함)
PFD_STEREO
- iPixelType은 색상을 표현시 사용하는 방식을 설정합니다.
PFD_TYPE_RGBA : 비트셋은 R,G,B값으로 사용함
PFD_TYPE_COLORINDEX : 각 비트셋을 색상룩업테이블을 사용함
- cColorBits은 색의 설정에 있어서 사용하는 비트수를 결정합니다.
- cRedBits, cGreenBits, cBlueBits, cAlphaBits는 각 요소에 대해 비트수를 대표합니다.
- cRedShift, cGreenShift, cBlueShift, cAlphaShift는 각 색상요소에대한 시작색값의 offset위치를 나타냅니다.

우선 중요한것을 설명했습니다. 나머지 파라메터에 대해 궁금하시면, Visual C++의 Online메뉴얼을 참고하시기 바랍니다.

m_hGLPixelIndex = ChoosePixelFormat(hDC, &pixelDesc);

ChoosePixelForamt은 hDC와 PIXELFORMATDEXCRIPTOR를 수용하고, 이 형식을 참조할 인덱스값을 리턴합니다. (만약 실패할경우 0 값)

형식이 설정되었으면,  RC를 생성하고, 설정해야합니다.

BOOL CSampleView::CreateViewGLContext(HDC hDC)
{
        m_hGLContext = wglCreateContext(hDC);
        if (m_hGLContext == NULL)
        {
                return FALSE;
        }
        if (wglMakeCurrent(hDC, m_hGLContext)==FALSE)
        {
                return FALSE;
        }
        return TRUE;
}

이 클래스에 아래 변수를 추가합니다.
HGLRC m_hGLContext; // protected

그리고, OnCreate에 위 함수를 호출하는 부분을 추가합니다.
int CSampleView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
        if (CView::OnCreate(lpCreateStruct) == -1)
                return -1;
        HWND hWnd = GetSafeHwnd();
        HDC hDC = ::GetDC(hWnd);
        if (SetWindowPixelFormat(hDC)==FALSE)
                return 0;
        if (CreateViewGLContext(hDC)==FALSE)
                return 0;
        return 0;
}

□ 파  괴
void CSampleView::OnDestroy()
{
        if(wglGetCurrentContext()!=NULL)
        {
                // make the rendering context not current
                wglMakeCurrent(NULL, NULL) ;
        }
        if (m_hGLContext!=NULL)
        {
                wglDeleteContext(m_hGLContext);
                m_hGLContext = NULL;
        }
        // Now the associated DC can be released.
        CView::OnDestroy();
}


마지막으로 본 클래스의 생성자에 다음과 같이 추가합니다.

CSampleView::CSampleView()
{
        m_hGLContext = NULL;
        m_GLPixelIndex = 0;
}


여기까지, OpenGL의 환경설정부분이 끝났습니다. 이부분에서 역시 한번 컴파일해서, 오타가 났는지를 검사하고 넘어가시기바랍니다.

위 프로그램을 실행시켜보면, 일반적인 MFC 플랫폼으로 보입니다. 하지만, 실질적으로는 OpenGL 묘화가 가능한 상태입니다. 이 코드들의 앞부분에서, RC를 생성을 했으며, 그리기를 할때마다, DC를 생성해야하는 GDI프로그램과는 다르게, 이후로는 자유롭게 OpenGL을 사용할수 있습니다.

다음으로 각 메소드의 역할에 대해 알아보겠습니다.

CreateViewGLContext는 현재의 RC를 생성합니다. wglCreateContext는 RC로 핸들을 넘겨줍니다. 그리고, DC와 관련된 픽셀형식은 CreateViewGLContext가 생성되기전에 선언되어야 합니다. wglMakeCurrent는 위에서 생성한 RC를 현재의 context로 지정합니다. 만일 다른 RC가 wglMakeCurrent에 의해 지정된경우에는 예전의 RC를 방출해 버리며(flush) 새로운것으로 교체시킵니다. 그리고, wglMakeCurrent(NULL, NULL)을 사용해서, RC를 만들지 않음을 지정할수도 있습니다.

OnDestroy함수는 윈도우의 RC를 제거하기 때문에, 렌더링 콘텍스트를 이곳에서 삭제해야합니다. 하지만, RC를 제거하기 전에, 제거할 RC가 현재의 RC로 지정되지 않았는지를 확인해야 합니다. wglGetCurrentContext를 사용해서, 현재의 RC가 무엇인지를 알수 있습니다. 만일, 존재할경우에는 wglMakeCurrent(NULL, NULL)을 이용해서 그것을 제거해야합니다. 다음으로는 wglDeleteContext를 이용해서 RC를 제거합니다.(이 순서를 지키지않을경우, 따르는 피해는 엄마 엄마 할껍니다.)

OpenGL에서의 팁들...

1. viewport와 matrix mode는 WM_SIZE 메세지에 지정한다.
2. 모든 묘화는 WM_PAINT메세지에서 행한다.
3. RC를 생성하는것은 CPU를 많이 잡아먹으므로, 가급적 하나의 프로그램에 한번만 생성시키도록!
4. document class에 묘화시키는 명령들을 캡슐화 시키도록 노력할것. 이것이 다른 뷰에서 같은 도큐먼트를 참조하는것이 가능하게 되니깐 말이얌.
다음검색
현재 게시글 추가 기능 열기

댓글

댓글 리스트
맨위로

카페 검색

카페 검색어 입력폼