1. 編譯環境設定
a. 連結 OpenGL 函式庫, 在 Visual C++ 的 Project, Setting 中, 點選 Linker. 加上 OpenGL32.lib GLu32.lib, 如下圖所示:
b. 引用OpenGL標頭檔
// Include OpenGL header file
#include <gl\gl.h> // Header File For The OpenGL32 Library
#include <gl\glu.h> // Header File For The GLu32 Library
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
WNDCLASS wc;
MSG uMsg;
memset(&uMsg, 0, sizeof(uMsg));
//下面程式碼中, 我們抓到視窗的 instance, 然後就定義視窗類別
g_hInstance = GetModuleHandle(NULL); // Grab An Instance For Our Window
wc.lpszClassName = "MY_WINDOWS_CLASS";
//CS_HREDRAW 和 CS_VREDRAW 是用來強迫視窗在改變大小時要重新繪製. CS_OWNDC
//為視窗建立私有的 DC. 也就是指 DC 不能被別的應用程式所共用
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = WndProc;
wc.hInstance = g_hInstance;
wc.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_WIN32GL);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
wc.lpszMenuName = NULL;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
if( !RegisterClass(&wc) )
return E_FAIL;
g_WinWidth = 1280;
g_WinHeight = 800;
//建立視窗
g_hWnd = CreateWindowEx( NULL,
"MY_WINDOWS_CLASS",
"OpenGL Win32 Window",
//我們在視窗形式上設了 WS_CLIPSIBLINGS 和 WS_CLIPCHILDREN.
//WS_CLIPSIBLINGS 和 WS_CLIPCHILDREN 是必須要的, 這樣 OpenGL 才能
//正常運作. 這些形式的設定可以避免別的視窗畫到我們的 OpenGL 視窗上.
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
0, 0, g_WinWidth, g_WinHeight,
NULL,
NULL,
g_hInstance,
NULL );
if( g_hWnd == NULL )
return E_FAIL;
ShowWindow(g_hWnd, nCmdShow);
UpdateWindow(g_hWnd);
....................
}
基本上分成下面幾個步驟:
a. 先取得一個DC(Device Context); 開始在視窗繪圖之前, 一定要有一個Windows Device Context; Windows下的任何繪圖(即時繪製到記憶體內的bitmap)都需要DC以識別被繪製的物件.
b. 調整這個DC的像素格示(PixelFormat)讓OpenGL進行繪製
c. 用wglCreateContext(hDC)去得到RC(OpenGL的著色區)
d. 用wglMakeCurrent(hRC,hDC)將剛才建立的RC變成目前的RC
部分程式碼如下所示:
BOOL CreateGLWindow(int WinWidth, int WinHeight)
{
// 每一個 OpenGL 程式都要連結到一個著色區. 著色區就是連結到 OpenGL 的裝置內容 (Device Context). OpenGL 的著色區定義為 hRC. 為了將你的程式畫到視窗上, 你必須建立一個裝置內容(Device Context). 視窗的裝置內容 (Device Context) 定義為 hDC. 這個 DC 連接到視窗的 GDI (圖形裝置介面). RC 連接 OpenGL 到 DC.
if (!(g_hDC = GetDC(g_hWnd))) // Did We Get A Device Context?
{
KillGLWindow();
MessageBoxA(NULL,"Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
if(!SetupPixelFormat(g_hDC))
{
KillGLWindow();
return FALSE;
}
if (!(g_hRC = wglCreateContext(g_hDC))) // Are We Able To Get A Rendering Context?
{
KillGLWindow(); // Reset The Display
MessageBoxA(NULL,
"Can't Create A GL Rendering Context.",
"ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
if(!wglMakeCurrent(g_hDC,g_hRC)) // Try To Activate The Rendering Context
{
KillGLWindow();
MessageBoxA(NULL,
"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
ReSizeGLScene(WinWidth, WinHeight); // Set Up Our Perspective GL Screen
if (!InitGL()) // Initialize Our Newly Created GL Window
{
KillGLWindow(); // Reset The Display
MessageBoxA(NULL,"Initialization Failed.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
return TRUE;
}
SetupPixelFormat的程式碼如下所示:
// The pixel format is an extension to the Win32 API that is provided for support of OpenGL functionality.
BOOL SetupPixelFormat(HDC hDC)
{
int nPixelFormat;
//這一段程式碼描述一個圖素的格式. 我們選擇一個格式支援 OpenGL 和雙緩衝區, 在 RGBA (紅,綠, 藍, 透明) 狀態. 並且找一個圖素格式符合所選的色彩位元數 (16bit,24bit,32bit). 最後設定 16bit的 Z-緩衝區.
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR), // size of structure.
1, // always 1.
PFD_DRAW_TO_WINDOW | // support window
PFD_SUPPORT_OPENGL | // support OpenGl
PFD_DOUBLEBUFFER, // support double buffering
PFD_TYPE_RGBA, // support RGBA
16, // bit color mode
0, 0, 0, 0, 0, 0, // ignore color bits
0, // no alpha buffer
0, // ignore shift bit
0, // no accumulation buffer
0, 0, 0, 0, // ignore accumulation bits
16, // number of depth buffer bits
0, // number of stencil buffer bits
0, // 0 means no auxiliary buffer
PFD_MAIN_PLANE, // The main drawing plane
0, // this is reserved
0, 0, 0 }; // layer masks ignored
// 準備一個裝置內容給 OpenGL 視窗, 並試著找到一個圖素格式符合我們上面所描述的
if (!(nPixelFormat = ChoosePixelFormat(hDC, &pfd))) // Did Windows Find A Matching Pixel Format?
{
MessageBoxA(NULL,"Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
//設定圖素格式.
if(!SetPixelFormat(hDC, nPixelFormat, &pfd)) // Are We Able To Set The Pixel Format?
{
MessageBoxA(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE;
}
return TRUE;
}
這個函式在程式一開始執行時, 至少會被執行一次, 以設定透視觀點. 在改變視窗大小時, 也必須呼叫此函式, OpenGL 畫面會依據視窗顯示的寬高來調整大小
GLvoid ReSizeGLScene(GLsizei width, GLsizei height) // Resize And Initialize The GL Window
{
if (height==0) // Prevent A Divide By Zero By
{
height=1; // Making Height Equal One
}
glViewport(0,0,width,height); // Reset The Current Viewport
glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix
// Calculate The Aspect Ratio Of The Window
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
glLoadIdentity(); // Reset The Modelview Matrix
}
下面這一段程式碼就是用來設定 OpenGL. 我們設定了清除畫面的顏色, 我們開啟深度緩衝區, 以及平滑著色功能, 等等. 這個函式會在 OpenGL 視窗建立後才被呼叫
int InitGL(GLvoid) // All Setup For OpenGL Goes Here
{
glShadeModel(GL_SMOOTH); // Enable Smooth Shading
glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background
glClearDepth(1.0f); // Depth Buffer Setup
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations
return TRUE; // Initialization Went OK
}
所有繪圖作業的地方, 都放在此函式當中, 在此, 我們繪製一個矩形
int DrawGLScene(GLvoid) // Here's Where We Do All The Drawing
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer
glLoadIdentity(); // Reset The Current Modelview Matrix
glTranslatef(0.0, 0.0, -10.0);
glColor3f(60.0, 0.0, 230.0);
glRectf(0.0, 0.0, 1.0, 1.0);
return TRUE; // Keep Going
}
while( uMsg.message != WM_QUIT )
{
if( PeekMessage( &uMsg, NULL, 0, 0, PM_REMOVE ) )
{
TranslateMessage( &uMsg );
DispatchMessage( &uMsg );
}
else
{
DrawGLScene();
SwapBuffers(g_hDC);
}
}
GLvoid KillGLWindow(GLvoid) // Properly Kill The Window
{
if (g_hRC) // Do We Have A Rendering Context?
{
if (!wglMakeCurrent(NULL,NULL)) // Are We Able To Release The DC And RC Contexts?
{
MessageBoxA(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
}
if (!wglDeleteContext(g_hRC)) // Are We Able To Delete The RC?
{
MessageBoxA(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
}
g_hRC=NULL; // Set RC To NULL
}
if (g_hDC && !ReleaseDC(g_hWnd,g_hDC)) // Are We Able To Release The DC
{
MessageBoxA(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
g_hDC=NULL; // Set DC To NULL
}
if (g_hWnd && !DestroyWindow(g_hWnd)) // Are We Able To Destroy The Window?
{
MessageBoxA(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
g_hWnd=NULL; // Set hWnd To NULL
}
if (!UnregisterClass("MY_WINDOWS_CLASS", g_hInstance)) // Are We Able To Unregister Class
{
MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
g_hInstance=NULL; // Set hInstance To NULL
}
}
下面圖示為此範例的結果:
沒有留言:
張貼留言