#include <windows.h> #include <gl/gl.h> #include <gl/glu.h> #include <gl/glaux.h> HGLRC hRC = NULL; HDC hDC = NULL; HWND hWnd = NULL; HINSTANCE hInstance; bool keys[256]; bool active = TRUE; bool fullscreen = TRUE; LRESULT CALLBACK WndProc( HWND hWnd, // handle to window UINT uMsg, // message identifier WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ); // 下面的代码的作用是重新设置OpenGL场景的大小 // OpenGL场景的尺寸将被设置成它显示时所在窗口的大小 GLvoid ReSizeGLScene(GLsizei width, GLsizei height) { if (height == 0) { // 防止被零除 height = 1; } glViewport(0, 0, width, height); // 重置当前的视口(Viewport) // 为透视图设置屏幕。意味着越远的东西看起来越小 // 这么做创建了一个现实外观的场景。此处透视按照基于窗口宽度和高度的45度视角来计算 // 0.1f,100.0f是我们在场景中所能绘制深度的起点和终点 glMatrixMode(GL_PROJECTION); // 选择投影矩阵 glLoadIdentity(); // 重置投影矩阵 // 计算窗口的外观比例 gluPerspective(45.0f, (GLfloat)width/(GLfloat)height, 0.1f, 100.0f); glMatrixMode(GL_MODELVIEW); // 选择模型观察矩阵 glLoadIdentity(); // 重置模型观察矩阵 } // 对OpenGL进行所有的设置 // 我们将设置清除屏幕所用的颜色,打开深度缓存,启用smooth shading(阴影平滑),等等 int InitGL(GLvoid) { glShadeModel(GL_SMOOTH); // 启用阴影平滑 // 色彩值的范围从0.0f到1.0f。0.0f代表最黑的情况,1.0f就是最亮的情况 // 采用RGBA模式 glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // 黑色背景 // 将深度缓存设想为屏幕后面的层 // 深度缓存不断的对物体进入屏幕内部有多深进行跟踪 // 几乎所有在屏幕上显示3D场景OpenGL程序都使用深度缓存 // 它的排序决定那个物体先画。这样您就不会将一个圆形后面的正方形画到圆形上来 glClearDepth(1.0f); // 设置深度缓存 glEnable(GL_DEPTH_TEST); // 启用深度测试 glDepthFunc(GL_LEQUAL); // 所作深度测试的类型 // 告诉OpenGL我们希望进行最好的透视修正 // 这会十分轻微的影响性能。但使得透视图看起来好一点。 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // 真正精细的透视修正 return TRUE; } GLfloat rtri; GLfloat rquad; // 任何您所想在屏幕上显示的东东都将在此段代码中出现 int DrawGLScene(GLvoid) { // 从这里开始进行所有的绘制 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除屏幕和深度缓存 glLoadIdentity(); // 重置当前的模型观察矩阵 // // glTranslatef(-1.5f, 0.0f, -6.0f); glRotatef(rtri, 0.0f, 1.0f, 0.0f); glBegin(GL_TRIANGLES); glColor3f(1.0f, 0.0f, 0.0f); glVertex3f(0.0f, 1.0f, 0.0f); glColor3f(0.0f, 1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 0.0f); glColor3f(0.0f, 0.0f, 1.0f); glVertex3f(1.0f, -1.0f, 0.0f); glEnd(); glLoadIdentity(); glTranslatef(1.5f, 0.0f, -6.0f); glRotatef(rquad, 1.0f, 0.0f, 0.0f); glBegin(GL_QUADS); glVertex3f(-1.0f, 1.0f, 0.0f); glVertex3f(1.0f, 1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 0.0f); glEnd(); rtri += 10.0f; rquad -= 10.0f; // // return TRUE; } GLvoid KillGLWindow(GLvoid) { // 检查我们是否处于全屏模式。如果是,我们要切换回桌面 if (fullscreen) { // 将NULL作为第一个参数,0作为第二个参数传递 // 强制Windows使用当前存放在注册表中的值(缺省的分辨率、色彩深度、刷新频率,等等) // 来有效的恢复我们的原始桌面 ChangeDisplaySettings(NULL, 0); ShowCursor(TRUE); } if (hRC) { // 查看我们能否释放它(将 hRC从hDC分开) if (!wglMakeCurrent(NULL, NULL)) { MessageBox(NULL, "Release Of DC And RC Failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); } // 试着删除着色描述表 if (!wglDeleteContext(hRC)) { MessageBox(NULL, "Release Rendering Context Failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); } hRC = NULL; } // 查看是否存在设备描述表,如果有尝试释放它 if (hDC && !ReleaseDC(hWnd, hDC)) { MessageBox(NULL, "Release Device Context Failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); hDC = NULL; } // 查看是否存在窗口句柄,我们调用 DestroyWindow( hWnd )来尝试销毁窗口 if (hWnd && !DestroyWindow(hWnd)) { MessageBox(NULL, "Release hWnd Failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); hWnd = NULL; } // 注销我们的窗口类 if (!UnregisterClass("OpenGL", hInstance)) { MessageBox(NULL, "Unregister Class Failed.", "SHUTDOWN ERROR", MB_OK | MB_ICONINFORMATION); hInstance = NULL; } } BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag) { GLuint PixelFormat; // 保存相匹配的象素格式的模式值的结果 WNDCLASS wc; // 窗口类结构 DWORD dwExStyle; // 扩展窗口风格 DWORD dwStyle; // 窗口风格 RECT WindowRect; // 取得矩形的左上角和右下角的坐标值 WindowRect.left = (long)0; // 将Left设为0 WindowRect.right = (long)width; // 将Right设为要求的宽度 WindowRect.top = (long)0; // 将Top设为0 WindowRect.bottom = (long)height; // 将Bottom设为要求的高度 fullscreen = fullscreenflag; // 设置全局全屏标志 hInstance = GetModuleHandle(NULL); // 取得我们窗口的实例 wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // 移动时重画,并为窗口取得DC wc.lpfnWndProc = (WNDPROC)WndProc; // WndProc处理消息 wc.cbClsExtra = 0; // 无额外窗口数据 wc.cbWndExtra = 0; // 无额外窗口数据 wc.hInstance = hInstance; // 设置实例 wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); // 装入缺省图标 wc.hCursor = LoadCursor(NULL, IDC_ARROW); // 装入鼠标指针 wc.hbrBackground = NULL; // GL不需要背景 wc.lpszMenuName = NULL; // 不需要菜单 wc.lpszClassName = "OpenGL"; // 设定类名字 // 尝试注册窗口类 if (!RegisterClass(&wc)) { MessageBox(NULL, "Register Class Error.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return FALSE; } // 如果应该是全屏模式的话,我们将尝试设置全屏模式 if (fullscreen) { // 全屏模式下所用的宽度和高度等同于窗口模式下的宽度和高度 // 最最重要的是要在创建窗口之前设置全屏模式 // 分配了用于存储视频设置的空间 // 设定了屏幕的宽,高,色彩深度 DEVMODE dmScreenSettings; // 设备模式 memset(&dmScreenSettings, 0, sizeof(dmScreenSettings)); // 确保内存分配 dmScreenSettings.dmSize = sizeof(dmScreenSettings); // Devmode 结构的大小 dmScreenSettings.dmPelsWidth = width; // 所选屏幕宽度 dmScreenSettings.dmPelsHeight = height; // 所选屏幕高度 dmScreenSettings.dmBitsPerPel = bits; // 每象素所选的色彩深度 dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; // 尝试切换成与dmScreenSettings所匹配模式 if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL) { // 若模式失败,提供两个选项:退出或在窗口内运行 if (MessageBox(NULL, "The Requested Fullscreen Mode Is Not Supported By/n" "Your Video Card. Use Windowed Instead?", "EXCEPTION", MB_YESNO | MB_ICONEXCLAMATION) == IDYES) { fullscreen = false; } else { MessageBox(NULL, "Program Will Now Close.", "ERROR", MB_OK | MB_ICONSTOP); return FALSE; } } } // 由于全屏模式可能失败,用户可能决定在窗口下运行 // 我们需要在设置屏幕/窗口之前,再次检查fullscreen的值是TRUE或FALSE if (fullscreen) { // 设置扩展窗体风格为WS_EX_APPWINDOW,这将强制我们的窗体可见时处于最前面 dwExStyle = WS_EX_APPWINDOW; // 窗体的风格设为WS_POPUP。这个类型的窗体没有边框,使我们的全屏模式得以完美显示 dwStyle = WS_POPUP; // 在全屏模式下禁用鼠标指针通常是个好主意 ShowCursor(FALSE); } else { // 在扩展窗体风格中增加了 WS_EX_WINDOWEDGE,增强窗体的3D感观 dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; // 窗体风格改用 WS_OVERLAPPEDWINDOW // 创建一个带标题栏、可变大小的边框、菜单和最大化/最小化按钮的窗体 dwStyle = WS_OVERLAPPEDWINDOW; } // 通常边框会占用窗口的一部分 // 使用AdjustWindowRectEx 后,我们的OpenGL场景就不会被边框盖住 // 实际上窗口变得更大以便绘制边框。全屏模式下,此命令无效 AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); // 调整窗口达到真正要求的大小 // 检查看窗口是否正常创建 // 如果成功, hWnd保存窗口的句柄。如果失败,弹出消息窗口,并退出程序 if (!(hWnd = CreateWindowEx(dwExStyle, // 扩展窗体风格 "OpenGL", // 类名字 title, // 窗口标题 // 要让OpenGL正常运行,这两个属性是必须的 // 他们阻止别的窗体在我们的窗体内/上绘图 WS_CLIPSIBLINGS | WS_CLIPCHILDREN | // 必须的窗体风格属性 dwStyle, // 选择的窗体属性 0, 0, // 窗口位置 WindowRect.right - WindowRect.left, // 计算调整好的窗口宽度 WindowRect.bottom - WindowRect.top, // 计算调整好的窗口高度 NULL, // 无父窗口 NULL, // 无菜单 hInstance, // 实例 NULL))) // 不向WM_CREATE传递任何东东 { KillGLWindow(); MessageBox(NULL, "Window Creation Error.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return FALSE; } //下面的代码描述象素格式 // 我们选择了通过RGBA(红、绿、蓝、alpha通道)支持OpenGL和双缓存的格式 static PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), // 上诉格式描述符的大小 1, // 版本号 PFD_DRAW_TO_WINDOW | // 格式必须支持窗口 PFD_SUPPORT_OPENGL | // 格式必须支持OpenGL PFD_DOUBLEBUFFER, // 必须支持双缓冲 PFD_TYPE_RGBA, // 申请 RGBA 格式 bits, // 选定色彩深度 0, 0, 0, 0, 0, 0, // 忽略的色彩位 0, // 无Alpha缓存 0, // 忽略Shift Bit 0, // 无聚集缓存 0, 0, 0, 0, // 忽略聚集位 16, // 16位 Z-缓存 (深度缓存) 0, // 无模板缓存 0, // 无辅助缓存 PFD_MAIN_PLANE, // 主绘图层 0, // 保留 0, 0, 0 // 忽略层遮罩 }; // 尝试取得OpenGL设备描述表。若无法取得DC,弹出错误消息程序退出 if (!(hDC = GetDC(hWnd))) { KillGLWindow(); MessageBox(NULL, "Can't Create A GL Device Context.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return FALSE; } // 为OpenGL窗口取得设备描述表后,我们尝试找到对应与此前我们选定的象素格式的象素格式 // 如果Windows不能找到的话,弹出错误消息,并退出程序 if (!(PixelFormat = ChoosePixelFormat(hDC, &pfd))) { KillGLWindow(); MessageBox(NULL, "Can't Find A Suitable PixelFormat.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return FALSE; } // Windows 找到相应的象素格式后,尝试设置象素格式 // 如果无法设置,弹出错误消息,并退出程序 if (!SetPixelFormat(hDC, PixelFormat, &pfd)) { KillGLWindow(); MessageBox(NULL, "Can't Set Pixel Format.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return FALSE; } // 正常设置象素格式后,尝试取得着色描述表 // 如果不能取得着色描述表的话,弹出错误消息,并退出程序 if (!(hRC = wglCreateContext(hDC))) { KillGLWindow(); MessageBox(NULL, "Can't Create A GL Rendering Context", "ERROR", MB_OK | MB_ICONEXCLAMATION); return FALSE; } // 如果到现在仍未出现错误的话,我们已经设法取得了设备描述表和着色描述表 // 接着要做的是激活着色描述表。如果无法激活,弹出错误消息,并退出程序 if (!wglMakeCurrent(hDC, hRC)) { KillGLWindow(); MessageBox(NULL, "Can't Active GL Rendering Context.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return FALSE; } ShowWindow(hWnd, SW_SHOW); // 显示窗口 SetForegroundWindow(hWnd); // 略略提高优先级 SetFocus(hWnd); // 设置键盘的焦点至此窗口 // 调用ReSizeGLScene 将屏幕的宽度和高度设置给透视OpenGL屏幕 ReSizeGLScene(width, height); // 设置透视 GL 屏幕 // 跳转至 InitGL(),这里可以设置光照、纹理、等等任何需要设置的东东 if (!InitGL()) { KillGLWindow(); MessageBox(NULL, "Initilization Failed.", "ERROR", MB_OK | MB_ICONEXCLAMATION); return FALSE; } return TRUE; } // 下面的代码处理所有的窗口消息 // 当我们注册好窗口类之后,程序跳转到这部分代码处理窗口消息。 LRESULT CALLBACK WndProc( HWND hwnd, // handle to window UINT uMsg, // message identifier WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ) { switch (uMsg) { // 检查Windows消息 case WM_ACTIVATE: // 监视窗口激活消息 { if (!HIWORD(wParam)) { // 检查最小化状态 // 如果窗口已被激活,变量active的值为TRUE active = TRUE; // 程序处于激活状态 } else { // 如果窗口已被最小化,将变量active设为FALSE active = FALSE; // 程序不再激活 } return 0; // 返回消息循环 } case WM_SYSCOMMAND: // 中断系统命令Intercept System Commands { switch (wParam) { // 检查系统调用Check System Calls case SC_SCREENSAVE: // 屏保要运行? case SC_MONITORPOWER: // 显示器要进入节电模式? return 0; // 阻止发生 返回消息循环 } break; } case WM_CLOSE: { PostQuitMessage(0); return 0; } case WM_KEYDOWN: { keys[wParam] = TRUE; // 如果有键按下,设为TRUE return 0; } case WM_KEYUP: { keys[wParam] = FALSE; // 如果有键放开,设为FALSE } case WM_SIZE: // 调整OpenGL窗口大小 { ReSizeGLScene(LOWORD(lParam), HIWORD(lParam)); // LoWord=Width,HiWord=Height return 0; } } return DefWindowProc(hwnd, uMsg, wParam, lParam); } int WINAPI WinMain( HINSTANCE hInstance, // handle to current instance HINSTANCE hPrevInstance, // handle to previous instance LPSTR lpCmdLine, // command line int nCmdShow // show state ) { MSG msg; BOOL done = FALSE; // 用来退出循环的Bool 变量 // 提示用户选择运行模式 if (MessageBox(NULL, "Would You Like To Run In Fullscreen Mode?", "Start Fullscreen?", MB_YESNO | MB_ICONQUESTION) == IDNO) { fullscreen = FALSE; } // 创建OpenGL窗口 if (!CreateGLWindow("OpenGL DrawSth_1", 640, 480, 16, fullscreen)) { return 0; // 失败退出 } while (!done) { if (PeekMessage(&msg, hWnd, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) { done = TRUE; } else { TranslateMessage(&msg); DispatchMessage(&msg); } } else { // 如果没有消息,绘制我们的OpenGL场景 // 代码的第一行查看窗口是否激活 // 如果按下ESC键,done变量被设为TRUE,程序将会退出。 if (active) { if (keys[VK_ESCAPE]) { done = TRUE; } else { // 绘制场景并交换缓存(使用双缓存可以实现无闪烁的动画)。 DrawGLScene(); SwapBuffers(hDC); } } // 允许用户按下F1键在全屏模式和窗口模式间切换 if (keys[VK_F1]) { keys[VK_F1] = FALSE; KillGLWindow(); fullscreen = !fullscreen; if (!CreateGLWindow("OpenGL DrawSth_1", 640, 480, 16, fullscreen)) { return 0; } } } } KillGLWindow(); return (msg.wParam); } |