win32下的OpenGL绘图环境框架

时间:2024-03-12 19:35:25

Win32OpenGL入门

主要的步骤包括:添加opengl头文件,库文件,键盘鼠标响应,像素格式设置,opengl环境初始化,绘图变量设置,创建窗口,窗口大小改变时响应,绘制场景,源文件

1, 新建一个win32项目(注意,不是console程序),在添加过程中,创建一个空的项目,然后,在解决方案资源管理器的源文件树目录下,添加一个cpp文件,文件可以命名为mian.cpp

2, 添加绘图相关的头文件和库文件,在新建的main.cpp中,加入如下头文件:

 

#include <windows.h>                                         // Windows的头文件

#include <gl\gl.h>                                                 // Header File For The OpenGL32 Library

#include <gl\glu.h>                                               // Header File For The GLu32 Library

#include <gl\glaux.h>                      // Header File For The Glaux Library

#include <math.h>                                               // 引入数学函数库中的Sin

#include <stdio.h>                                                // Header File For Standard Input/Output

在项目,配置属性-连接器-输入的附加依赖项里,加入opengl32.libglu32.libglaux.lib

glut.lib,如图所示。

3,为创建绘图窗口,定义基本的相关变量,这些变量在opengl绘图中,都必须用到。

HGLRC           hRC=NULL;   // 窗口着色描述表句柄
HDC             hDC=NULL;    // OpenGL渲染描述表句柄
HWND            hWnd=NULL;                    // 保存我们的窗口句柄
HINSTANCE       hInstance;     // 保存程序的实例

第一行设置的变量是Rendering Context(着色描述表)。每一个OpenGL都被连接到一个着色描述表上。着色描述表将所有的OpenGL调用命令连接到Device Context(设备描述表)上。我将OpenGL的着色描述表定义为 hRC 。要让您的程序能够绘制窗口的话,还需要创建一个设备描述表,也就是第二行的内容。Windows的设备描述表被定义为 hDC 。DC将窗口连接到GDI(Graphics Device Interface图形设备接口)。而RC将OpenGL连接到DC。第三行的变量 hWnd 将保存由Windows给我们的窗口指派的句柄。最后,第四行为我们的程序创建了一个Instance(实例)。

 

4,为响应键盘,鼠标等操作,以及窗口活动情况,是否全屏等

bool            keys[256];                     // 保存键盘按键的数组
bool            active=TRUE;                // 窗口的活动标志,缺省为TRUE
bool            fullscreen=TRUE;         // 全屏标志缺省,缺省设定成全屏模式
GLfloat             rtri;     // 用于几何体旋转的角度

active 变量用来告知程序窗口是否处于最小化的状态。如果窗口已经最小化的话,我们可以做从暂停代码执行到退出程序的任何事情。我喜欢暂停程序。这样可以使得程序不用在后台保持运行。

fullscreen 变量的作用相当明显。如果我们的程序在全屏状态下运行, fullscreen 的值为TRUE,否则为FALSE。这个全局变量的设置十分重要,它让每个过程都知道程序是否运行在全屏状态下。

 

5我们需要先定义WndProc()。必须这么做的原因是CreateGLWindow()有对WndProc()的引用,但WndProc()CreateGLWindow()之后才出现。在C语言中,如果我们想要访问一个当前程序段之后的过程和程序段的话,必须在程序开始处先申明所要访问的程序段。所以下面的一行代码先行定义了WndProc(),使得CreateGLWindow()能够引用WndProc()

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);   // WndProc的定义

 

6,绘图视口的尺寸以及在窗口大小改变时的响应函数。不管窗口的大小是否已经改变(假定您没有使用全屏模式)。甚至您无法改变窗口的大小时(例如您在全屏模式下),它至少仍将运行一次--在程序开始时设置我们的透视图。OpenGL场景的尺寸将被设置成它显示时所在窗口的大小。

GLvoid ReSizeGLScene(GLsizei width, GLsizei height)   // 重置OpenGL窗口大小
{
                    if (height==0)                                                         // 防止被零除
                    {
                                         height=1;                                            // Height设为1
                    }
 
                    glViewport(0, 0, width, height);                           // 重置当前的视口glMatrixMode(GL_PROJECTION);                            // 选择投影矩阵
                    glLoadIdentity();                                                    // 重置投影矩阵
 
                    // 设置投影模式为透视投影
                    gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
 
                    glMatrixMode(GL_MODELVIEW);                     // 选择模型观察矩阵
                    glLoadIdentity();                                                    // 重置模型观察矩阵
}

