

1 #define WM_TOGGLEFULLSCREEN (WM_USER+1) // 定义全屏/窗口切换消息
2 #define TIMER1 101 // 定义时钟代号,设置为号
3 #define TITLE "5DG's FrameWork -- Hello OpenGL World!" // 定义窗口标题
4 #define CLASSNAME "5DG_OPENGL" // 定义窗口类名
5 #define WIDTH 640 // 定义窗口宽度
6 #define HEIGHT 480 // 定义窗口高度
7 #define BPP 16 // 定义每像素的位数
8 // 在此处引用程序要求的头文件:
9 #include <windows.h> // Windows的头文件
10 #include <gl\gl.h> // OpenGL32库的头文件
11 #include <gl\glu.h> // Glu32库的头文件
12 // 在此处加入程序要求的库到链接器中:
13
14 #pragma comment( lib, "opengl32.lib" ) // 链接时查找OpenGL32.lib
15 #pragma comment( lib, "glu32.lib" ) // 链接时查找glu32.lib
16 // 在此处定义与窗口相关的结构体:
17
18 typedef struct { // 定义处理键盘的结构体
19 BOOL keyDown [256]; // 存储键盘按键状态的数组
20 } Keys;
21 typedef struct { // 定义存储应用程序实例的结构体
22
23 HINSTANCE hInstance; // 应用程序实例
24 const char* className; // 应用程序类名
25 } Application;
26 typedef struct { // 定义初始化窗口所需信息
27
28 Application* application; // 所属的应用程序
29 char* title; // 窗口标题
30 int width; // 窗口宽度
31 int height; // 窗口高度
32 int bitsPerPixel; // 每像素的位数
33 BOOL isFullScreen; // 是否全屏
34 } GL_WindowInit;
35 typedef struct { // 定义窗口结构体
36
37 Keys* keys; // 键盘
38 HWND hWnd; // 窗口句柄
39 HDC hDC; // 设备描述表
40 HGLRC hRC; // 绘制描述表
41 GL_WindowInit init; // 提供Window初始化信息的结构体
42 } GL_Window;
43 // 此代码模块中包含的函数的前向声明:
44
45 BOOL Initialize(GL_Window* window, Keys* keys); // 设置你绘制前的初始化值
46 void DrawSceneGL(void); // 在这里完成场景的绘制
47 void Update(void); // 在此处更新对消息的动作
48 void Deinitialize(void); // 在此处做退出前扫尾工作
49 // 在此处定义全局变量:
50
51 GL_Window* OGL_window; // 存储窗口信息
52 Keys* OGL_keys; // 存储按键信息
53 // 用户定义的全局变量
2.重设窗口大小


1 void ResizeWindowGL(int width, int height) // 重新设置窗口大小
2 {
3 glViewport(0, 0, (GLsizei)(width), (GLsizei)(height)); // 重置当前视口大小
4 glMatrixMode(GL_PROJECTION); // 切换到投影矩阵模式
5 glLoadIdentity(); // 重置投影矩阵
6 gluPerspective(45, (float)width/(float)height, 0.1, 100); // 设置透视投影
7 glMatrixMode(GL_MODELVIEW); // 切换到模型视图矩阵
8 glLoadIdentity(); // 重置模型视图矩阵
9 }
3.修改屏幕分辨率


1 BOOL ChangeScreenResolution(int width, int height, int bitsPerPixel) // 修改屏幕分辨率
2 {
3 DEVMODE dmScreenSettings; // 设备设置模式
4 ZeroMemory(&dmScreenSettings, sizeof(DEVMODE)); // 清空
5 dmScreenSettings.dmSize = sizeof(DEVMODE); // Devmode结构的大小
6 dmScreenSettings.dmPelsWidth = width; // 设置为屏幕宽度
7 dmScreenSettings.dmPelsHeight = height; // 设置为屏幕高度
8 dmScreenSettings.dmBitsPerPel = bitsPerPixel; // 设为指定位长;
9 dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
10 // 尝试设置显示模式并返回结果。注: CDS_FULLSCREEN 移去了状态栏
11 if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
12 {
13 return FALSE; // 分辨率修改失败,返回False
14 }
15 return TRUE; // 分辨率修改成功,返回True;
16 }
4.建立openGL窗口


