日志 教程
当前位置: 教程  >  游戏开发  >  程序开发基础  >  正文

通过代码学习OpenGL

作者:郭同学 发表于 2011/12/16 13:31:17     评论(2)     阅读(4289)     
这是本人学习江超宇翻译的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 // Header File For Windows
  #include // Header File For Standard Input/Output
  #include

  #include // Header File For The OpenGL32 Library
  #include // Header File For The GLu32 Library
  #include // Header File For The GLaux Library

  #include
  #include

  ////全局变量//////////////////////////////////////////////////////////////////////////////////
  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(); //完成绘制

评论
显示
悄悄话
    伟浪
  • 伟浪的评论:
  •   好
    2012-05-22 15:59
    陈思婷
    2012-01-05 12:48
汇众教育官网 | 联系方式 | 版权声明 | 友情链接
Copyright 2008© 汇众益智(北京)教育科技有限公司. All Rights Reserved
京ICP备09092043号 京公网安备11010802009023号