GLUT库(v1.1):https://files.cnblogs.com/hcbin/glutdlls37beta.zip
注:暂时只支持英文
//注:本人使用VS2008创建此程序
/*
1.VS2008->"新建"->"项目"->"Visual C++"->选择"空项目"->输入名称"test.sln"
2.在test项目的"解决方案资源管理器"的"源文件"上->右击添加新的"C++文件"->输入名称"test.cpp"或添加已有的cpp文件
3.双击打开test.cpp文件->在菜单栏上选择"项目"->"test属性"->"配置属性"->"C/C++"->"代码生成"->在右边将"运行时库"改为:MTd[含大量调试信息]/MT(一般情况下Debug用MTd, Release用MT)
[第3步是为了生成的程序可以在其它没有安装VS2008的电脑上运行, 没有设置会有错误警告]
*/
/*****************************************新添加的代码*****************************************/
#include <stdio.h> //标准输入/输出库的头文件
#include <math.h> //数学库
#include <stdarg.h> //用来定义可变参数的头文件
/**********************************************************************************************/
#include <GL/glut.h> //包含OpenGL实用库
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; //退出程序
}
/*
1.VS2008->"新建"->"项目"->"Visual C++"->选择"空项目"->输入名称"test.sln"
2.在test项目的"解决方案资源管理器"的"源文件"上->右击添加新的"C++文件"->输入名称"test.cpp"或添加已有的cpp文件
3.双击打开test.cpp文件->在菜单栏上选择"项目"->"test属性"->"配置属性"->"C/C++"->"代码生成"->在右边将"运行时库"改为:MTd[含大量调试信息]/MT(一般情况下Debug用MTd, Release用MT)
[第3步是为了生成的程序可以在其它没有安装VS2008的电脑上运行, 没有设置会有错误警告]
*/
/*****************************************新添加的代码*****************************************/
#include <stdio.h> //标准输入/输出库的头文件
#include <math.h> //数学库
#include <stdarg.h> //用来定义可变参数的头文件
/**********************************************************************************************/
#include <GL/glut.h> //包含OpenGL实用库
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; //退出程序
}