1 BOOL CreateWindowGL(GL_Window* window) // 建立OpenGL窗口
2 {
3 DWORD windowStyle = WS_OVERLAPPEDWINDOW; // 设置窗口样式
4 DWORD windowExtendedStyle = WS_EX_APPWINDOW; // 设置窗口扩展样式
5 PIXELFORMATDESCRIPTOR pfd = // 像素格式描述符(pfd)的设置
6 {
7 sizeof(PIXELFORMATDESCRIPTOR), // 像素的尺寸
8 1, // 版本号
9 PFD_DRAW_TO_WINDOW | // pfd必须支持窗口绘制
10 PFD_SUPPORT_OPENGL | // pfd必须支持OpenGL
11 PFD_DOUBLEBUFFER, // pfd必须支持双缓存
12 PFD_TYPE_RGBA, // 像素格式为RGBA
13 window->init.bitsPerPixel, // 设置颜色深度
14 0, 0, 0, 0, 0, 0, // 忽略颜色位数
15 0, // 无Alpha缓存
16 0, // 忽略偏移位
17 0, // 无累积缓存
18 0, 0, 0, 0, // 忽略累积缓存位
19 16, // 深度缓存为位
20 0, // 无模板缓存
21 0, // 无辅助缓存
22 PFD_MAIN_PLANE, // 主要绘制层
23 0, // 保留位
24 0, 0, 0 // 忽略层遮罩
25 };
26
28 RECT windowRect = {0, 0, window->init.width, window->init.height}; // 定义窗口大小
29 GLuint PixelFormat; // 保存像素格式
30 if (window->init.isFullScreen == TRUE) // 切换全屏
31 {
32 if (ChangeScreenResolution(window->init.width, window->init.height, window->init.bitsPerPixel) == FALSE)
33 {
34 // 全屏切换失败
35 MessageBox(HWND_DESKTOP,
36 "无法切换到全屏模式,在窗口模式下运行.\nMode Switch Failed,Running In Windowed Mode.",
37 "Error", MB_OK | MB_ICONEXCLAMATION);
38 window->init.isFullScreen = FALSE; // 设置isFullscreen 为False
39 }
40 else // 全屏切换成功
41 {
42 ShowCursor (FALSE); // 隐藏鼠标
43 windowStyle = WS_POPUP; // 设置窗口样式
44 windowExtendedStyle |= WS_EX_TOPMOST; // 设置窗口扩展样式
45 }
46 }
47 else
48 {
49 // 调整窗口大小,包括窗口边界
50 AdjustWindowRectEx(&windowRect, windowStyle, 0, windowExtendedStyle);
51 }
52 // 开始创建OpenGL 窗口
55 window->hWnd = CreateWindowEx(windowExtendedStyle, // 窗口扩展样式
56 window->init.application->className,// 应用程序类名
57 window->init.title, // 窗口标题
58 windowStyle, // 窗口样式
59 0, 0, // 窗口的X,Y 坐标位置
60 windowRect.right - windowRect.left, // 窗口宽度
61 windowRect.bottom - windowRect.top, // 窗口高度
62 HWND_DESKTOP, // 父窗口为桌面
63 0, // 无菜单
64 window->init.application->hInstance,// 传入应用程序实例
65 window);
66
67 if (window->hWnd == 0) // 窗口是否成功创建
68 {
69 return FALSE; // 若失败,则返回FALSE
70 }
71
72 window->hDC = GetDC(window->hWnd); // 取得当前窗口的设备描述表
73 if (window->hDC == 0) // 若未得到设备描述表
74 {
75 DestroyWindow(window->hWnd); // 销毁该窗口
76 window->hWnd = 0; // 窗口句柄清零
77 return FALSE; // 返回FALSE
78 }
79 PixelFormat = ChoosePixelFormat(window->hDC, &pfd); // 选择兼容的像素格式
80 if (PixelFormat == 0) // 若选择失败
81 {
82 ReleaseDC(window->hWnd, window->hDC); // 释放设备描述表
83 window->hDC = 0; // 将设备描述表清零
84 DestroyWindow(window->hWnd); // 销毁窗口
85 window->hWnd = 0; // 窗口句柄清零
86 return FALSE; // 返回FALSE
87 }
88 if (SetPixelFormat(window->hDC, PixelFormat, &pfd) == FALSE) // 设置像素格式并判断是否失败
89 {
90 ReleaseDC(window->hWnd, window->hDC); // 释放设备描述表
91 window->hDC = 0; // 将设备描述表清零
92 DestroyWindow(window->hWnd); // 销毁窗口
93 window->hWnd = 0; // 窗口句柄清零
94 return FALSE; // 返回FALSE
95 }
96 window->hRC = wglCreateContext(window->hDC); // 取得绘制描述表
97 if (window->hRC == 0) // 若未得到绘制描述表
98 {
99 ReleaseDC(window->hWnd, window->hDC); // 释放设备描述表
100 window->hDC = 0; // 将设备描述表清零
101 DestroyWindow(window->hWnd); // 销毁窗口
102 window->hWnd = 0; // 窗口句柄清零
103 return FALSE; // 返回FALSE
104 }
105 if (wglMakeCurrent(window->hDC, window->hRC) == FALSE) // 设置绘制描述表并判断是否失败
106 {
107 wglDeleteContext(window->hRC); // 删除绘制描述表
108 window->hRC = 0; // 将绘制描述表清零
109 ReleaseDC(window->hWnd, window->hDC); // 释放设备描述表
110 window->hDC = 0; // 将设备描述表清零
111 DestroyWindow(window->hWnd); // 销毁窗口
112 window->hWnd = 0; // 窗口句柄清零
113 return FALSE; // 返回FALSE
114 }
115 ShowWindow(window->hWnd, SW_NORMAL); // 显示窗口
116 ResizeWindowGL(window->init.width, window->init.height); // 重设窗口
117 ZeroMemory(window->keys, sizeof (Keys)); // 清空键盘缓冲区
118 return TRUE; // 窗口创建成功
119 }
5.销毁窗口