透视图设置屏幕。意味着越远的东西看起来越小。这么做创建了一个现实外观的场景。此处透视按照基于窗口宽度和高度的45度视角来计算。0.1f,100.0f是我们在场景中所能绘制深度的起点和终点。 glMatrixMode(GL_PROJECTION)指明接下来的两行代码将影响projection matrix(投影矩阵)。投影矩阵负责为我们的场景增加透视。 glLoadIdentity()近似于重置。它将所选的矩阵状态恢复成其原始状态。调用 glLoadIdentity()之后我们为场景设置透视图。

glMatrixMode(GL_MODELVIEW)指明任何新的变换将会影响 modelview matrix(模型观察矩阵)。模型观察矩阵中存放了我们的物体讯息。最后我们重置模型观察矩阵。如果您还不能理解这些术语的含义,请别着急。在以后的教程里,我会向大家解释。只要知道如果您想获得一个精彩的透视场景的话,必须这么做。

 

7接下的代码段中,我们将对OpenGL进行所有的设置。我们将设置清除屏幕所用的颜色,打开深度缓存,启用smooth shading(阴影平滑),等等。这个例程直到OpenGL窗口创建之后才会被调用。此过程将有返回值。但我们此处的初始化没那么复杂,现在还用不着担心这个返回值

int InitGL(GLvoid)                             // 此处开始对OpenGL进行所有设置
{
glShadeModel(GL_SMOOTH);                 // 启用阴影平滑
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);           // 黑色背景
glClearDepth(1.0f);                                     // 设置深度缓存
glEnable(GL_DEPTH_TEST);                  // 启用深度测试
glDepthFunc(GL_LEQUAL); // 所作深度测试的类型
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);// 告诉系统对透视进行修正
return TRUE;                                                 // 初始化 OK
}

GL_SMOOTH阴影平滑通过多边形精细的混合色彩,并对外部光进行平滑。我将在另一个教程中更详细的解释阴影平滑。

深度缓存:将深度缓存设想为屏幕后面的层。深度缓存不断的对物体进入屏幕内部有多深进行跟踪。几乎所有在屏幕上显示3D场景OpenGL程序都使用深度缓存。它的排序决定那个物体先画。这样您就不会将一个圆形后面的正方形画到圆形上来。深度缓存是OpenGL十分重要的部分。

 

8,绘图部分。下一段包括了所有的绘图代码。任何您所想在屏幕上显示的东东都将在此段代码中出现。 这里,绘制了一个简单的金字塔,每个顶点采用不用的颜色

int DrawGLScene(GLvoid)                                  // 从这里开始进行所有的绘制
{
                    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);      // 清除屏幕和深度缓存
                    glLoadIdentity();                                                    // 重置当前的模型观察矩阵
                    glTranslatef(-1.5f,0.0f,-6.0f);                              // 左移 1.5 单位,并移入屏幕 6.0
                    glRotatef(rtri,0.0f,1.0f,0.0f);            // Y轴旋转金字塔
 
                    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, 1.0f);                                // 三角形的左下顶点 (前侧面)
                                         glColor3f(0.0f,0.0f,1.0f);                                      // 蓝色
                                         glVertex3f( 1.0f,-1.0f, 1.0f);                                 // 三角形的右下顶点 (前侧面)
                                         glColor3f(1.0f,0.0f,0.0f);                                      // 红色
                                         glVertex3f( 0.0f, 1.0f, 0.0f);                                 // 三角形的上顶点 (右侧面)
                                         glColor3f(0.0f,0.0f,1.0f);                                      // 蓝色
                                         glVertex3f( 1.0f,-1.0f, 1.0f);                                 // 三角形的左下顶点 (右侧面)
                                         glColor3f(0.0f,1.0f,0.0f);                 // 绿色
                                         glVertex3f( 1.0f,-1.0f, -1.0f);                               // 三角形的右下顶点 (右侧面)
                                         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, -1.0f);                               // 三角形的左下顶点 (后侧面)
                                         glColor3f(0.0f,0.0f,1.0f);                                                          // 蓝色
                                         glVertex3f(-1.0f,-1.0f, -1.0f);                               // 三角形的右下顶点 (后侧面)
                                         glColor3f(1.0f,0.0f,0.0f);                                                          // 红色
                                         glVertex3f( 0.0f, 1.0f, 0.0f);                                 // 三角形的上顶点 (左侧面)
                                         glColor3f(0.0f,0.0f,1.0f);                                                          // 蓝色
                                         glVertex3f(-1.0f,-1.0f,-1.0f);                                // 三角形的左下顶点 (左侧面)
                                         glColor3f(0.0f,1.0f,0.0f);                                                          // 绿色
                                         glVertex3f(-1.0f,-1.0f, 1.0f);                                // 三角形的右下顶点 (左侧面)
                    glEnd();      
                    rtri+=0.2f;                       // 增加三角形的旋转变量
                                                                                 
                    return TRUE;                                                                              
}

