轉自http://blog.csdn.net/augusdi/article/details/20572533
代碼如下:
/*****************************************新添加的代碼*****************************************/
#include <Windows.h>
#include <stdio.h> //標準輸入/輸出庫的頭文件
#include <math.h> //數學庫
#include <stdarg.h> //用來定義可變參數的頭文件
/**********************************************************************************************/
#include <GL/glut.h> //包含OpenGL實用庫
#pragma comment(lib,"glut32.lib")
HDC hDC = NULL; //窗口着色描述表句柄
HWND hWnd = NULL; //保存窗口句柄
HGLRC hGLRC = NULL; //OpenGL渲染描述表句柄
HINSTANCE hInstance; //保存程序的實例
BOOL keys[256]; //保存鍵盤按鍵的數組
BOOL active = TRUE; //窗口的活動標誌, 缺省爲TRUE
BOOL fullscreen = TRUE; //全屏標誌, 缺省爲全屏模式
UINT winWidth = 640, //窗體寬度
winHeight = 480, //窗體高度
winBits = 16; //顏色深度(可選8/16/32)
/*****************************************新添加的代碼*****************************************/
GLuint base; //繪製字體的顯示列表的開始位置
GLYPHMETRICSFLOAT gmf[256]; //保存256個輪廓字體顯示列表中對應的每一個列表的位置和方向的信息
void BuildFont()
{
HFONT font, oldfont; //字體句柄, 舊的字體句柄
base = glGenLists(256); //創建256個顯示列表
font = CreateFont( //創建字體
-24, //字體高度(告訴Windows尋找基於CHARACTER高度的字體.如果是正數, 就尋找基於CELL的高度相匹配的字體)
0, //字體寬度(使用默認值)
0, //字體的旋轉角度Angle Of Escapement
0, //字體底線的旋轉角度Orientation Angle
FW_BOLD, //字體的重量(0-1000)[FW_DONTCARE是0, FW_NORMAL是400, FW_BOLD是700, FW_BLACK是900]
FALSE, //是否使用斜體
FALSE, //是否使用下劃線
FALSE, //是否使用刪除線
ANSI_CHARSET, //設置字符集
OUT_TT_PRECIS, //輸出精度
CLIP_DEFAULT_PRECIS, //裁剪精度
ANTIALIASED_QUALITY, //輸出質量
DEFAULT_PITCH | FF_DONTCARE, //Pitch And Family
"Times New Roman" //字體名稱
);
oldfont = (HFONT)SelectObject(hDC, font); //選擇需要的字體
wglUseFontOutlines( //使用Windows的wgl函數來創建字體
hDC, //設置當前窗口設備描述表的句柄
0, //用於創建顯示列表字體的第1個字符的ASCII值
255, //字符數
base, //第1個顯示列表的名稱
0.0f, //字體的光滑度, 越小越光滑, 0.0爲最光滑的狀態
0.2f, //在Z方向突出的距離(即輪廓字體的厚度)
WGL_FONT_POLYGONS, //使用多邊形來生成字符, 每個頂點具有獨立的法線(WGL_FONT_LINES:使用線形生成字符)
gmf //一個接收字形度量數據的數組的地址, 每個數組元素用它對應的顯示列表字符的數據填充
);
SelectObject(hDC, oldfont); //選擇原來的字體
DeleteObject(font); //刪除字體
}
void glPrintf(const char *fmt, ...)
{
if(!fmt) return; //如果無輸入則返回
char text[256]; //保存文字串
va_list ap; //指向一個變量列表的指針
va_start(ap, fmt); //分析可變參數
vsprintf(text, fmt, ap); //把參數值寫入字符串
va_end(ap); //結束分析
/////////////////////將字符串居中/////////////////////
float length = 0.0f; //保存字符串的長度
for(UINT i=0;i<strlen(text);i++){ //查找整個字符串的長度
length += gmf[text[i]].gmfCellIncX; //計算輪廓後字符串的寬度(gmfCellIncX表示顯示位置從已繪製上的上一個字符向右移動的真正距離)
}
glTranslatef(-length/2.0f, 0.0f, 0.0f); //把字符串置於最左邊
//////////////////////////////////////////////////////
glPushAttrib(GL_LIST_BIT); //把顯示列表屬性壓入屬性堆棧
glListBase(base); //設置顯示列表的基礎值爲0
glCallLists(strlen(text), GL_UNSIGNED_BYTE, text); //調用顯示列表繪製字符串
glPopAttrib(); //彈出屬性堆棧
}
void KillFont() //刪除顯示列表
{
glDeleteLists(base, 256); //刪除256個顯示列表
}
/**********************************************************************************************/
//重置OpenGL窗口大小
GLvoid ReSizeGLScene(UINT width, UINT height)
{
if(height <= 0){ height = 1; } //防止被零除且防止負數存在
glViewport(0, 0, (GLsizei)width, (GLsizei)height); //重置當前的視口
glMatrixMode(GL_PROJECTION); //選擇投影矩陣
glLoadIdentity(); //重置投影矩陣
gluPerspective(45.0f, (GLdouble)width / (GLdouble)height, 0.1f, 100.0f); //設置視口的大小
glMatrixMode(GL_MODELVIEW); //選擇模型觀察矩陣
glLoadIdentity(); //重置模型觀察矩陣
}
//對OpenGL窗口進行初始化設置
BOOL InitGL(GLvoid)
{
glShadeModel(GL_SMOOTH); //啓用陰影平滑
glClearColor(0.0f, 0.0f, 0.0f, 0.5f); //黑色背景
glClearDepth(1.0f); //設置深度緩存
glEnable(GL_DEPTH_TEST); //啓用深度測試
glDepthFunc(GL_LEQUAL); //所作深度測試的類型
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); //告訴系統對透視進行修正
/*****************************************新添加的代碼*****************************************/
BuildFont(); //創建字體
/**********************************************************************************************/
return TRUE; //初始化成功
}
//從這裏開始進行所有的繪製
BOOL DrawGLScene(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕和深度緩存
glLoadIdentity(); //重置當前的模型觀察矩陣
/*****************************************新添加的代碼*****************************************/
static GLfloat rot = 0.0f; //旋轉變量
glTranslatef(0.0f, 0.0f, -15.0f); //移入屏幕15個單位
glRotatef(rot, 1.0f, 0.0f, 0.0f); //沿X軸旋轉
glRotated(rot * 1.5f, 0.0f, 1.0f, 0.0f); //沿Y軸旋轉
glRotatef(rot * 1.4f, 0.0f, 0.0f, 1.0f); //沿Z軸旋轉
/////////////////根據字體位置設置顏色/////////////////
float r = 1.0f * float(cos(rot / 20.0f)), //紅色
g = 1.0f * float(sin(rot / 25.0f)), //綠色
b = 1.0f - 0.5f * float(cos(rot / 17.0f)); //藍色
glColor3f(r, g, b); //設置顏色
//////////////////////////////////////////////////////
glPrintf("3D Active OpenGL Text - %3.2f", rot / 50); //輸出文字到屏幕
rot += 0.5f; //增加旋轉變量
/**********************************************************************************************/
return TRUE; //繪製場景成功
}
//銷燬窗口
GLvoid KillGLWindow(GLvoid)
{
if(fullscreen) //是否處於全屏模式
{
ChangeDisplaySettings(NULL, 0); //切換回桌面
ShowCursor(TRUE); //顯示鼠標指針
}
if(hGLRC)
{ //是否擁有OpenGL描述表
if(!wglMakeCurrent(NULL, NULL))
{ //是否已釋放DC和RC描述表
MessageBox(NULL, "釋放DC或RC失敗!", "錯誤", MB_OK | MB_ICONEXCLAMATION);
}
if(!wglDeleteContext(hGLRC))
{ //是否已刪除RC
MessageBox(NULL, "釋放RC失敗!", "錯誤", MB_OK | MB_ICONEXCLAMATION);
}
hGLRC = NULL; //將RC設爲NULL
}
if(hDC && !ReleaseDC(hWnd, hDC))
{ //是否已釋放DC
MessageBox(NULL, "釋放DC失敗!", "錯誤", MB_OK | MB_ICONEXCLAMATION);
hDC = NULL; //將DC設爲NULL
}
if(hWnd && !DestroyWindow(hWnd))
{ //是否已銷燬窗口
MessageBox(NULL, "銷燬窗口失敗!", "錯誤", MB_OK | MB_ICONEXCLAMATION);
hWnd = NULL; //將hWnd設爲NULL
}
if(!UnregisterClass("OpenGL", hInstance))
{ //是否已註銷類
MessageBox(NULL, "註銷窗口類失敗!", "錯誤", MB_OK | MB_ICONEXCLAMATION);
hInstance = NULL; //將hInstance設爲NULL
}
/*****************************************新添加的代碼*****************************************/
KillFont(); //刪除字體
/**********************************************************************************************/
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
//WndProc(窗口的句柄, 窗口的消息, 附加的消息內容, 附加的消息內容)
switch(uMsg)
{ //檢查Windows消息
case WM_ACTIVATE: //監視窗口激活消息
if(!HIWORD(wParam))
{ //檢查最小化狀態
active = TRUE; //程序處於激活狀態
}
else
{
active = FALSE; //程序不再激活
}
return 0; //返回消息循環
case WM_CLOSE: //收到Close消息
PostQuitMessage(0); //發出退出消息
return 0; //返回
case WM_KEYDOWN: //有鍵被按下
keys[wParam] = TRUE; //設爲TRUE
return 0; //返回
case WM_KEYUP: //有鍵被放開
keys[wParam] = FALSE; //設爲FALSE
return 0; //返回
case WM_SIZE: //調整OpenGL窗口大小
ReSizeGLScene(LOWORD(lParam), HIWORD(lParam)); //LoWord=Width, HiWord=Height
return 0; //返回
case WM_SYSCOMMAND: //系統中斷命令
switch(wParam)
{ //檢查系統調用
case SC_SCREENSAVE: //運行屏保
case SC_MONITORPOWER: //顯示器要進入節電模式
return 0; //阻止發生
}
break; //退出
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
BOOL CreateGLWindow(const char *title, UINT width, UINT height, UINT bits, BOOL fullscreenflag)
{
//CreateGLWindow(標題, 寬度, 高度, 顏色的位深, 是否使用全屏模式)
GLuint PixelFormat; //保存查找匹配的結果
DWORD dwStyle, dwExStyle; //窗口風格, 擴展窗口風格
RECT WindowRect; //取得矩形的左上角和右下角的座標值
WNDCLASS wc; //窗口類結構
hInstance = GetModuleHandle(NULL); //取得我們窗口的實例
fullscreen = fullscreenflag; //設置全局全屏標誌
WindowRect.top = (long)0; //將Top 設爲 0
WindowRect.left = (long)0; //將Left 設爲 0
WindowRect.right = (long)width; //將Right 設爲要求的寬度
WindowRect.bottom = (long)height; //將Bottom 設爲要求的高度
/*
像素格式明確了OpenGL繪製平面的特性, 如象素緩衝區是單緩衝還是雙緩衝, 數據是 RGBA方式還是Color Index方式等.每個OpenGL顯示設
備一般用名爲PIXELFORMATDESCRIPTOR的結構來表示某個的像素格式, 這個結構包含26個屬性信息.Win32定義PIXELFORMATDESCRIPTOR如下所示:
typedef struct tagPIXELFORMATDESCRIPTOR
{ //pfd
WORD nSize; //是象素格式描述子結構的大小, sizeof(PIXELFORMATDESCRIPTOR)設定其值
WORD nVersion; //是PIXELFORMATDESCRIPTOR結構的版本, 一般設爲1
DWORD dwFlags; //是一組表明象素緩衝特性的標誌位, 如緩衝是否支持GDI或OpenGL等
BYTE iPixelType; //說明象素數據類型是RGBA還是顏色索引
BYTE cColorBits; //每個顏色緩衝區中顏色位平面的數目, 對顏色索引方式是緩衝區大小
BYTE cRedBits; //每個RGBA顏色緩衝區中紅色位平面的數目
BYTE cRedShift; //每個RGBA顏色緩衝區中紅色位平面的偏移數
BYTE cGreenBits; //每個RGBA顏色緩衝區中綠色位平面的數目
BYTE cGreenShift; //每個RGBA顏色緩衝區中綠色位平面的偏移數
BYTE cBlueBits; //每個RGBA顏色緩衝區中藍色位平面的數目
BYTE cBlueShift; //每個RGBA顏色緩衝區中藍色位平面的偏移數
BYTE cAlphaBits; //每個RGBA顏色緩衝區中alpha位平面的數目(保留的, 現不支持)
BYTE cAlphaShift; //每個RGBA顏色緩衝區中alpha位平面的偏移數(保留的, 現不支持)
BYTE cAccumBits; //累加緩衝區中全部位平面的數目
BYTE cAccumRedBits; //累加緩衝區中紅色位平面的數目
BYTE cAccumGreenBits; //累加緩衝區中綠色位平面的數目
BYTE cAccumBlueBits; //累加緩衝區中藍色位平面的數目
BYTE cAccumAlphaBits; //累加緩衝區中alpha位平面的數目
BYTE cDepthBits; //Z(深度)緩衝區的深度
BYTE cStencilBits; //模板緩衝區的深度
BYTE cAuxBuffers; //軸向緩衝區的數量(一般1.0版本不支持)
BYTE iLayerType; //被忽略, 爲了一致性而包含的
BYTE bReserved; //表層和底層平面的數量::位0-3表最多15層表層平面, 位4-7表底層
DWORD dwLayerMask; //被忽略, 爲了一致性而包含的
DWORD dwVisibleMask; //是透明色彩的值(RGBA方式)或是一個底層平面的索引(Index)
DWORD dwDamageMask; //被忽略, 爲了一致性而包含的
} PIXELFORMATDESCRIPTOR;
*/
const PIXELFORMATDESCRIPTOR pfd = //pfd告訴窗口使用的像素格式
{
sizeof(PIXELFORMATDESCRIPTOR), //上述格式描述符的大小
1, //版本號
PFD_DOUBLEBUFFER | PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL, //格式支持雙緩衝, 支持窗口, 支持OpenGL
PFD_TYPE_RGBA, //申請RGBA格式
bits, //選定色彩深度
0, 0, 0, 0, 0, 0, 0, 0, //忽略的色彩位(前6位), 無Alpha緩存(第7位), 忽略Shift Bit(第8位)
0, //無累加緩存
0, 0, 0, 0, //忽略聚集位
16, //16位Z-緩存(深度緩存)
0, //無蒙板緩存
0, //無輔助緩存
PFD_MAIN_PLANE, //主繪圖層
0, //不使用重疊層
0, 0, 0 //忽略層遮罩
};
wc.cbClsExtra = 0; //無額外窗口數據
wc.cbWndExtra = 0; //無額外窗口數據
wc.hbrBackground = NULL; //GL不需要背景
wc.hCursor = LoadCursor(NULL, IDC_ARROW); //裝入鼠標指針
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); //裝入缺省圖標
wc.hInstance = hInstance; //設置實例
wc.lpfnWndProc = (WNDPROC)WndProc; //WndProc處理消息
wc.lpszClassName = "OpenGL"; //設定類名字
wc.lpszMenuName = NULL; //不需要菜單
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; //移動時重畫, 併爲窗口取得DC
if(!RegisterClass(&wc))
{ //嘗試註冊窗口類
MessageBox(NULL, "註冊窗口類錯誤!", "錯誤", MB_OK | MB_ICONEXCLAMATION);
return FALSE; //退出並返回FALSE
}
if(fullscreen)
{ //嘗試全屏模式
DEVMODE dmScreenSettings; //設備模式
memset(&dmScreenSettings, 0, sizeof(dmScreenSettings)); //確保內存清空爲零
dmScreenSettings.dmSize = sizeof(dmScreenSettings); //DEVMODE結構的大小
dmScreenSettings.dmBitsPerPel = bits; //每象素所選的色彩深度
dmScreenSettings.dmPelsWidth = width; //所選屏幕寬度
dmScreenSettings.dmPelsHeight = height; //所選屏幕高度
dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
//嘗試設置顯示模式並返回結果(注:CDS_FULLSCREEN 移去了狀態條)
if(ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
{
if(MessageBox(NULL, "全屏模式失敗!是否使用窗口模式?", "錯誤", MB_YESNO | MB_ICONQUESTION) == IDYES)
{
fullscreen = FALSE; //選擇窗口模式
}
else
{
MessageBox(NULL, "程序退出!", "錯誤", MB_OK | MB_ICONSTOP);
return FALSE; //退出並返回FALSE
}
}
}
if(fullscreen)
{ //仍處於全屏模式
dwStyle = WS_POPUP; //窗體風格
dwExStyle = WS_EX_APPWINDOW; //擴展窗體風格
ShowCursor(FALSE); //隱藏鼠標指針
}
else
{
dwStyle = WS_OVERLAPPEDWINDOW; //窗體風格
dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; //擴展窗體風格
}
AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); //調整窗口達到真正要求的大小
if(!(hWnd = CreateWindowEx(dwExStyle, //擴展窗體風格
"OpenGL", //類名字
title, //窗口標題
dwStyle | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, //必須的窗體風格屬性
0, 0, //窗口左上角X座標和Y座標位置
WindowRect.right - WindowRect.left, //計算調整好的窗口高度的寬度
WindowRect.bottom - WindowRect.top, //計算調整好的窗口高度的高度
NULL, //無父窗口
NULL, //無菜單
hInstance, //實例
NULL))){ //不向WM_CREATE傳遞任何信息
KillGLWindow(); //重置顯示區
MessageBox(NULL, "窗口創建失敗!", "錯誤", MB_OK | MB_ICONEXCLAMATION);
return FALSE; //窗口創建失敗
}
if(!(hDC = GetDC(hWnd)))
{ //是否取得設備描述表
KillGLWindow(); //重置顯示區
MessageBox(NULL, "創建設備描述表失敗!", "錯誤", MB_OK | MB_ICONEXCLAMATION);
return FALSE; //創建設備描述表失敗
}
if(!(PixelFormat = ChoosePixelFormat(hDC, &pfd)))
{ //是否Windows找到相應的象素格式
KillGLWindow(); //重置顯示區
MessageBox(NULL, "沒有相匹配的像素格式!", "錯誤", MB_OK | MB_ICONEXCLAMATION);
return FALSE; //沒有相匹配的像素格式
}
if(!SetPixelFormat(hDC, PixelFormat, &pfd))
{ //是否能設置象素格式
KillGLWindow(); //重置顯示區
MessageBox(NULL, "設置像素格式失敗!", "錯誤", MB_OK | MB_ICONEXCLAMATION);
return FALSE; //設置像素格式失敗
}
if(!(hGLRC = wglCreateContext(hDC)))
{ //是否能取得OpenGL渲染描述表
KillGLWindow(); //重置顯示區
MessageBox(NULL, "創建OpenGL渲染表失敗!", "錯誤", MB_OK | MB_ICONEXCLAMATION);
return FALSE; //創建OpenGL渲染表失敗
}
if(!wglMakeCurrent(hDC, hGLRC))
{ //嘗試激活着色描述表
KillGLWindow(); //重置顯示區
MessageBox(NULL, "激活當前OpenGL渲染表失敗!", "錯誤", MB_OK | MB_ICONEXCLAMATION);
return FALSE; //激活當前OpenGL渲染表失敗
}
ShowWindow(hWnd, SW_SHOW); //顯示窗口
SetForegroundWindow(hWnd); //略略提高優先級
SetFocus(hWnd); //設置鍵盤的焦點至此窗口
ReSizeGLScene(width, height); //設置透視GL屏幕
if(!InitGL())
{ //初始化新建的GL窗口
KillGLWindow();
MessageBox(NULL, "初始化失敗!", "錯誤", MB_OK | MB_ICONEXCLAMATION);
return FALSE; //初始化失敗
}
return TRUE; //創建窗口成功
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
//WinMain(當前窗口實例, 前一個窗口實例, 命令行參數, 窗口顯示狀態)
MSG msg; //Windowsx消息結構
BOOL done = FALSE; //用來退出循環的BOOL變量
winWidth = 640; //定義窗口寬度
winHeight = 480; //定義窗口高度
winBits = 16; //定顏色深度爲
//提示用戶選擇運行模式
if(MessageBox(NULL, "是否使用全屏模式?", "OpenGL", MB_YESNO | MB_ICONQUESTION) == IDNO)
{
fullscreen = FALSE; //FALSE爲窗口模式
}
//創建OpenGL窗口
if(!CreateGLWindow("OpenGL", winWidth, winHeight, winBits, fullscreen))
{
return 0; //失敗退出
}
while(!done)
{ //保持循環直到done=TRUE
if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{ //是否在等待消息
if(msg.message == WM_QUIT)
{ //收到退出消息
done = TRUE; //是, 則done=TRUE
}
else
{ //不是, 處理窗口消息
TranslateMessage(&msg); //翻譯消息
DispatchMessage(&msg); //發送消息
}
}
else
{
//如果沒有消息
//繪製場景。監視ESC鍵和來自DrawGLScene()的退出消息
if(active)
{ //程序是否激活
if(keys[VK_ESCAPE])
{ //是否ESC按下
done = TRUE; //ESC發出退出信號
}
else
{ //不是退出的時候, 刷新屏幕
DrawGLScene(); //繪製場景
SwapBuffers(hDC); //交換緩存(雙緩存)
}
}
if(keys[VK_F1])
{ //是否按下F1鍵
keys[VK_F1] = FALSE; //若是, 使對應的Key數組中的值爲FALSE
KillGLWindow(); //銷燬當前的窗口
//切換全屏/窗口模式
if(!CreateGLWindow("OpenGL", winWidth, winHeight, winBits, !fullscreen))
{ //重建OpenGL窗口
return 0; //如果窗口未能創建, 程序退出
}
}
}
}
//關閉程序
KillGLWindow(); //銷燬窗口
return msg.wParam; //退出程序
}