1 BOOL DestroyWindowGL(GL_Window* window) // 销毁窗口并释放程序所用的资源
2 {
3 if (window->hWnd != 0) // 窗口释放有句柄?
4 {
5 if (window->hDC != 0) // 窗口是否有得到绘制描述表?
6 {
7 wglMakeCurrent(window->hDC, 0); // 将当前描述表指针置为
8 if (window->hRC != 0) // 该窗口是否有绘制描述表
9 {
10 wglDeleteContext(window->hRC); // 释放绘制描述表
11 window->hRC = 0; // 将绘制描述表清零
12 }
13 ReleaseDC(window->hWnd, window->hDC); // 释放设备描述表
14 window->hDC = 0; // 将设备描述表清零
15 }
16 DestroyWindow(window->hWnd); // 销毁窗口
17 window->hWnd = 0; // 将窗口句柄清零
18 }
19
20 if (window->init.isFullScreen) // 若窗口在全屏模式下
21 {
22 ChangeDisplaySettings(NULL ,0); // 切换为桌面分辨率
23 ShowCursor(TRUE); // 显示鼠标
24 }
25 return TRUE; // 返回TRUE
26 }
6.窗口消息处理


1 LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)// 窗口消息处理
2 {
3
4 GL_Window* window = (GL_Window*)(GetWindowLong(hWnd, GWL_USERDATA));// 取得窗口信息
5
6 switch (uMsg) // 取得Window的消息
7 {
8 case WM_SYSCOMMAND: // 截取系统命令
9 {
10 switch (wParam) // 监听系统调用
11 {
12 case SC_SCREENSAVE: // 要运行屏保?
13 case SC_MONITORPOWER: // 显示器进入节电模式?
14 return 0; // 提前返回,防止系统调用执行
15 }
16 break; // 退出
17
18 }
19
20 case WM_CREATE: // 创建窗口
21 {
22 CREATESTRUCT* creation = (CREATESTRUCT*)(lParam); // 保存窗口结构指针
23 window = (GL_Window*)(creation->lpCreateParams);
24 SetWindowLong(hWnd, GWL_USERDATA, (LONG)(window)); // 改变窗口属性
25 return 0; // 返回
26 }
27
28 case WM_TIMER: // TIMER事件
29 {
30 InvalidateRect(window->hWnd, NULL, FALSE); // 使窗口失效
31 break;
32 }
33
34 case WM_PAINT: // PAINT消息处理,在此处绘图
35 {
36 DrawSceneGL(); // 在此处绘图
37 SwapBuffers(window->hDC); // 交换缓存
38 ValidateRect(window->hWnd, NULL); // 使窗口有效
39 break;
40 }
41
42 case WM_CLOSE: // 关闭窗口
43 {
44 PostMessage(window->hWnd, WM_QUIT, 0, 0); // 结束程序
45 return 0;
46 }
47
48 case WM_SIZE: // 窗口大小变化
49 {
50 ResizeWindowGL(LOWORD(lParam), HIWORD(lParam)); // 修改窗口大小为Width = LoWord, Height = HiWord
51 return 0; // 返回
52
53 }
54
55 case WM_KEYDOWN: // 按键按下时更新键盘缓冲
56 if ((wParam >= 0) && (wParam <= 255)) // 按键是否合法?
57 {
58 window->keys->keyDown[wParam] = TRUE; // 设相应键为True
59 return 0; // 返回
60 }
61 break; // 退出
62
63 case WM_KEYUP: // 按键松开时更新键盘缓冲
64 if ((wParam >= 0) && (wParam <= 255)) // 按键是否合法?
65 {
66 window->keys->keyDown[wParam] = FALSE; // 设相应键为为FALSE
67 return 0; // 返回
68 }
69 break; // 退出
70
71 case WM_TOGGLEFULLSCREEN: // 开关全屏模式
72 {
73 Deinitialize(); // 做扫尾工作
74 DestroyWindowGL(window); // 销毁窗口
75 window->init.isFullScreen = !window->init.isFullScreen; // 改变窗口模式
76 CreateWindowGL(window); // 重新创建窗口
77 Initialize(window, window->keys); // 初始化OpenGL绘制程序
78 break; // 退出
79 }
80
81 default:
82 break;
83 }
84
85 return DefWindowProc(hWnd, uMsg, wParam, lParam); // 将本程序不处理的消息传给DefWindowProc
86
87
7.注册类


1 BOOL RegisterWindowClass(Application* application) // 为本应用程序注册一个类
2 {
3 WNDCLASSEX windowClass; // 窗口类
4 ZeroMemory (&windowClass, sizeof(WNDCLASSEX)); // 清空内存
5 windowClass.cbSize = sizeof(WNDCLASSEX); // 窗口类的大小
6 windowClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // 在窗口移动,改变大小的时候重绘
7 windowClass.lpfnWndProc = (WNDPROC)(WindowProc); // 用WindowProc函数处理消息
8 windowClass.hInstance = application->hInstance; // 设置实例
9 windowClass.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE); // 类背景的画刷颜色
10 windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); // 载入鼠标指针
11 windowClass.lpszClassName = application->className; // 设置应用程序的类名
12 if (RegisterClassEx(&windowClass) == 0) // 注册类失败?
13 {
14 MessageBox(HWND_DESKTOP, "应用程序类注册失败!\nRegisterClassEx Failed!", "Error", MB_OK | MB_ICONEXCLAMATION);
15 return FALSE; // 返回False (注册失败)
16 }
17 return TRUE; // 返回True (注册成功)
18 }
8.WinMain