关于绘图:在opengl里面绘图,首先要了解的就是坐标原点,OpenGL的坐标原点默认情况下,在屏幕中心。OpenGL的坐标系统是右手坐标系统,默认情况下,屏幕朝上为y,朝左为x,朝外为z。

9,释放程序的绘图指针。在程序退出之前调用。KillGLWindow() 的作用是依次释放着色描述表,设备描述表和窗口句柄。

GLvoid KillGLWindow(GLvoid)      // 正常销毁窗口
{
                    if (fullscreen)                 // 我们处于全屏模式吗?
                    {
                                         ChangeDisplaySettings(NULL,0);                     // 是的话,切换回桌面,因为全屏模式直接关闭窗口,可能引发错误
                                         ShowCursor(TRUE);                                            // 显示鼠标指针
                    }
                    if (hRC)                                                                   // 我们拥有OpenGL渲染描述表吗?
                    {
                                         if (!wglMakeCurrent(NULL,NULL))                   // 我们能否释放DCRC描述表?
                                         {
                                                             MessageBox(NULL,"释放DCRC失败。","关闭错误",MB_OK | MB_ICONINFORMATION);
                                         }
                                         if (!wglDeleteContext(hRC))           // 我们能否删除RC?
                                         {
                                                             MessageBox(NULL,"释放RC失败。","关闭错误",MB_OK | MB_ICONINFORMATION);
                                         }
                                         hRC=NULL;                                                           // RC设为 NULL
                    }
                    if (hDC && !ReleaseDC(hWnd,hDC))                                   // 我们能否释放 DC?
                    {
                                         MessageBox(NULL,"释放DC失败。","关闭错误",MB_OK | MB_ICONINFORMATION);
                                         hDC=NULL;                                                           //  DC 设为 NULL
                    }
                    if (hWnd && !DestroyWindow(hWnd))                                   // 能否销毁窗口?
                    {
                                         MessageBox(NULL,"释放窗口句柄失败。","关闭错误",MB_OK | MB_ICONINFORMATION);
                                         hWnd=NULL;                                                        //  hWnd 设为 NULL
                    }
                    if (!UnregisterClass("OpenG",hInstance))                            // 能否注销类?
                    {
                                         MessageBox(NULL,"不能注销窗口类。","关闭错误",MB_OK | MB_ICONINFORMATION);
                                         hInstance=NULL;                                                 //  hInstance 设为 NULL
                    }
}

 

10,创建窗口。此过程返回布尔变量(TRUE FALSE)。他还带有5个参数:窗口的标题栏,窗口的宽度,窗口的高度,色彩位数(16/24/32),和全屏标志(TRUE --全屏模式, FALSE--窗口模式 )。返回的布尔值告诉我们窗口是否成功创建。

BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
//我们要求Windows为我们寻找相匹配的象素格式时,Windows寻找结束后将模式值保存在变量PixelFormat
                    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;// 移动时重画,并为窗口取得DCCS_OWNDC为窗口创建一个私有的DC。这意味着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      = "OpenG";                                                             // 设定类名字
 
                    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.dmPelsWidth = width;                           // 所选屏幕宽度
                                         dmScreenSettings.dmPelsHeight                     = height;                          // 所选屏幕高度
                                         dmScreenSettings.dmBitsPerPel = bits;          // 每象素所选的色彩深度                    dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
                                         // 尝试设置显示模式并返回结果。注: CDS_FULLSCREEN 移去了状态条。
                                         if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
                                         {
                                                             // 若模式失败,提供两个选项:退出或在窗口内运行。
                                                             if (MessageBox(NULL,"全屏模式在当前显卡上设置失败!\n使用窗口模式?","NeHe G",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
                                                             {
                                                                                 fullscreen=FALSE;// 选择窗口模式(Fullscreen=FALSE)
                                                             }
                                                             else
                                                             {
                                                                                 // 弹出一个对话框,告诉用户程序结束
                                                                                 MessageBox(NULL,"程序将被关闭","错误",MB_OK|MB_ICONSTOP);
                                                                                 return FALSE;               // 退出并返回 FALSE
                                                             }
                                         }
                    }
                    if (fullscreen)                                     // 仍处于全屏模式吗?
                    {
                                         dwExStyle=WS_EX_APPWINDOW;                 // 扩展窗体风格
                                         dwStyle=WS_POPUP;                                         // 窗体风格
                                         ShowCursor(FALSE);                      // 隐藏鼠标指针
                    }
                    else
                    {
                                         dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;// 扩展窗体风格
                                         dwStyle=WS_OVERLAPPEDWINDOW;                               // 窗体风格
                    }
                    AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); // 调整窗口大小
                    if (!(hWnd=CreateWindowEx(        dwExStyle,                                         // 扩展窗体风格
                                                                                                      "OpenGL",                     // 类名字
                                                                                                      title,                                                      // 窗口标题
                                                                                                      WS_CLIPSIBLINGS |   // 必须的窗体风格属性
                                                                                                      WS_CLIPCHILDREN |                     // 必须的窗体风格属性
                                                                                                      dwStyle,                         // 选择的窗体属性
                                                                                                     0, 0,                                                      // 窗口位置
                                                                                                      WindowRect.right-WindowRect.left,// 
                                                                                                      WindowRect.bottom-WindowRect.top,//高度
                                                                                                      NULL,                                                  // 无父窗口
                                                                                                      NULL,                                                  // 无菜单
                                                                                                      hInstance,                                           // 实例
                                                                                                      NULL)))    // 不向WM_CREATE传递任何东东
                    {
                                         KillGLWindow();                                                                        // 重置显示区
                                         MessageBox(NULL,"不能创建一个窗口设备描述表","错误",MB_OK|MB_ICONEXCLAMATION);
                                         return FALSE;                                                        // 返回 FALSE
                    }
                    static           PIXELFORMATDESCRIPTOR pfd=// /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,                                                          // Reserved
                                         0, 0, 0                                                  // 忽略层遮罩
                    };
                    if (!(hDC=GetDC(hWnd)))                                                        // 取得设备描述表了么?
                    {
                                         KillGLWindow();                                                                        // 重置显示区
                                         MessageBox(NULL,"不能创建一种相匹配的像素格式","错误",MB_OK|MB_ICONEXCLAMATION);
                                         return FALSE;                                                                            // 返回 FALSE
                    }
                    if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd)))// Windows 找到相应的象素格式了吗?
                    {
                                         KillGLWindow();                                                    // 重置显示区
                                         MessageBox(NULL,"不能设置像素格式","错误",MB_OK|MB_ICONEXCLAMATION);
                                         return FALSE;                                                        // 返回 FALSE
                    }
                    if(!SetPixelFormat(hDC,PixelFormat,&pfd))    // 能够设置象素格式么?
                    {
                                         KillGLWindow();                                                                        // 重置显示区
                                         MessageBox(NULL,"不能设置像素格式","错误",MB_OK|MB_ICONEXCLAMATION);
                                         return FALSE;                                                                            // 返回 FALSE
                    }
                    if (!(hRC=wglCreateContext(hDC)))                                      // 能否取得着色描述表?
                    {
                                         KillGLWindow();                                // 重置显示区
                                         MessageBox(NULL,"不能创建OpenGL渲染描述表","错误",MB_OK|MB_ICONEXCLAMATION);
                                         return FALSE;                                                                            // 返回 FALSE
                    }
                    if(!wglMakeCurrent(hDC,hRC))                         // 尝试激活着色描述表
                    {
                                         KillGLWindow();                                // 重置显示区
                                         MessageBox(NULL,"不能激活当前的OpenGL渲然描述表","错误",MB_OK|MB_ICONEXCLAMATION);
                                         return FALSE;                                    // 返回 FALSE
                    }
                    ShowWindow(hWnd,SW_SHOW);                                        // 显示窗口
                    SetForegroundWindow(hWnd);                         // 略略提高优先级
                    SetFocus(hWnd);                                                  // 设置键盘的焦点至此窗口
                    ReSizeGLScene(width, height);                         // 设置透视 GL 屏幕
                    if (!InitGL())                                                             // 初始化新建的GL窗口
                    {
                                         KillGLWindow();           // 重置显示区
                                         MessageBox(NULL,"Initialization Failed.","ERROR",MB_OK|MB_ICONEXCLAMATION);
                                         return FALSE;                                    // 返回 FALSE
                    }
                    return TRUE;                                                          // 成功
}

 

