这是本人学习江超宇翻译的NeHe OpenGL Tutorial 的结果,这个教程新手看很实用,只要你懂一点点OpenGL和WIN32就可以看的很明白. 创建了一个新的 Win32 程序(并非控制台程序) 之后, 链接 OpenGL 的库文件。 操作步骤是: Project-> Settings, 点击 LINK 标签, 在 “Object/Library Modules” 下面那一行的开始处(在kernel32.lib之前) 增添 OpenGL32.lib, GLu32.lib 和 GLaux.lib, 完成之后点击 OK 按钮. 然后把下面的代码贴上去就可以编译了. /////////////代码 #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers #include <windows.h> // Header File For Windows #include <stdio.h> // Header File For Standard Input/Output #include <stdlib.h> #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 <stdio.h> #include <stdlib.h> ////全局变量////////////////////////////////////////////////////////////////////////////////// HGLRC hRC=NULL; //设置一个渲染描述表,将OpenGL调用连接到设备描述表 HDC hDC=NULL; //设置一个设备描述表,将窗口连接到 GDI(Graphics Device Interface, 图形设备接口) HWND hWnd=NULL; //保存 Windows 分配给程序的窗口句柄 HINSTANCE hInstance; //保存应用程序实例句柄 bool keys[256]; //用于接收键盘输入的数组,支持同时按下多个键 bool active=TRUE; //程序是否被最小化,当最小化的时候挂起程序 bool fullscreen=TRUE; //是否运行于全屏幕模式,如果运行于窗口模式,它就为FALSE bool blend=TRUE; //是否打开混合 //浮点数是OpenGL编程中最基本的东西 GLfloat rtri=0.0f; //保存四凌锥旋转角度 GLfloat rquad=0.0f; //保存立方体旋转角度 GLfloat xrot=0.0f; //控制立方体在x轴上的旋转角度 GLfloat yrot=0.0f; //控制立方体在y轴上的旋转角度 GLfloat zrot=0.0f; //控制立方体在z轴上的旋转角度 GLfloat xspeed=0.2f; //控制立方体在x轴上的旋转速度 GLfloat yspeed=0.3f; //控制立方体在x轴上的旋转速度 GLfloat z=-5.0f; //控制立方体在屏幕中的深度 BOOL light=false; //记录光照是否打开 BOOL lp=false; //键盘上的L键是否被按下? BOOL fp=false; //键盘上的F键是否被按下? BOOL bp=false; //键盘上的B键是否被按下? //设置光照的数组(光照参数) GLfloat LightAmbient[]= { 0.5f, 0.5f, 0.5f, 1.0f }; //创建半亮度白色环境光.因为参数都是0.5f,所以是一个介于无光(黑)和全亮(白)的灯光. //没有环境光的话,漫射光照不到的地方将会非常黑暗 GLfloat LightDiffuse[]= { 1.0f, 1.0f, 1.0f, 1.0f }; //用于创建一个非常明亮,全亮度的漫射光.所有值都是1.0f,是可以达到的最亮的灯光 //设置光照位置 GLfloat LightPosition[]= { 0.0f, 0.0f, 2.0f, 1.0f };//前三个参数当然是用于指定其坐标x,y和z,最后一个参数是1.0f告知OpenGL指定的坐标就是光源的位置 //你可以想象显示器的玻璃平面就是z轴上0.0f的平面,把光源放在2.0f的位置上,所以如果你真的能看到这个光源的话,它应该在你的显示器玻璃屏上飘浮着 //当你到达0.0f时,图像看起来会很大,充斥了整个屏幕;而如果你向里到达一个极限值的时候,图像就看不见了 GLuint filter; //用于指定显示哪一个纹理.第一个纹理(纹理0)使用GL_NEAREST过滤(无平滑),第二个纹理(纹理1)使用GL_LINEAR 过滤,得到平滑的图像.第三个纹理(纹理2)使用 mipmap 纹理,创建非常漂亮的纹理外观.默认值0 GLuint texture[3]; //用于给3个不同的纹理创建存储空间 //mipmap纹理按照观察距离选择不同尺寸的纹理,实现多层次的细节.mipmap 纹理显示更好的外观,但占用更多的内存 ////函数声明////////////////////////////////////////////////////////////////////////////////////// LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); //声明窗口回调函数 WndProc() //////函数/////////////////////////////////////////////////////////////////////////////////////////// //纹理图像的宽度和高度必须是2的幂.宽度或高度最小应是64个像素,为了兼容性,最多应是256个像素 AUX_RGBImageRec *LoadBMP(char *Filename) //读入位图文件.果文件不存在将返回NULL值代表文件不能被读取 { FILE *File=NULL; //创建一个文件句柄
if (!Filename) //检测文件名是否合法 { return NULL; //If Not Return NULL }
File=fopen(Filename,"r"); //检测文件是否存在
if (File) //文件是否存在? { fclose(File); //关闭文件 return auxDIBImageLoad(Filename); //返回auxDIBImageLoad(Filename)读入的图像数据。 } //使用aux库读入位图,所以要保证aux库被包含了.Delphi和Visual C++都有aux库
return NULL; //If Load Failed Return NULL } ////////////////////////////////////////////////////////////////////////////////////////
int LoadGLTextures() //读取位图(通过调用上面的代码)并转换成纹理 {
int Status=FALSE; //记录是否成功地读取了位图并建造了纹理.初始化为FLASE(代表什么都没有读取和建造)
AUX_RGBImageRec *TextureImage[1]; //保存位图的图像记录,持有图像的宽度,高度和数据
memset(TextureImage,0,sizeof(void *)*1); //清空图像记录
//读入目录中的*.bmp Check For Errors, If Bitmap's Not Found Quit if (TextureImage[0]=LoadBMP("Data/Data.bmp")) { Status=TRUE; //Set The Status To TRUE
//建造纹理 glGenTextures(3, &texture[0]); //获得3个未使用的纹理名称 //第一纹理使用GL_NEAREST过滤,它几乎不做过滤,低运算量,但效果很差 //如果某个游戏的纹理看起来都是锯齿,可能使用的就是这种过滤.不过通常它可以在慢速电脑上运行良好 glBindTexture(GL_TEXTURE_2D, texture[0]);//绑定一个纹理名称到一个纹理对象 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); //NEW glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); //NEW //定义一个2D纹理 //0代表图像的详细级别,这通常为0 (与多纹理贴图有关) //3指定数据成分,因为这里的图像是红,绿,蓝组成的,所以为3 //TextureImage[0]->sizeX是纹理宽度,TextureImage[0]->sizeY是纹理高度 //0是纹理边界,通常为 0.GL_RGB告知OpenGL图像数据是红,绿,蓝顺序存储的 //GL_UNSIGNED_BYTE表示图像的数据类型是8位无符号整数.TextureImage[0]->data指定纹理数据 glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
//第二个纹理使用linear过滤建造纹理,不同的是这次储存在texture[1] glBindTexture(GL_TEXTURE_2D, texture[1]); //下面两行代码分别用于设置在放大(GL_TEXTURE_MAG_FILTER)和缩小(GL_TEXTURE_MIN_FILTER)纹理贴图的时候所使用的过滤 //通常将它们设置为GL_LINEAR,使纹理贴图在距离屏幕很远和很近的时候都能看起来很平滑 //使用GL_NEAREST可能会出现一些锯齿.也可以把它们结合起来使用,放大用一种,缩小用一种 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);//线性过滤 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);//线性过滤 //定义一个2D纹理 glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
//创建mipmap纹理 glBindTexture(GL_TEXTURE_2D, texture[2]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST); // New //要创建一个 2D 纹理,使用3种颜色(RGB),TextureImage[0]->sizeX是图像宽度,TextureImage[0]->sizeY是图像高度 //GL_RGB表示使用红绿蓝顺序,GL_UNSIGNED_BYTE表示图像的数据类型是字节,TextureImage[0]->data指定纹理数据 gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
}
//释放掉存储位图数据的所有内存 if (TextureImage[0]) //检查是否有位图保存在 { if (TextureImage[0]->data) //检查是否有数据 { free(TextureImage[0]->data); //有即释放掉 } free(TextureImage[0]); //释放掉图像结构 }
return Status; //如果一切顺利变量Status将为TRUE,否则为FALSE } ////////////////////////////////////////////////////////////////////////// //当窗口大小被调整时 (对于窗口模式) 调整 OpenGL 场景的大小,至少会在程序首次运行的时候被调用一次,用来设置透视.Resize And Initialize The GL Window GLvoid ReSizeGLScene(GLsizei width, GLsizei height) { if (height==0) // Prevent A Divide By Zero By { height=1; // Making Height Equal One } glViewport(0, 0, width, height); // Reset The Current Viewport glMatrixMode(GL_PROJECTION); //设置当前矩阵为投影矩阵,给场景增加透视 glLoadIdentity(); //重置当前指定的矩阵为单位矩阵 //设置透视图:基于窗口的宽与高,透视被设置为 45 度,视野指定透视深度:始点0.1f末点100.0f gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f); glMatrixMode(GL_MODELVIEW); //设置当前矩阵为模型视图矩阵,储存有关物体的信息 glLoadIdentity(); //重置当前指定的矩阵为单位矩阵 } ////////////////////////////////////////////////////////////////////////// //GL状态机初始化 All Setup For OpenGL Goes Here int InitGL(GLvoid) { if (!LoadGLTextures()) //读取位图并建造纹理 { return FALSE; //If Texture Didn't Load Return FALSE } glEnable(GL_TEXTURE_2D); //启用(激活)纹理映射
glShadeModel(GL_SMOOTH); //打开平滑阴影,平滑阴影会在多边形内精细地混合颜色和平滑光照 glClearColor(0.0f, 0.0f, 0.0f, 0.5f); //设置清除屏幕所用的颜色,产生黑色背景 //设置深度缓存(Depth Buffer),深度缓存会拣选出哪一个物体需要被先绘制,这样如果你在一个圆形的后面绘制一个正方形,那么正方形不会覆盖到圆形的上面来。 //深度缓存实际上存储了屏幕中每个像素点的深度值,这样具有较小深度值的像素(距离我们近的像素)会把具有较大深度值的像素(距离我们远的像素)覆盖掉。 glClearDepth(1.0f); // Depth Buffer Setup glEnable(GL_DEPTH_TEST); // Enables Depth Testing glDepthFunc(GL_LEQUAL); // The Type Of Depth Test To Do glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); //最好的透视修正,使透视图看起来好一些
//设置光照 glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient); //设置light1发出的环境光.使用LightAmbient存储的环境光参数(半强度环境光)
glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse); //设置light1发出的散射光.保存在LightDiffuse中的参数,全亮度白光
glLightfv(GL_LIGHT1, GL_POSITION,LightPosition);//设置light1光源的位置,参数保存在LightPosition 中
glEnable(GL_LIGHT1); //开启light1.ight1已经设置好了,在没有开启GL_LIGHTING 之前,仍然不会有光照
glBlendFunc(GL_SRC_ALPHA,GL_ONE); //设置混合类型
return TRUE; // Initialization Went OK } ////////////////////////////////////////////////////////////////////////// int DrawGLScene(GLvoid) //绘图代码 { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕到之前指定的颜色,清除深度缓存 //当你调用glLoadIdentity()的时候会回到屏幕(场景)的中心位置, //X轴是从左到右方向的,Y轴是从下往上方向的,Z轴是从屏幕里面射出屏幕方向的 //OpenGL屏幕(场景)的中心位置是在X轴,Y轴和Z轴上都为0.0f的位置 //这样,中心的左面是负值.右面是正值(X轴);下面是负值,上面是正值(Y轴);里面是负值,外面是正值(Z轴) glLoadIdentity(); //重置当前视图模型矩阵 glTranslatef(-2.0f,0.0f,-7.0f); //glTranslatef(x, y, z)的功能是沿X轴Y轴和Z轴做移动.在X轴上向左移动2.0个单位,Y轴上没有移动(0.0),Z轴上向屏幕里面移动7.0个单位
//glRotatef(Angle,Xvector,Yvector,Zvector)用于绕轴旋转物体,Angle是一个用于指定旋转角度的数字(通常存储于变量中) //Xvector,Yvector和Zvector这三个参数用于描述一条向量,以规定物体的旋转轴.如果使用(1,0,0)这样的值就描述了一条长度为1个单位的顺着x轴指向右方的向量 //X 轴 - 你正在使用一台台锯,木头穿过锯刀的中心.锯刀在x轴上飞速旋转,刀齿看起来是向上切或者向下切,这取决于锯刀的旋转方向.这与在OpenGL中以x轴旋转物体相类似 //Y 轴 - 想象一下,原野上挂起了龙卷风.龙卷风的中心正处于y轴上,它刮起的泥土还有碎片正绕着y轴(龙卷风的中心)旋转着,从左到右或从右到左.这与在OpenGL中以y轴旋转物体相类似 //Z 轴 - 你正看着一台旋转着的电风扇,电风扇的中心点面向着你.电风扇的扇片绕着z轴旋转,顺时针或逆时针方向.这与在OpenGL中以z轴旋转物体相类似 glRotatef(rtri,0.0f,1.0f,0.0f); // Rotate The Triangle On The Y axis
//如果想使物体绕某一轴旋转,物体的中心应该在(0,0,0) //平滑上色会混合三角形三个顶点的三种颜色,形成漂亮的混合色彩 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(); //完成绘制
glLoadIdentity(); //因为坐标系已经被旋转,所以重置当前视图模型矩阵 glTranslatef(2.5f,0.0f,-8.0f); //在X轴上向右移动2.5个单位,Y轴上没有移动(0.0),Z轴上向屏幕里面移动8.0个单位 glRotatef(rquad,1.0f,1.0f,1.0f); //同时围绕三个轴旋转
//以顺时针的顺序将绘制出四边形的背面,也可以说实际上看到的是它的背面. //物体沿逆时针顺序被绘制的话就会以正面面向,这里6个四边形都是按逆时针顺序绘制的:右上,左上,左下,右下 //拿一张白纸,画上坐标系标出每个面的每个顶点就可以直观地看出面及顶点的顺序 glBegin(GL_QUADS); //绘制立方体 glColor3f(0.0f,1.0f,0.0f); //设置颜色绿色 glVertex3f( 1.0f, 1.0f,-1.0f); //顶面右上点 glVertex3f(-1.0f, 1.0f,-1.0f); //顶面左上点 glVertex3f(-1.0f, 1.0f, 1.0f); //顶面左下点 glVertex3f( 1.0f, 1.0f, 1.0f); //顶面右下点
glColor3f(1.0f,0.5f,0.0f); //设置颜色为橘色 glVertex3f( 1.0f,-1.0f, 1.0f); //底面右上点 glVertex3f(-1.0f,-1.0f, 1.0f); //底面左上点 glVertex3f(-1.0f,-1.0f,-1.0f); //底面左下点 glVertex3f( 1.0f,-1.0f,-1.0f); //底面右下点 glColor3f(0.5f,0.5f,1.0f); //纯蓝色 glVertex3f( 1.0f, 1.0f, 1.0f); //正面右上点 glVertex3f(-1.0f, 1.0f, 1.0f); //正面左上点 glVertex3f(-1.0f,-1.0f, 1.0f); //正面左下点 glVertex3f( 1.0f,-1.0f, 1.0f); //正面右下点
glColor3f(1.0f,1.0f,0.0f); //黄色 glVertex3f( 1.0f,-1.0f,-1.0f); //背面右上点 glVertex3f(-1.0f,-1.0f,-1.0f); //背面左上点 glVertex3f(-1.0f, 1.0f,-1.0f); //背面左下点 glVertex3f( 1.0f, 1.0f,-1.0f); //背面右下点 glColor3f(0.0f,0.0f,1.0f); //兰色 glVertex3f(-1.0f, 1.0f, 1.0f); //左面右上点 glVertex3f(-1.0f, 1.0f,-1.0f); //左面左上点 glVertex3f(-1.0f,-1.0f,-1.0f); //左面左下点 glVertex3f(-1.0f,-1.0f, 1.0f); //左面右下点
glColor3f(1.0f,0.0f,1.0f); //桃红色 glVertex3f( 1.0f, 1.0f,-1.0f); //右面右上点 glVertex3f( 1.0f, 1.0f, 1.0f); //右面左上点 glVertex3f( 1.0f,-1.0f, 1.0f); //右面左下点 glVertex3f( 1.0f,-1.0f,-1.0f); //右面右下点 glEnd(); //完成绘制
glLoadIdentity(); //重置当前视图模型矩阵 glColor3f(1.0f,1.0f,1.0f); //白色 // glColor4f(1.0f,1.0f,1.0f,0.5f); //颜色设置为全亮度和50%的alpha(不透明度).这意味着当混合打开时物体会以50% 的透明度绘制 glTranslatef(0.0f,0.0f,z); //Z轴上向屏幕里面移动z个单位
//在x,y和z轴上旋转立方体,旋转的角度有分别由变量xrot,yrot和zrot控制 glRotatef(xrot,1.0f,0.0f,0.0f); // Rotate On The X Axis glRotatef(yrot,0.0f,1.0f,0.0f); // Rotate On The Y Axis glRotatef(zrot,0.0f,0.0f,1.0f); // Rotate On The Z Axis
//选择要使用的纹理.如果想变换纹理,就需要绑定另一个纹理.绑定纹理操作不能在glBegin()和glEnd()之间进行 //当按下F键时,变量filter会增1,当其大于2时,就会回到0,程序开始时filter会初始化为0.通过这种方式来选择使用哪一个纹理 glBindTexture(GL_TEXTURE_2D, texture[filter]); // Select A Texture Based On filter
//必须确保纹理与顶点右上角,左上角,右下角和左下角都能正确地一一对应,否则纹理有可能被倒置过来,也有可能歪斜了,或者根本没贴上 //为了与物体坐标相区分,纹理坐标一般使用字母s和t代替x和y.纹理坐标范围是0到1 //glTexCoord2f的第一个参数0.0f就是纹理的左边,0.5f就是纹理的中间,1.0f就是纹理的右边 //第二个参数是y坐标,0.0f就是纹理的底边,0.5f是中间,而1.0f就是纹理的顶边(最上边) glBegin(GL_QUADS); //法线(normal)是垂直于多边形表面的向量,当使用光照的时候必须要指定法线向量 //glNormal3f函数告诉OpenGL多边形的朝向(OpenGL通过法线向量确定在顶点处物体接收了多少光) //前面 glNormal3f( 0.0f, 0.0f, 1.0f); // Normal Pointing Towards Viewer glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); //纹理与四边形的左下点 glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); //右下点 glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); //右上点 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); //左上点 //背面 glNormal3f( 0.0f, 0.0f,-1.0f); // Normal Pointing Away From Viewer glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); //纹理与四边形的右下点 glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); //右上点 glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); //左上点 glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); //左下点 //顶面 glNormal3f( 0.0f, 1.0f, 0.0f); // Normal Pointing Up glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); //纹理与四边形的左上点 glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); //左下点 glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); //右下点 glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); //右上点 //底面 glNormal3f( 0.0f,-1.0f, 0.0f); // Normal Pointing Down glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); //纹理与四边形的右上点 glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f); //左上点 glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); //左下点 glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); //右下点 //右面 glNormal3f( 1.0f, 0.0f, 0.0f); // Normal Pointing Right glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); //纹理与四边形的右下点 glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); //右上点 glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); //左上点 glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); //左下点 //左面 glNormal3f(-1.0f, 0.0f, 0.0f); // Normal Pointing Left glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); //纹理与四边形的左下点 glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); //右下点 glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); //右上点 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); //左上点 glEnd();
//数值越高,物体旋转得就越快;数值越低,物体旋转得就就越慢.改变代码中的+号和-号物体以相反的方向旋转 rtri+=0.2f; //Increase The Rotation Variable For The Triangle rquad-=0.15f; //Decrease The Rotation Variable For The Quad xrot+=xspeed; // Add xspeed To xrot yrot+=yspeed; // Add yspeed To yrot zrot+=0.4f; // Z Axis Rotation return TRUE; // Everything Went OK } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// GLvoid KillGLWindow(GLvoid) //程序退出之前被调用.释放 RC,DC还有窗口句柄 { if (fullscreen) //检查我们是否处于全屏幕模式,是的话需要先切换回桌面 { ChangeDisplaySettings(NULL,0); //输入NULL和0强制Windows使用注册表中的值(默认的分辨率,颜色深度,刷新率等)来有效地恢复原来的桌面 ShowCursor(TRUE); //恢复了鼠标指针的可见 } if (hRC) //然后检查我们是否有一个Rendering Context(hRC) { if (!wglMakeCurrent(NULL,NULL)) //下面的代码将检查我们能否将hRC从hDC分开 { MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); } if (!wglDeleteContext(hRC)) //接下来释放RC { MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); } hRC=NULL; //设置hRC为 NULL }
if (hDC && !ReleaseDC(hWnd,hDC)) //检查是否拥有DC,有的话将其释放 { MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); hDC=NULL; //设置hDC为NULL }
if (hWnd && !DestroyWindow(hWnd)) //释放窗口句柄,销毁窗口 { MessageBox(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); hWnd=NULL; //设置hWnd为NULL } if (!UnregisterClass("OpenGL",hInstance)) //注销窗口类 { MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); hInstance=NULL; //设置hInstance为NULL } }
|
接上面,附件里带有可执行文件.欢迎大家跟我交流,QQ:31377458^_^
///////////////////////////////////////////////////////////////////////////////////////////////////
//函数接收5个参数:窗口标题,窗口宽度,窗口高度,色彩深度(16/24/32)还有全屏幕模式开关.TRUE代表使用全屏幕模式,FALSE代表使用窗口模式
//函数返回一个BOOL值来表明窗口创建是否成功。
BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
{
GLuint PixelFormat; //保存Windows给出匹配的像素点格式
WNDCLASS wc; //保存窗口类结构
DWORD dwExStyle; //用于存放扩展窗口风格的信息
DWORD dwStyle; //存放通常窗口风格的信息
//获取矩形左上角和右下角的值,因为窗口是具有边框的,所以一般窗口尺寸会大于绘图区尺寸
RECT WindowRect; // Grabs Rectangle Upper Left / Lower Right values
WindowRect.left=(long)0; // Set Left value To 0
WindowRect.right=(long)width; // Set Right value To Requested Width
WindowRect.top=(long)0; // Set Top value To 0
WindowRect.bottom=(long)height; // Set Bottom value To Requested Height
fullscreen=fullscreenflag; //把fullscreenflag的值赋给全局变量fullscreen
hInstance = GetModuleHandle(NULL); //获取应用程序实例句柄,然后定义窗口类
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; //窗口在调整尺寸时重绘,创建一个私有的窗口DC
wc.lpfnWndProc = (WNDPROC) WndProc; //窗口过程 WndProc Handles Messages
wc.cbClsExtra = 0; //不需要额外的窗口数据
wc.cbWndExtra = 0; //不需要额外的窗口数据
wc.hInstance = hInstance; // Set The Instance
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); // Load The Default Icon
wc.hCursor = LoadCursor(NULL, IDC_ARROW); // Load The Arrow Pointer
wc.hbrBackground = NULL; //之后在OpenGL中设置窗口背景色
wc.lpszMenuName = NULL; //不需要菜单
wc.lpszClassName = "OpenGL"; //设置类名
if (!RegisterClass(&wc)) //注册窗口类
{
MessageBox (NULL," Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; //退出
}
if (fullscreen) //切换到全屏幕模式
{
DEVMODE dmScreenSettings; // Device Mode
memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); //清除用于保存显示设置参数的空间
dmScreenSettings.dmSize=sizeof(dmScreenSettings); // Size Of The Devmode Structure
dmScreenSettings.dmPelsWidth = width; //设置需要的宽,在全屏幕模式下使用的窗口的宽和高应该和在窗口模式中使用的宽和高相同
dmScreenSettings.dmPelsHeight= height; //设置需要的高
dmScreenSettings.dmBitsPerPel = bits; //设置色彩深度
dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
//尝试设置与dmScreenSettings中相匹配的模式
//NOTE:CDS_FULLSCREEN使得在切换全屏模式的时候移除底部的开始菜单任务栏,而且在整个切换过程中不会影响到桌面上其它窗口的尺寸.
if (ChangeDisplaySettings (&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
{
//如果所请求的全屏幕模式不被支持,将弹出一个消息框.你可以选择使用窗口模式或退出
if (MessageBox(NULL,"The Requested Fullscreen Mode Is Not Supported By\nYour Video Card. Use Windowed Mode Instead?","NeHe GL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
{
fullscreen=FALSE; // Select Windowed Mode (Fullscreen=FALSE)
}
else
{
//弹出一个消息框告诉用户程序即将结束,窗口创建失败
MessageBox(NULL,"Program Will Now Close.","ERROR",MB_OK|MB_ICONSTOP);
return FALSE; //程序退出
}
}
}
if (fullscreen) //仍在全屏设置中
{
dwExStyle=WS_EX_APPWINDOW; //设置扩展风格为WS_EX_APPWINDOW,强制使窗口可见于最前面
dwStyle=WS_POPUP; //设置通常风格为WS_POPUP,没有边框的弹出式窗口,使得全屏模式窗口完美显示
ShowCursor(FALSE); //禁止鼠标箭头
}
else //转为窗口模式继续运行
{
dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; //增加WS_EX_WINDOWEDGE到扩展风格,使窗口具有 3D 外观
dwStyle=WS_OVERLAPPEDWINDOW; //用WS_OVERLAPPEDWINDOW代替WS_POPUP,具有标题栏,窗口菜单,最大化最小化按钮和可调整尺寸的窗口
}
AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); //根据窗口风格来调整窗口尺寸,对于全屏幕模式来说这条函数没有效果
//创建窗口
if (!(hWnd=CreateWindowEx( dwExStyle, //扩展窗口风格
"OpenGL", //类名(必须与之前注册的类名相同)
title, //窗口标题
WS_CLIPSIBLINGS | //WS_CLIPSIBLINGS和WS_CLIPCHILDREN 风格,这是保证OpenGL正常运行所必须的.
WS_CLIPCHILDREN | //能预防其它窗口在OpenGL窗口上进行绘图
dwStyle, //通常样式
0, 0, //窗口左上角坐标
WindowRect.right-WindowRect.left, //窗口宽与高
WindowRect.bottom-WindowRect.top,
NULL, //没有父窗口
NULL, //不需要菜单
hInstance, //应用程序句柄
NULL))) // Don't Pass Anything To WM_CREATE
{ //创建不成功
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Window Creation Error.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
//描述象素点格式
static PIXELFORMATDESCRIPTOR pfd= // pfd Tells Windows How We Want Things To Be
{
sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
1, // Version Number
PFD_DRAW_TO_WINDOW | // Format Must Support Window
PFD_SUPPORT_OPENGL | //格式支持 OpenGL
PFD_DOUBLEBUFFER, //支持双缓冲
PFD_TYPE_RGBA, //支持RGBA 模式
bits, //要求的色彩深度相匹配色深
0, 0, 0, 0, 0, 0, // Color Bits Ignored
0, // No Alpha Buffer
0, // Shift Bit Ignored
0, // No Accumulation Buffer(积累缓存)
0, 0, 0, 0, // Accumulation Bits Ignored
16, //设置一个16bit的Z-缓存(Depth Buffer)
0, // No Stencil Buffer(模版缓存)
0, // No Auxiliary Buffer
PFD_MAIN_PLANE, // Main Drawing Layer
0, // Reserved
0, 0, 0 // Layer Masks Ignored
};
if (!(hDC=GetDC(hWnd))) //尝试获取一个OpenGL Device Context
{
KillGLWindow(); //Reset The Display
MessageBox(NULL, "Can't Create A GL Device Context.", "ERROR", MB_OK|MB_ICONEXCLAMATION);
return FALSE; //程序退出
}
if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd))) //找出一个与上面所描述的相匹配的像素点格式
{
KillGLWindow(); // Reset The Display
MessageBox(NULL, "Can't Find A Suitable PixelFormat.", "ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; //程序退出
}
if(!SetPixelFormat(hDC,PixelFormat,&pfd)) //设置像素点格式
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; //Return FALSE程序退出
}
if (!(hRC=wglCreateContext(hDC))) //获取一个Rendering Context
{
KillGLWindow(); //Reset The Display
MessageBox(NULL, "Can't Create A GL Rendering Context.", "ERROR", MB_OK|MB_ICONEXCLAMATION);
return FALSE; //Return FALSE程序退出
}
if(!wglMakeCurrent(hDC,hRC)) //激活Rendering Context
{
KillGLWindow(); // Reset The Display
MessageBox(NULL, "Can't Activate The GL Rendering Context.", "ERROR", MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
ShowWindow(hWnd,SW_SHOW); //将窗口显示出来 Show The Window
SetForegroundWindow(hWnd); //并把它设到前台 Slightly Higher Priority
SetFocus(hWnd); //设置焦点到窗口上 Sets Keyboard Focus To The Window
ReSizeGLScene(width, height); //将窗口的宽与高作为参数来调用ReSizeGLScene以设置OpenGL透视
if (!InitGL()) // Initialize Our Newly Created GL Window
{
KillGLWindow(); // Reset The Display
MessageBox(NULL,"Initialization Failed.","ERROR",MB_OK|MB_ICONEXCLAMATION);
return FALSE; // Return FALSE
}
return TRUE; //返回TRUE告知WinMain()未发生错误
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK WndProc( HWND hWnd, // Handle For This Window
UINT uMsg, // Message For This Window
WPARAM wParam, // Additional Message Information
LPARAM lParam) // Additional Message Information
{
switch (uMsg) //uMsg用来保存待处理的消息的名字 Check For Windows Messages
{
case WM_ACTIVATE: // Watch For Window Activate Message
{
if (!HIWORD(wParam)) //检查窗口是否还处于活动状态
{
active=TRUE; //窗口处于活动状态
}
else
{
active=FALSE; //窗口处于非活动状态
}
return 0; // Return To The Message Loop
}
case WM_SYSCOMMAND: //(系统消息)
{
switch (wParam) // Check System Calls
{
case SC_SCREENSAVE: //代表屏幕保护程序将启动
case SC_MONITORPOWER: //代表显示器将进入节能模式
return 0; //返回0以阻止这种事情的发生
}
break; // Exit
}
case WM_CLOSE: //窗口被关闭
{
PostQuitMessage(0); //发出一条退出消息
return 0; //Jump Back
}
case WM_KEYDOWN: //某个键被按下,支持多个键同时按下的方法
{
keys[wParam] = TRUE; //数组keys[]中与那个键相对应的元素将被设置为TRUE
return 0; // Jump Back
}
case WM_KEYUP: //某个键抬起
{
keys[wParam] = FALSE; //数组keys[]中与那个键相对应的元素将被设置为FALSE
return 0; // Jump Back
}
case WM_SIZE: //调整窗口的尺寸消息
{
ReSizeGLScene(LOWORD(lParam),HIWORD(lParam)); //读取lParam 的低字和高字的值可以获取新的窗口尺寸的宽与高,以新的尺寸值作为参数调用ReSizeGLScene(),OpenGL场景做出相应的尺寸调整
return 0; // Jump Back
}
}
//其余的消息递给DefWindowProc,让 Windows 自行处理。
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain (HINSTANCE hInstance, // Instance
HINSTANCE hPrevInstance, // Previous Instance
LPSTR lpCmdLine, // Command Line Parameters
int nCmdShow) // Window Show State
{
MSG msg; //用于检测是否有消息需要处理 Windows Message Structure
BOOL done = FALSE; //当它为TRUE的时候程序就会退出 Bool Variable To Exit Loop
//询问是否要运行于全屏幕模式
if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
{
fullscreen=FALSE; //选择NO变量fullscreen从TRUE(默认值)变为FALSE.并且程序会运行于窗口模式
}
//创建OpenGL窗口
if (!CreateGLWindow("OpenGL",640,480,16,fullscreen))
{
return 0; // Quit If Window Was Not Created
}
while(!done) // Loop That Runs Until done=TRUE
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) //否有 Windows 消息待处理.使用PeekMessage() 检索消息可以避免程序等待。
{
if (msg.message==WM_QUIT) // Have We Received A Quit Message?
{
done=TRUE; // If So done=TRUE
}
else // If Not, 解释消息
{
TranslateMessage(&msg); // Translate The Message
DispatchMessage(&msg); // Dispatch The Message
}
}
else //没有消息的话绘制OpenGL场景
{
// Draw The Scene. Watch For ESC Key And Quit Messages From DrawGLScene()
if (active) //检查窗口是否处于活动状态
{
if (keys[VK_ESCAPE]) // Was ESC Pressed?
{
done=TRUE; // ESC Signalled A Quit
}
else // Not Time To Quit, Update Screen
{
//如果程序处于活动状态并且 ESC 按钮没有被按下
DrawGLScene(); //绘制场景
SwapBuffers(hDC); //交换缓冲区(使用双缓冲技术可以得到平滑无闪烁的动画)
if (keys['L'] && !lp) //L键是否被按下?
{ //lp初值为false.false意味着L键不处于压下状态,或早已释放.true意味着L键处于压下状态
lp=TRUE; //lp置为true.为了避免如果按住L键光照就会因为反复的开、关而闪烁
light=!light; //转换光照的开与关.变量light只能是true或false,如果它是true的话就会变成false,是false的话就会变成true
if (!light) //light为false
{
glDisable(GL_LIGHTING); //关闭光照
}
else //light为true
{
glEnable(GL_LIGHTING); //打开光照
}
}
if (!keys['L']) //检测是否已经释放L键
{
lp=FALSE; //是的话就置变量lp为false
}
if (keys['F'] && !fp) //检测F键是否被按下?
{ //同上的方式
fp=TRUE; // fp Becomes TRUE
filter+=1; //F键被按下一次变量filter就会增加1
if (filter>2) //当其大于2时
{
filter=0; //回到0
}
}
if (!keys['F']) //是否已经释放F键?
{
fp=FALSE; //是的话就置变量fp为false
}
if (keys[VK_PRIOR]) //检测是否按下"Page Up"?
{
z-=0.02f; //是的话变量z就会有减量.把木箱向屏幕里的方向移动
}
if (keys[VK_NEXT]) //检测是否按下"Page Down"键
{
z+=0.02f; //是的话变量z就会有增量,DrawGLScene中的glTranslatef(0.0f,0.0f,z)就会把木箱向外移动。
}
//检测方向键是否被按下
//按下左或右键,变量xspeed就会相应地减量或增量;按下上或下键,变量yspeed就会相应地减量或增量
//xspeed或yspeed的(绝对)值越高,木箱旋转的就会越快.所以按住某个方向键的时间越长,木箱在对应的方向就会旋转的越快
if (keys[VK_UP]) // Is Up Arrow Being Pressed?
{
xspeed-=0.01f; // If So, Decrease xspeed
}
if (keys[VK_DOWN]) // Is Down Arrow Being Pressed?
{
xspeed+=0.01f; // If So, Increase xspeed
}
if (keys[VK_RIGHT]) // Is Right Arrow Being Pressed?
{
yspeed+=0.01f; // If So, Increase yspeed
}
if (keys[VK_LEFT]) // Is Left Arrow Being Pressed?
{
yspeed-=0.01f; // If So, Decrease yspeed
}
//在绘制透明物体的时候,关闭深度缓存,因为希望在半透明物体后面的物体能被绘制,这并不是使用混合的正确方式,但是在情况简单的时候它会工作得很好
//正确的方法是在整个场景绘制完之后再绘制所有的透明(alpha < 1.0)多边形,而且要按照由远及近的深度顺序
//这是由于事实上按照不同的顺序混合两个(1和2)多边形会得到不同的结果
//假如多边形1距离我们最近,那么正确的方法是先绘制多边形2,然后是多边形1
//如果你看着它,那么真实情况就是它们后面的光首先经过多边形2,然后经过多边形1,最后到达你的眼睛
//所以,需要按照深度将透明的多边形进行排序,开着深度缓存并在整个场景绘制完毕之后再绘制它们,否则将得到不正确的结果
if (keys['B'] && !bp) //检查键盘上的B键是否被按下?
{
bp=TRUE; // If So, bp Becomes TRUE
blend = !blend; // Toggle blend TRUE / FALSE
if(blend) //混合是关?
{
glEnable(GL_BLEND); //打开混合
glDisable(GL_DEPTH_TEST); //关闭深度测试
}
else //混合是开
{
glDisable(GL_BLEND); //关闭混合
glEnable(GL_DEPTH_TEST); //打开深度测试
}
}
if (!keys['B']) //B被释放?
{
bp=FALSE; //bp置为FALSE
}
}
}
//使用 F1 切换全屏幕模式和窗口模式。
if (keys[VK_F1]) // Is F1 Being Pressed?
{
keys[VK_F1]=FALSE; // If So Make Key FALSE
KillGLWindow(); // Kill Our Current Window
fullscreen=!fullscreen; // Toggle Fullscreen / Windowed Mode
// Recreate Our OpenGL Window
if (!CreateGLWindow("OpneGL",640,480,16,fullscreen))
{
return 0; // Quit If Window Was Not Created
}
}
}
}
//程序退出,完全地摧毁窗口,所有东西都释放掉
KillGLWindow(); // Kill The Window
return (msg.wParam); // Exit The Program
}