1 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
2 {
3 Application application; // 应用程序
4 GL_Window window; // 窗口
5 Keys keys; // 键盘按键
6 MSG msg; // Window消息
7 BOOL bRet; // 保存消息获得是否成功
8 // 给应用程序赋值
9 application.className = CLASSNAME; // 程序类名字
10 application.hInstance = hInstance; // 程序入口
11 // 窗口相关信息设置
12 ZeroMemory(&window, sizeof(GL_Window)); // 清零窗口变量的内存空间
13 window.keys = &keys; // 设置按键
14 window.init.application = &application; // 设置窗口程序
15 window.init.title = TITLE; // 设置标题
16 window.init.width = WIDTH; // 设置窗口宽度
17 window.init.height = HEIGHT; // 设置窗口高度
18 window.init.bitsPerPixel = BPP; // 设置每像素的位数
19 window.init.isFullScreen = FALSE; // 设置初始窗口是否全屏否(FALSE)
20 ZeroMemory(&keys, sizeof(Keys)); // 键盘缓冲清零
21 if (RegisterWindowClass(&application) == FALSE) // 注册类是否失败
22 {
23 MessageBox(HWND_DESKTOP, "窗口类注册失败!\nError Registering Window Class!", "Error", MB_OK | MB_ICONEXCLAMATION);
24 return -1; // 结束程序
25 }
26 if (CreateWindowGL(&window) == TRUE) // 创建窗口是否成功
27 {
28 if (Initialize(&window, &keys) == FALSE) // 初始化OpenGL绘制程序
29 {
30 PostMessage(window.hWnd, WM_QUIT, 0, 0); // 抛出消息WM_QUIT
31 }
32 }
33 else
34 {
35 return 0; // 退出程序
36 }
37 while ((bRet = GetMessage(&msg, NULL, 0, 0) != 0)) // 循环直到收到消息为WM_QUIT
38 {
39 if (bRet != -1)
40 {
41 Update(); // 更新处理消息事件
42 TranslateMessage(&msg); // 传递消息
43 DispatchMessage(&msg); // 分派消息
44 }
45 else
46 {
47 break; // 退出循环
48 }
49 }
50 // 退出消息循环,准备退出程序
51 Deinitialize(); // 做退出前的扫尾工作
52 DestroyWindowGL(&window); // 销毁窗口
53 UnregisterClass(application.className, application.hInstance); // 注销窗口类
54 return 0; // 退出程序
55 }
9.定义绘制场景的函数