11,建立了窗口类之后,我们接下来完成窗口消息处理函数。

LRESULT CALLBACK WndProc( HWND        hWnd,                             // 窗口的句柄
                                                                                 UINT           uMsg,                              // 窗口的消息
                                                                                 WPARAM   wParam,                        // 附加的消息内容
                                                                                 LPARAM    lParam)                          // 附加的消息内容
{
                    switch (uMsg)                                                        // 检查Windows消息
                    {
                                         case WM_ACTIVATE:                      // 监视窗口激活消息
                                         {
                                                             if (!HIWORD(wParam))                    // 检查最小化状态
                                                             {
                                                                                 active=TRUE;                // 程序处于激活状态
                                                             }
                                                             else
                                                             {
                                                                                 active=FALSE;              // 程序不再激活
                                                             }
 
                                                             return 0;                          // 返回消息循环
                                         }
                                         case WM_SYSCOMMAND:                                // 系统中断命令
                                         {
                                                             switch (wParam)                               // 检查系统调用
                                                             {
                                                                                 case SC_SCREENSAVE:               // 屏保要运行?
                                                                                 case SC_MONITORPOWER:// 显示器要进入节电模式?
                                                                                 return 0;                          // 阻止发生
                                                             }
                                                             break;                                                                      // 退出
                                         }
                                         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;                                              // 返回
                                         }
                    }
                    //  DefWindowProc传递所有未处理的默认消息。
                    return DefWindowProc(hWnd,uMsg,wParam,lParam);
}

 

12,好了,南西北风都准备好了,现在只差东风。接下来我们来定义东风——程序的入口。调用窗口创建例程,处理窗口消息,并监视人机交互。

int  WINAPI WinMain(                    HINSTANCE                 hInstance,                      // 当前窗口实例
                                                             HINSTANCE                 hPrevInstance,              // 前一个窗口实例
                                                             LPSTR                            lpCmdLine,                                        // 命令行参数
                                                             int                                    nCmdShow)                  // 窗口显示状态
{
                    MSG           msg;                                                                        // Windowsx消息结构
                    BOOL         done=FALSE;                                                        // 用来退出循环的Bool 变量
                    // 提示用户选择运行模式
                    if (MessageBox(NULL,"你想在全屏模式下运行么?", "设置全屏模式",MB_YESNO|MB_ICONQUESTION)==IDNO)
                    {
                                         fullscreen=FALSE;                                               // FALSE为窗口模式
                    }
                    // 创建OpenGL窗口
/*CreateGLWindow函数的参数依次为标题、宽度、高度、色彩深度,以及全屏标志。就这么简单!我很欣赏这段代码的简洁。如果未能创建成功,函数返回FALSE。程序立即退出。*/
                    if (!CreateGLWindow("win32下的OpenGL程序学习入门 ",640,480,16,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();                                // 销毁当前的窗口
                                                                                 fullscreen=!fullscreen;                     // 切换 全屏 / 窗口 模式
                                                                                 // 重建 OpenGL 窗口
                                                                                 if (!CreateGLWindow("NeHe\'s OpenGL 程序框架",640,480,16,fullscreen))
                                                                                 {
                                                                                                      return 0;     // 如果窗口未能创建,程序退出
                                                                                 }
                                                             }
                                         }
                    }
                    // 关闭程序
                    KillGLWindow();                                                                                                                                                          // 销毁窗口
                    return (msg.wParam);                                                                                                                           // 退出程序
}

双缓冲绘图:使用双缓存可以实现无闪烁的动画,绘图领域一个经常用到的技术,特别是大规模场景的绘图。我们实际上在另一个看不见的"屏幕"上绘图。当我们交换缓存后,我们当前的屏幕被隐藏,现在看到的是刚才看不到的屏幕。这也是我们看不到场景绘制过程的原因。场景只是即时显示。

 

13,好了,运行程序

 

 

 

 

 

 

 

 

附:

常见错误列表:

1新建的工程如果出现error C2664: MessageBoxW: 不能将参数2 从const char [42]转换为LPCWSTR这种错误, 将项目->配置属性->常规->字符集改为支持多字节字符集

2. error LNK2019: 无法解析的外部符号_main,该符号在函数___tmainCRTStartup 中被引用,找到项目->配置属性->连接器->系统,将子系统修改为Windows (/SUBSYSTEM:WINDOWS)