(2010-03-08)
目前很多关于OpenGL的教材或教程都是基于VC来创建Win32下的OpenGL窗口的,而VS2005下的Win32项目的自生成代码与VC的还是略有不同。为了方便以后在VS2005下开发OpenGL程序,保留一个创建OpenGL窗口的模板是必要的。
假设新建了一个名为glTemp的Win32项目,找到其主函数(即程序入口点)所在的文件glTemp.cpp,在其中编写创建OpenGL窗口的代码:
(1)首先不要忘了在stdafx.h中加入OpenGL必要的头文件和链接库:
#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glaux.h>
#include <gl/glut.h>
#pragma comment(lib,"opengl32.lib")
#pragma comment(lib,"glu32.lib")
#pragma comment(lib,"glaux.lib")
#pragma comment(lib,"glut32.lib")
当然,.lib库文件也可以用非编程方式,而通过设置VS2005项目属性加入。
(2)glTemp.cpp代码如下:
// glTemp.cpp : 定义应用程序的入口点。
//
#include "stdafx.h"
#include "glTemp.h"
#define MAX_LOADSTRING 100
// 全局变量:
HINSTANCE hInst; // 当前实例
TCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
TCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
// 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
HWND hWnd; //窗口句柄
HDC hDC; //私有的GDI设备描述表
HGLRC hRC=NULL; //着色描述表
RECT rect;
int sw=640,sh=480; //全屏分辨率
bool fullscreen=1; //全屏标志
GLfloat aspect; //斜率
bool keys[256]; //按键是否被按下的信息
int mx,my; //获取鼠标指针坐标
//判断鼠标是否移动、是否左击、右击、双击
bool m_move,m_left,m_right,m_dclick;
//初始化OpenGL窗口
void SceneInit(int w,int h)
{
glShadeModel(GL_SMOOTH); //允许平滑着色
glClearColor(0.6f,0.2f,0.0f,0.5); //设置背景色
glClearDepth(1.0f); //设置深度缓冲区
glEnable(GL_DEPTH_TEST); //允许深度测试
glDepthFunc(GL_LEQUAL); //深度测试的类型
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST); //更加精细的透视校正
}
//重置OpenGL场景尺寸
void SceneResizeViewport(GLsizei w,GLsizei h)
{
if(h==0)
h=1;
aspect=(GLfloat)w/(GLfloat)h;
glViewport(0,0,w,h); //设置当前视口
glMatrixMode(GL_PROJECTION); //选择投影矩阵
glLoadIdentity(); //重置投影矩阵
gluPerspective(45.0f,aspect,0.1f,100.0f); //设置透视视景
glMatrixMode(GL_MODELVIEW); //选择模型视图矩阵
glLoadIdentity(); //重置模型视图矩阵
}
//所有OpenGL绘制
void SceneShow(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); //清屏和清除深度缓冲区
glLoadIdentity(); //重置当前Modelview矩阵(原点回到窗口中心)
gluLookAt(0,0,10,0,0,0,0,1,0);//设置视觉坐标系(视点位置,目标点位置,视点向上方向)
/*添加任何绘图代码*/
}
//激活OpenGL
void EnbleOpenGL()
{
PIXELFORMATDESCRIPTOR pfd; //像素格式描述符
int iFormat;
hDC=GetDC(hWnd); //获取设备描述表
ZeroMemory(&pfd,sizeof(pfd));
pfd.nSize=sizeof(pfd); //格式描述符大小
pfd.nVersion=1; //版本号
//格式必须支持窗口、OpenGL、双缓冲
pfd.dwFlags=PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER;
pfd.iPixelType=PFD_TYPE_RGBA; //申请RGBA格式
pfd.cColorBits=16; //色彩深度
pfd.cDepthBits=16; //深度缓存(Z缓存)
pfd.iLayerType=PFD_MAIN_PLANE; //主绘图层
iFormat=ChoosePixelFormat(hDC,&pfd); //选择像素格式
SetPixelFormat(hDC,iFormat,&pfd); //设置像素格式
hRC=wglCreateContext(hDC); //取得着色描述表
wglMakeCurrent(hDC,hRC); //激活着色描述表
}
//关闭OpenGL
void DisableOpenGL()
{
wglMakeCurrent(NULL,NULL); //释放DC和RC描述表
wglDeleteContext(hRC); //删除RC
ReleaseDC(hWnd,hDC); //释放DC
}
//改变屏幕分辨率
bool ChangeResolution(int w,int h,int bitdepth)
{
DEVMODE devMode; //设备模式
int modeSwitch;
int closeMode=0;
EnumDisplaySettings(NULL,closeMode,&devMode);
devMode.dmBitsPerPel=bitdepth; //每像素所选的色彩深度
devMode.dmPelsWidth=w; //屏幕宽
devMode.dmPelsHeight=h; //屏幕高
devMode.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
//设置显示模式
modeSwitch=ChangeDisplaySettings(&devMode,CDS_FULLSCREEN);
if(modeSwitch==DISP_CHANGE_SUCCESSFUL)
return true;
else
{
ChangeDisplaySettings(NULL,0);
return false;
}
}
//主函数(程序入口)
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 在此放置代码。
MSG msg;
HACCEL hAccelTable;
bool bQuit=false;
m_move=false;m_left=false;m_right=false;m_dclick=false;
if(MessageBox(NULL,L"是否选择全屏显示模式?",L"全屏方式运行?",MB_YESNO|MB_ICONQUESTION)==IDNO)
fullscreen=0;
// 初始化全局字符串
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_GLTEMP, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_GLTEMP));
// 主消息循环:
while(!bQuit)
{
if(PeekMessage(&msg,NULL,0,0,PM_REMOVE))
if(msg.message==WM_QUIT) //收到退出消息
bQuit=true;
else
{
TranslateMessage(&msg); //翻译消息
DispatchMessage(&msg); //发送消息
}
else
{
//OpenGL动画
SceneShow();
SwapBuffers(hDC); //交换缓存
/*添加响应键盘或鼠标消息的代码*/
}
}
//销毁OpenGL窗口的几个后续操作
DisableOpenGL();
ShowWindow(hWnd,SW_HIDE);
DestroyWindow(hWnd);
ChangeDisplaySettings(NULL,0);
return (int) msg.wParam;
}
//
// 函数: MyRegisterClass()
//
// 目的: 注册窗口类。
//
// 注释:
//
// 仅当希望
// 此代码与添加到 Windows 95 中的“RegisterClassEx”
// 函数之前的 Win32 系统兼容时,才需要此函数及其用法。调用此函数十分重要,
// 这样应用程序就可以获得关联的
// “格式正确的”小图标。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex; //窗口类结构
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_OWNDC; //为窗口取得DC
wcex.lpfnWndProc = WndProc; //WndProc处理消息
wcex.cbClsExtra = 0; //无额外窗口数据
wcex.cbWndExtra = 0; //无额外窗口数据
wcex.hInstance = hInstance; //设置实例
wcex.hIcon = LoadIcon(NULL,IDI_APPLICATION); //装入缺省图标
wcex.hCursor = LoadCursor(NULL, IDC_ARROW); //装入鼠标指针
wcex.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); //背景
wcex.lpszMenuName = MAKEINTRESOURCE(IDC_GLTEMP); //菜单
wcex.lpszClassName = L"Name"; //类名
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
//注册窗口类
return RegisterClassEx(&wcex);
}
//
// 函数: InitInstance(HINSTANCE, int)
//
// 目的: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 将实例句柄存储在全局变量中
if(fullscreen)
{
ChangeResolution(640,480,16);
hWnd=CreateWindow(L"Name",L"Lesson1",WS_POPUP|WS_CLIPSIBLINGS|WS_VISIBLE,0,0,GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN),NULL,NULL,hInstance,NULL);
}
else
{
hWnd=CreateWindow(L"Name",L"Lesson1",WS_TILEDWINDOW|WS_VISIBLE,
GetSystemMetrics(SM_CXSCREEN)/2-sw/2,
GetSystemMetrics(SM_CYSCREEN)/2-sh/2,
sw,sh,NULL,NULL,hInstance,NULL);
ChangeDisplaySettings(NULL,0);
}
//OpenGL窗口创建完毕的后续工作
ShowWindow(hWnd,nCmdShow);
UpdateWindow(hWnd);
EnbleOpenGL();
SceneInit(sw,sh);
if(!fullscreen)
{
GetWindowRect(hWnd,&rect);
sw=rect.right-rect.left;
sh=rect.bottom-rect.top;
if(sw>0&&sh>0)
SceneResizeViewport(sw,sh);
}
else
{
SceneResizeViewport(GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN));
}
return TRUE;
}
//
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目的: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: 在此添加任意绘图代码...
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_CREATE:
GetWindowRect(hWnd,&rect);
sw=rect.right-rect.left;
sh=rect.bottom-rect.top;
SceneResizeViewport(sw,sh);
break;
case WM_SIZE:
if(!fullscreen)
{
GetWindowRect(hWnd,&rect);
sw=rect.right-rect.left;
sh=rect.bottom-rect.top;
if(sw>0&&sh>0)
SceneResizeViewport(sw,sh);
}
else
{
SceneResizeViewport(GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN));
}
break;
case WM_CLOSE:
ShowWindow(hWnd,SW_HIDE);
PostQuitMessage(0);
break;
case WM_KEYDOWN:
keys[wParam]=true;
switch(wParam)
{
case VK_ESCAPE:
PostMessage(hWnd,WM_CLOSE,0,0);
break;
case VK_F1:
{
hInst=GetModuleHandle(NULL);
break;
}
}
break;
case WM_KEYUP:
keys[wParam]=false;
break;
//lParam的低4位为鼠标指针的x坐标,高4位为y坐标
case WM_LBUTTONDOWN: // 处理鼠标左键按下消息
{
mx=LOWORD(lParam);
my=HIWORD(lParam);
m_left=true;
break;
}
case WM_LBUTTONUP: // 处理鼠标左键抬起消息
{
mx=LOWORD(lParam);
my=HIWORD(lParam);
m_left=false;
break;
}
case WM_RBUTTONDOWN: // 处理鼠标右键按下消息
{
mx=LOWORD(lParam);
my=HIWORD(lParam);
m_right=true;
break;
}
case WM_RBUTTONUP: // 处理鼠标右键抬起消息
{
mx=LOWORD(lParam);
my=HIWORD(lParam);
m_right=false;
break;
}
case WM_LBUTTONDBLCLK: // 处理鼠标左键双击消息
{
mx=LOWORD(lParam);
my=HIWORD(lParam);
m_dclick=true;
break;
}
case WM_MOUSEMOVE: // 处理鼠标移动消息
{
mx=LOWORD(lParam);
my=HIWORD(lParam);
m_move=true;
break;
}
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}