1 BOOL Initialize(GL_Window* window, Keys* keys) // 用户绘制场景相关的函数在此处定义
2 {
3 // 设置全局变量
4 OGL_window = window;
5 OGL_keys = keys;
6 // 建立时钟
7 SetTimer(window->hWnd, TIMER1, 33, NULL); // 在此处初始化绘制场景
8 glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // 清屏为黑色
9 glClearDepth(1.0f); // 设置深度缓存
10 glDepthFunc(GL_LEQUAL); // 选择深度测试方式
11 glEnable(GL_DEPTH_TEST); // 开启深度测试
12 glShadeModel(GL_SMOOTH); // 阴暗处理采用平滑方式
13 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // 最精细的透视计算
14 return TRUE; // 初始化成功返回TRUE
15 }
10.绘制场景


1 void DrawSceneGL(void) // 绘制场景
2 {
3 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除颜色和深度缓存
4 glLoadIdentity(); // 重置当前矩阵
5 //在此处添加代码进行绘制
6 glFlush(); // 刷新GL命令队列
7 }
11.更新窗口


1 void Update(void) // 在此处更新对消息的动作
2 {
3 if (OGL_keys->keyDown[VK_ESCAPE] == TRUE) // 判断ESC键是否按下
4 {
5 PostMessage(OGL_window->hWnd, WM_QUIT, 0, 0); // 结束程序
6 }
7 if (OGL_keys->keyDown[VK_F1] == TRUE) // 判断F1是否按下
8 {
9 PostMessage(OGL_window->hWnd, WM_TOGGLEFULLSCREEN, 0, 0); // 在全屏/窗口模式间切换
10 }
11 }
12
12.退出清除
1 void Deinitialize(void) // 在此处做退出前扫尾工作
2 {
3 KillTimer(OGL_window->hWnd, TIMER1); // 释放时钟
4 }
2 {
3 KillTimer(OGL_window->hWnd, TIMER1); // 释放时钟
4 }