新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论高级C/C++编程、代码重构(Refactoring)、极限编程(XP)、泛型编程等话题
    [返回] 计算机科学论坛计算机技术与应用『 C/C++编程思想 』 → [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 05-lesson 06 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 33649 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 05-lesson 06 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     一分之千 帅哥哟,离线,有人找我吗?射手座1984-11-30
      
      
      威望:1
      等级:研一(随老板参加了WWW大会还和Tim Berners-Lee合了影^_^)
      文章:632
      积分:4379
      门派:XML.ORG.CN
      注册:2006/12/31

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给一分之千发送一个短消息 把一分之千加入好友 查看一分之千的个人资料 搜索一分之千在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看一分之千的博客楼主
    发贴心情 [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 05-lesson 06

    第五课第六课源码


    第五课  中文

    按此在新窗口浏览图片3D空间:

    我们使用多边形和四边形创建3D物体,在这一课里,我们把三角形变为立体的金子塔形状,把四边形变为立方体。

      
       
       
    在上节课的内容上作些扩展,我们现在开始生成真正的3D对象,而不是象前两节课中那样3D世界中的2D对象。我们给三角形增加一个左侧面,一个右侧面,一个后侧面来生成一个金字塔(四棱锥)。给正方形增加左、右、上、下及背面生成一个立方体。
    我们混合金字塔上的颜色,创建一个平滑着色的对象。给立方体的每一面则来个不同的颜色。
      
       

    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);     // 开始绘制金字塔的各个面

       
    有些人可能早已在上节课中的代码上尝试自行创建3D对象了。但经常有人来信问我:"我的对象怎么不会绕着其自身的轴旋转?看起来总是在满屏乱转。"要让您的对象绕自身的轴旋转,您必须让对象的中心坐标总是(0.0f,0,0f,0,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);   // 三角形的右下顶点 (前侧面)

       
    现在绘制右侧面。注意其底边上的两个顶点的X坐标位于中心右侧的一个单位处。顶点则位于Y轴上的一单位处,且Z坐标正好处于底边的两顶点的Z坐标中心。右侧面从上顶点开始向外侧倾斜至底边上。
    这次的左下顶点用蓝色绘制,以保持与前侧面的右下顶点的一致。蓝色将从这个角向金字塔的前侧面和右侧面扩展并与其他颜色混合。
    还应注意到后面的三个侧面和前侧面处于同一个glBegin(GL_TRIANGLES) 和 glEnd()语句中间。因为我们是通过三角形来构造这个金字塔的。OpenGL知道每三个点构成一个三角形。当它画完一个三角形之后,如果还有余下的点出现,它就以为新的三角形要开始绘制了。OpenGL在这里并不会将四点画成一个四边形,而是假定新的三角形开始了。所以千万不要无意中增加任何多余的点。  
       

      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);   // 三角形的右下顶点 (后侧面)

       
    最后画左侧面。又要切换颜色。左下顶点是蓝色,与后侧面的右下顶点相同。右下顶点是蓝色,与前侧面的左下顶点相同。
    到这里金字塔就画完了。因为金字塔只绕着Y轴旋转,我们永远都看不见底面,因而没有必要添加底面。如果您觉得有经验了,尝试增加底面(正方形),并将金字塔绕X轴旋转来看看您是否作对了。确保底面四个顶点的颜色与侧面的颜色相匹配。  
       

      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();      // 金字塔绘制结束

       
    接下来开始画立方体。他由六个四边形组成。所有的四边形都以逆时针次序绘制。就是说先画右上角,然后左上角、左下角、最后右下角。您也许认为画立方体的背面的时候这个次序看起来好像顺时针,但别忘了我们从立方体的背后看背面的时候,与您现在所想的正好相反。(译者注:您是从立方体的外面来观察立方体的)。
    注意到这次我们将立方体移地更远离屏幕了。因为立方体的大小要比金字塔大,同样移入6个单位时,立方体看起来要大的多。这是透视的缘故。越远的对象看起来越小 :) 。  
       

     glLoadIdentity();
     glTranslatef(1.5f,0.0f,-7.0f);    // 先右移再移入屏幕

     glRotatef(rquad,1.0f,1.0f,1.0f);   // 在XYZ轴上旋转立方体

     glBegin(GL_QUADS);     // 开始绘制立方体

       
    先画立方体的顶面。从中心上移一单位,注意Y坐标始终为一单位,表示这个四边形与Z轴平行。先画右上顶点,向右一单位,再屏幕向里一单位。然后左上顶点,向左一单位,再屏幕向里一单位。然后是靠近观察者的左下和右下顶点。就是屏幕往外一单位。  
       

      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);   // 四边形的右下顶点 (顶面)

       
    底面的画法和顶面十分类似。只是Y坐标变成了-1。如果我们从立方体的下面来看立方体的话,您会注意到右上角离观察者最近,因此我们先画离观察者最近的顶点。然后是左上顶点最后才是屏幕里面的左下和右下顶点。
    如果您真的不在乎绘制多边形的次序(顺时针或者逆时针)的话,您可以直接拷贝顶面的代码,将Y坐标从1改成 -1,也能够工作。但一旦您进入象纹理映射这样的领域时,忽略绘制次序会导致十分怪异的结果。
      
       

      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);   // 四边形的右下顶点(底面)

       
    接着画立方体的前面。保持Z坐标为一单位,前面正对着我们。  
       

      glColor3f(1.0f,0.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);   // 四边形的右下顶点(前面)

       
    立方体后面的绘制方法与前面类似。只是位于屏幕的里面。注意Z坐标现在保持 -1 不变。  
       

      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);   // 四边形的右下顶点(后面)

       
    还剩两个面就完成了。您会注意到总有一个坐标保持不变。这一次换成了X坐标。因为我们在画左侧面。  
       

      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);   // 四边形的右下顶点(左面)

       
    立方体的最后一个面了。X坐标保持为一单位。逆时针绘制。您愿意的话,留着这个面不画也可以,这样就是一个盒子:)
    或者您要是有兴趣可以改变立方体所有顶点的色彩值,象金字塔那样混合颜色。您会看见一个非常漂亮的彩色立方体,各种颜色在它的各个表面流淌。
      
       

      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();      // 立方体绘制结束

     rtri+=0.2f;      // 增加三角形的旋转变量
     rquad-=0.15f;      // 减少四边形的旋转变量
     return TRUE;      // 继续运行
    }

       
    这一课又结束了。到这里您应该已经较好的掌握了在3D空间创建对象的方法。必须将OpenGL屏幕想象成一张很大的画纸,后面还带着许多透明的层。差不多就是个由大量的点组成的立方体。这些点从左至右、从上至下、从前到后的布满了这个立方体。如果您能想象的出在屏幕的深度方向,应该在设计新3D对象时没有任何问题。
    如果您对3D空间的理解很困难的话,千万不要灰心! 刚开始的时候,领会这些内容会很难。象立方体这样的对象是您练习的好例子。继续努力吧!如果您有什么意见或建议请给我EMAIL。如果您认为有什么不对或可以改进,请告诉我。我想做最好的OpenGL教程并对您的反馈感兴趣。


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    越学越无知

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/10/13 9:52:00
     
     一分之千 帅哥哟,离线,有人找我吗?射手座1984-11-30
      
      
      威望:1
      等级:研一(随老板参加了WWW大会还和Tim Berners-Lee合了影^_^)
      文章:632
      积分:4379
      门派:XML.ORG.CN
      注册:2006/12/31

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给一分之千发送一个短消息 把一分之千加入好友 查看一分之千的个人资料 搜索一分之千在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看一分之千的博客2
    发贴心情 
    Lesson 05
       
    Expanding on the last tutorial, we'll now make the object into TRUE 3D object, rather than 2D objects in a 3D world. We will do this by adding a left, back, and right side to the triangle, and a left, right, back, top and bottom to the square. By doing this, we turn the triangle into a pyramid, and the square into a cube.

    We'll blend the colors on the pyramid, creating a smoothly colored object, and for the square we'll color each face a different color.   
       

    int DrawGLScene(GLvoid)      // Here's Where We Do All The Drawing
    {
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer
     glLoadIdentity();     // Reset The View
     glTranslatef(-1.5f,0.0f,-6.0f);    // Move Left And Into The Screen

     glRotatef(rtri,0.0f,1.0f,0.0f);    // Rotate The Pyramid On It's Y Axis

     glBegin(GL_TRIANGLES);     // Start Drawing The Pyramid

       
    A few of you have taken the code from the last tutorial, and made 3D objects of your own. One thing I've been asked quite a bit is "how come my objects are not spinning on their axis? It seems like they are spinning all over the screen". In order for your object to spin around an axis, it has to be designed AROUND that axis. You have to remember that the center of any object should be 0 on the X, 0 on the Y, and 0 on the Z.

    The following code will create the pyramid around a central axis. The top of the pyramid is one high from the center, the bottom of the pyramid is one down from the center. The top point is right in the middle (zero), and the bottom points are one left from center, and one right from center.

    Note that all triangles are drawn in a counterclockwise rotation. This is important, and will be explained in a future tutorial, for now, just know that it's good practice to make objects either clockwise or counterclockwise, but you shouldn't mix the two unless you have a reason to.

    We start off by drawing the Front Face. Because all of the faces share the top point, we will make this point red on all of the triangles. The color on the bottom two points of the triangles will alternate. The front face will have a green left point and a blue right point. Then the triangle on the right side will have a blue left point and a green right point. By alternating the bottom two colors on each face, we make a common colored point at the bottom of each face.   
       

      glColor3f(1.0f,0.0f,0.0f);   // Red
      glVertex3f( 0.0f, 1.0f, 0.0f);   // Top Of Triangle (Front)
      glColor3f(0.0f,1.0f,0.0f);   // Green
      glVertex3f(-1.0f,-1.0f, 1.0f);   // Left Of Triangle (Front)
      glColor3f(0.0f,0.0f,1.0f);   // Blue
      glVertex3f( 1.0f,-1.0f, 1.0f);   // Right Of Triangle (Front)

       
    Now we draw the right face. Notice then the two bottom point are drawn one to the right of center, and the top point is drawn one up on the y axis, and right in the middle of the x axis. causing the face to slope from center point at the top out to the right side of the screen at the bottom.

    Notice the left point is drawn blue this time. By drawing it blue, it will be the same color as the right bottom corner of the front face. Blending blue outwards from that one corner across both the front and right face of the pyramid.

    Notice how the remaining three faces are included inside the same glBegin(GL_TRIANGLES) and glEnd() as the first face. Because we're making this entire object out of triangles, OpenGL will know that every three points we plot are the three points of a triangle. Once it's drawn three points, if there are three more points, it will assume another triangle needs to be drawn. If you were to put four points instead of three, OpenGL would draw the first three and assume the fourth point is the start of a new triangle. It would not draw a Quad. So make sure you don't add any extra points by accident.   
       

      glColor3f(1.0f,0.0f,0.0f);   // Red
      glVertex3f( 0.0f, 1.0f, 0.0f);   // Top Of Triangle (Right)
      glColor3f(0.0f,0.0f,1.0f);   // Blue
      glVertex3f( 1.0f,-1.0f, 1.0f);   // Left Of Triangle (Right)
      glColor3f(0.0f,1.0f,0.0f);   // Green
      glVertex3f( 1.0f,-1.0f, -1.0f);   // Right Of Triangle (Right)

       
    Now for the back face. Again the colors switch. The left point is now green again, because the corner it shares with the right face is green.   
       

      glColor3f(1.0f,0.0f,0.0f);   // Red
      glVertex3f( 0.0f, 1.0f, 0.0f);   // Top Of Triangle (Back)
      glColor3f(0.0f,1.0f,0.0f);   // Green
      glVertex3f( 1.0f,-1.0f, -1.0f);   // Left Of Triangle (Back)
      glColor3f(0.0f,0.0f,1.0f);   // Blue
      glVertex3f(-1.0f,-1.0f, -1.0f);   // Right Of Triangle (Back)

       
    Finally we draw the left face. The colors switch one last time. The left point is blue, and blends with the right point of the back face. The right point is green, and blends with the left point of the front face.

    We're done drawing the pyramid. Because the pyramid only spins on the Y axis, we will never see the bottom, so there is no need to put a bottom on the pyramid. If you feel like experimenting, try adding a bottom using a quad, then rotate on the X axis to see if you've done it correctly. Make sure the color used on each corner of the quad matches up with the colors being used at the four corners of the pyramid.   
       

      glColor3f(1.0f,0.0f,0.0f);   // Red
      glVertex3f( 0.0f, 1.0f, 0.0f);   // Top Of Triangle (Left)
      glColor3f(0.0f,0.0f,1.0f);   // Blue
      glVertex3f(-1.0f,-1.0f,-1.0f);   // Left Of Triangle (Left)
      glColor3f(0.0f,1.0f,0.0f);   // Green
      glVertex3f(-1.0f,-1.0f, 1.0f);   // Right Of Triangle (Left)
     glEnd();      // Done Drawing The Pyramid

       
    Now we'll draw the cube. It's made up of six quads. All of the quads are drawn in a counter clockwise order. Meaning the first point is the top right, the second point is the top left, third point is bottom left, and finally bottom right. When we draw the back face, it may seem as though we are drawing clockwise, but you have to keep in mind that if we were behind the cube looking at the front of it, the left side of the screen is actually the right side of the quad, and the right side of the screen would actually be the left side of the quad.

    Notice we move the cube a little further into the screen in this lesson. By doing this, the size of the cube appears closer to the size of the pyramid. If you were to move it only 6 units into the screen, the cube would appear much larger than the pyramid, and parts of it might get cut off by the sides of the screen. You can play around with this setting, and see how moving the cube further into the screen makes it appear smaller, and moving it closer makes it appear larger. The reason this happens is perspective. Objects in the distance should appear smaller :)   
       

     glLoadIdentity();
     glTranslatef(1.5f,0.0f,-7.0f);    // Move Right And Into The Screen

     glRotatef(rquad,1.0f,1.0f,1.0f);   // Rotate The Cube On X, Y & Z

     glBegin(GL_QUADS);     // Start Drawing The Cube

       
    We'll start off by drawing the top of the cube. We move up one unit from the center of the cube. Notice that the Y axis is always one. We then draw a quad on the Z plane. Meaning into the screen. We start off by drawing the top right point of the top of the cube. The top right point would be one unit right, and one unit into the screen. The second point would be one unit to the left, and unit into the screen. Now we have to draw the bottom of the quad towards the viewer. so to do this, instead of going into the screen, we move one unit towards the screen. Make sense?   
       

      glColor3f(0.0f,1.0f,0.0f);   // Set The Color To Green
      glVertex3f( 1.0f, 1.0f,-1.0f);   // Top Right Of The Quad (Top)
      glVertex3f(-1.0f, 1.0f,-1.0f);   // Top Left Of The Quad (Top)
      glVertex3f(-1.0f, 1.0f, 1.0f);   // Bottom Left Of The Quad (Top)
      glVertex3f( 1.0f, 1.0f, 1.0f);   // Bottom Right Of The Quad (Top)

       
    The bottom is drawn the exact same way as the top, but because it's the bottom, it's drawn down one unit from the center of the cube. Notice the Y axis is always minus one. If we were under the cube, looking at the quad that makes the bottom, you would notice the top right corner is the corner closest to the viewer, so instead of drawing in the distance first, we draw closest to the viewer first, then on the left side closest to the viewer, and then we go into the screen to draw the bottom two points.

    If you didn't really care about the order the polygons were drawn in (clockwise or not), you could just copy the same code for the top quad, move it down on the Y axis to -1, and it would work, but ignoring the order the quad is drawn in can cause weird results once you get into fancy things such as texture mapping.   
       

      glColor3f(1.0f,0.5f,0.0f);   // Set The Color To Orange
      glVertex3f( 1.0f,-1.0f, 1.0f);   // Top Right Of The Quad (Bottom)
      glVertex3f(-1.0f,-1.0f, 1.0f);   // Top Left Of The Quad (Bottom)
      glVertex3f(-1.0f,-1.0f,-1.0f);   // Bottom Left Of The Quad (Bottom)
      glVertex3f( 1.0f,-1.0f,-1.0f);   // Bottom Right Of The Quad (Bottom)

       
    Now we draw the front of the Quad. We move one unit towards the screen, and away from the center to draw the front face. Notice the Z axis is always one. In the pyramid the Z axis was not always one. At the top, the Z axis was zero. If you tried changing the Z axis to zero in the following code, you'd notice that the corner you changed it on would slope into the screen. That's not something we want to do right now :)   
       

      glColor3f(1.0f,0.0f,0.0f);   // Set The Color To Red
      glVertex3f( 1.0f, 1.0f, 1.0f);   // Top Right Of The Quad (Front)
      glVertex3f(-1.0f, 1.0f, 1.0f);   // Top Left Of The Quad (Front)
      glVertex3f(-1.0f,-1.0f, 1.0f);   // Bottom Left Of The Quad (Front)
      glVertex3f( 1.0f,-1.0f, 1.0f);   // Bottom Right Of The Quad (Front)

       
    The back face is a quad the same as the front face, but it's set deeper into the screen. Notice the Z axis is now minus one for all of the points.   
       

      glColor3f(1.0f,1.0f,0.0f);   // Set The Color To Yellow
      glVertex3f( 1.0f,-1.0f,-1.0f);   // Bottom Left Of The Quad (Back)
      glVertex3f(-1.0f,-1.0f,-1.0f);   // Bottom Right Of The Quad (Back)
      glVertex3f(-1.0f, 1.0f,-1.0f);   // Top Right Of The Quad (Back)
      glVertex3f( 1.0f, 1.0f,-1.0f);   // Top Left Of The Quad (Back)

       
    Now we only have two more quads to draw and we're done. As usual, you'll notice one axis is always the same for all the points. In this case the X axis is always minus one. That's because we're always drawing to the left of center because this is the left face.   
       

      glColor3f(0.0f,0.0f,1.0f);   // Set The Color To Blue
      glVertex3f(-1.0f, 1.0f, 1.0f);   // Top Right Of The Quad (Left)
      glVertex3f(-1.0f, 1.0f,-1.0f);   // Top Left Of The Quad (Left)
      glVertex3f(-1.0f,-1.0f,-1.0f);   // Bottom Left Of The Quad (Left)
      glVertex3f(-1.0f,-1.0f, 1.0f);   // Bottom Right Of The Quad (Left)

       
    This is the last face to complete the cube. The X axis is always one. Drawing is counter clockwise. If you wanted to, you could leave this face out, and make a box :)

    Or if you felt like experimenting, you could always try changing the color of each point on the cube to make it blend the same way the pyramid blends. You can see an example of a blended cube by downloading Evil's first GL demo from my web page. Run it and press TAB. You'll see a beautifully colored cube, with colors flowing across all the faces.   
       

      glColor3f(1.0f,0.0f,1.0f);   // Set The Color To Violet
      glVertex3f( 1.0f, 1.0f,-1.0f);   // Top Right Of The Quad (Right)
      glVertex3f( 1.0f, 1.0f, 1.0f);   // Top Left Of The Quad (Right)
      glVertex3f( 1.0f,-1.0f, 1.0f);   // Bottom Left Of The Quad (Right)
      glVertex3f( 1.0f,-1.0f,-1.0f);   // Bottom Right Of The Quad (Right)
     glEnd();      // Done Drawing The Quad

     rtri+=0.2f;      // Increase The Rotation Variable For The Triangle
     rquad-=0.15f;      // Decrease The Rotation Variable For The Quad
     return TRUE;      // Keep Going
    }

       
    By the end of this tutorial, you should have a better understanding of how objects are created in 3D space. You have to think of the OpenGL screen as a giant piece of graph paper, with many transparent layers behind it. Almost like a giant cube made of of points. Some of the points move left to right, some move up and down, and some move further back in the cube. If you can visualize the depth into the screen, you shouldn't have any problems designing new 3D objects.

    If you're having a hard time understanding 3D space, don't get frustrated. It can be difficult to grasp right off the start. An object like the cube is a good example to learn from. If you notice, the back face is drawn exactly the same as the front face, it's just further into the screen. Play around with the code, and if you just can't grasp it, email me, and I'll try to answer your questions.

    Jeff Molofee (NeHe)

    ----------------------------------------------
    越学越无知

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/10/13 9:54:00
     
     一分之千 帅哥哟,离线,有人找我吗?射手座1984-11-30
      
      
      威望:1
      等级:研一(随老板参加了WWW大会还和Tim Berners-Lee合了影^_^)
      文章:632
      积分:4379
      门派:XML.ORG.CN
      注册:2006/12/31

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给一分之千发送一个短消息 把一分之千加入好友 查看一分之千的个人资料 搜索一分之千在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看一分之千的博客3
    发贴心情 

    第六课

    按此在新窗口浏览图片纹理映射:

    在这一课里,我将教会你如何把纹理映射到立方体的六个面。

      
       
       
    学习 texture map 纹理映射(贴图)有很多好处。比方说您想让一颗导弹飞过屏幕。根据前几课的知识,我们最可行的办法可能是很多个多边形来构建导弹的轮廓并加上有趣的颜色。使用纹理映射,您可以使用真实的导弹图像并让它飞过屏幕。您觉得哪个更好看?照片还是一大堆三角形和四边形?使用纹理映射的好处还不止是更好看,而且您的程序运行会更快。导弹贴图可能只是一个飞过窗口的四边形。一个由多边形构建而来的导弹却很可能包括成百上千的多边形。很显然,贴图极大的节省了CPU时间。
    现在我们在第一课的代码开始处增加五行新代码。新增的第一行是 #include <stdio.h> 。它允许我们对文件进行操作,为了在后面的代码中使用 fopen() ,我们增加了这一行。然后我们增加了三个新的浮点变量... xrot , yrot 和 zrot 。这些变量用来使立方体绕X、Y、Z轴旋转。最后一行 GLuint texture[1] 为一个纹理分配存储空间。如果您需要不止一个的纹理,应该将参数1改成您所需要的参数。  
       

    #include <stdio.h>       // 标准输入/输出库的头文件
    #include <glaux.h>       // GLaux库的头文件

    GLfloat  xrot;        // X 旋转量
    GLfloat  yrot;        // Y 旋转量
    GLfloat  zrot;        // Z 旋转量

    GLuint  texture[1];       // 存储一个纹理

       
    紧跟上面的代码在 ReSizeGLScene() 之前,我们增加了下面这一段代码。这段代码用来加载位图文件。如果文件不存在,返回 NULL 告知程序无法加载位图。在我开始解释这段代码之前,关于用作纹理的图像我想有几点十分重要,并且您必须明白。此图像的宽和高必须是2的n次方;宽度和高度最小必须是64象素;并且出于兼容性的原因,图像的宽度和高度不应超过256象素。如果您的原始素材的宽度和高度不是64,128,256象素的话,使用图像处理软件重新改变图像的大小。可以肯定有办法能绕过这些限制,但现在我们只需要用标准的纹理尺寸。
    首先,我们创建一个文件句柄。句柄是个用来鉴别资源的数值,它使程序能够访问此资源。我们开始先将句柄设为 NULL 。
      
       

    AUX_RGBImageRec *LoadBMP(char *Filename)     // 载入位图图象
    {
     FILE *File=NULL;       // 文件句柄

       
    接下来检查文件名是否已提供。因为 LoadBMP() 可以无参数调用,所以我们不得不检查一下。您可不想什么都没载入吧.....:)  
       

     if (!Filename)        // 确保文件名已提供
     {
      return NULL;       // 如果没提供,返回 NULL
     }

       
    接着检查文件是否存在。下面这一行尝试打开文件。  
       

     File=fopen(Filename,"r");      // 尝试打开文件

       
    如果我们能打开文件的话,很显然文件是存在的。使用 fclose(File) 关闭文件。 auxDIBImageLoad(Filename) 读取图象数据并将其返回。  
       

     if (File)        // 文件存在么?
     {
      fclose(File);       // 关闭句柄
      return auxDIBImageLoad(Filename);    // 载入位图并返回指针
     }

       
    如果我们不能打开文件,我们将返回NULL。这意味着文件无法载入。程序在后面将检查文件是否已载入。如果没有,我们将退出程序并弹出错误消息。  
       

     return NULL;        // 如果载入失败,返回 NULL
    }

       
    下一部分代码载入位图(调用上面的代码)并转换成纹理。  
       

    int LoadGLTextures()        // 载入位图(调用上面的代码)并转换成纹理
    {

       
    然后设置一个叫做 Status 的变量。我们使用它来跟踪是否能够载入位图以及能否创建纹理。 Status 缺省设为 FALSE (表示没有载入或创建任何东东)。  
       

     int Status=FALSE;       // 状态指示器

       
    现在我们创建存储位图的图像记录。次记录包含位图的宽度、高度和数据。  
       

     AUX_RGBImageRec *TextureImage[1];     // 创建纹理的存储空间

       
    清除图像记录,确保其内容为空  
       

     memset(TextureImage,0,sizeof(void *)*1);    // 将指针设为 NULL

       
    现在载入位图,并将其转换为纹理。 TextureImage[0]=LoadBMP("Data/NeHe.bmp") 调用 LoadBMP() 的代码。载入 Data 目录下的 NeHe.bmp 位图文件。如果一切正常,图像数据将存放在 TextureImage[0] 中, Status 被设为 TRUE ,然后我们开始创建纹理。  
       

     // 载入位图,检查有无错误,如果位图没找到则退出
     if (TextureImage[0]=LoadBMP("Data/NeHe.bmp"))
     {
      Status=TRUE;       // 将 Status 设为 TRUE

       
    现在使用中 TextureImage[0] 的数据创建纹理。第一行 glGenTextures(1, &texture[0]) 告诉OpenGL我们想生成一个纹理名字(如果您想载入多个纹理,加大数字)。值得注意的是,开始我们使用 GLuint texture[1] 来创建一个纹理的存储空间,您也许会认为第一个纹理就是存放在 &texture[1] 中的,但这是错的。正确的地址应该是 &texture[0] 。同样如果使用 GLuint texture[2] 的话,第二个纹理存放在 texture[1] 中。『译者注:学C的,在这里应该没有障碍,数组就是从零开始的嘛。』
    第二行 glBindTexture(GL_TEXTURE_2D, texture[0]) 告诉OpenGL将纹理名字 texture[0] 绑定到纹理目标上。2D纹理只有高度(在 Y 轴上)和宽度(在 X 轴上)。主函数将纹理名字指派给纹理数据。本例中我们告知OpenGL, &texture[0] 处的内存已经可用。我们创建的纹理将存储在 &texture[0] 的 指向的内存区域。
      
       

      glGenTextures(1, &texture[0]);     // 创建纹理

      // 使用来自位图数据生成 的典型纹理
      glBindTexture(GL_TEXTURE_2D, texture[0]);

       
    下来我们创建真正的纹理。下面一行告诉OpenGL此纹理是一个2D纹理 ( GL_TEXTURE_2D )。参数“0”代表图像的详细程度,通常就由它为零去了。参数三是数据的成分数。因为图像是由红色数据,绿色数据,蓝色数据三种组分组成。 TextureImage[0]->sizeX 是纹理的宽度。如果您知道宽度,您可以在这里填入,但计算机可以很容易的为您指出此值。 TextureImage[0]->sizey 是纹理的高度。参数零是边框的值,一般就是“0”。 GL_RGB 告诉OpenGL图像数据由红、绿、蓝三色数据组成。
    GL_UNSIGNED_BYTE 意味着组成图像的数据是无符号字节类型的。最后... TextureImage[0]->data 告诉OpenGL纹理数据的来源。此例中指向存放在 TextureImage[0] 记录中的数据。  
       

      // 生成纹理
      glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);

       
    下面的两行告诉OpenGL在显示图像时,当它比放大得原始的纹理大 ( GL_TEXTURE_MAG_FILTER )或缩小得比原始得纹理小( GL_TEXTURE_MIN_FILTER )时OpenGL采用的滤波方式。通常这两种情况下我都采用 GL_LINEAR 。这使得纹理从很远处到离屏幕很近时都平滑显示。使用 GL_LINEAR 需要CPU和显卡做更多的运算。如果您的机器很慢,您也许应该采用 GL_NEAREST 。过滤的纹理在放大的时候,看起来斑驳的很『译者注:马赛克啦』。您也可以结合这两种滤波方式。在近处时使用 GL_LINEAR ,远处时 GL_NEAREST 。  
       

      glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // 线形滤波
      glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // 线形滤波
     }

       
    现在我们释放前面用来存放位图数据的内存。我们先查看位图数据是否存放在处。如果是的话,再查看数据是否已经存储。如果已经存储的话,删了它。接着再释放 TextureImage[0] 图像结构以保证所有的内存都能释放。  
       

     if (TextureImage[0])       // 纹理是否存在
     {
      if (TextureImage[0]->data)     // 纹理图像是否存在
      {
       free(TextureImage[0]->data);    // 释放纹理图像占用的内存
      }

      free(TextureImage[0]);      // 释放图像结构
     }

       
    最后返回状态变量。如果一切OK,变量 Status 的值为 TRUE 。否则为 FALSE 。  
       

     return Status;        // 返回 Status
    }

       
    我只在 InitGL 中增加很少的几行代码。但为了方便您查看增加了哪几行,我这段代码全部重贴一遍。 if (!LoadGLTextures()) 这行代码调用上面讲的子例程载入位图并生成纹理。如果因为任何原因 LoadGLTextures() 调用失败,接着的一行返回FALSE。如果一切OK,并且纹理创建好了,我们启用2D纹理映射。如果您忘记启用的话,您的对象看起来永远都是纯白色,这一定不是什么好事。  
       

    int InitGL(GLvoid)        // 此处开始对OpenGL进行所有设置
    {
     if (!LoadGLTextures())       // 调用纹理载入子例程
     {
      return FALSE;       // 如果未能载入,返回FALSE
     }

     glEnable(GL_TEXTURE_2D);      // 启用纹理映射
     glShadeModel(GL_SMOOTH);      // 启用阴影平滑
     glClearColor(0.0f, 0.0f, 0.0f, 0.5f);     // 黑色背景
     glClearDepth(1.0f);       // 设置深度缓存
     glEnable(GL_DEPTH_TEST);      // 启用深度测试
     glDepthFunc(GL_LEQUAL);       // 所作深度测试的类型
     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);   // 真正精细的透视修正
     return TRUE;        // 初始化 OK
    }

       
    现在我们绘制贴图『译者注:其实贴图就是纹理映射。将术语换来换去不好,我想少打俩字。^_^』过的立方体。这段代码被狂注释了一把,应该很好懂。开始两行代码 glClear() 和 glLoadIdentity() 是第一课中就有的代码。 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) 清除屏幕并设为我们在 InitGL() 中选定的颜色,本例中是黑色。深度缓存也被清除。模型观察矩阵也使用glLoadIdentity()重置。  
       

    int DrawGLScene(GLvoid)        // 从这里开始进行所有的绘制
    {
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);   // 清除屏幕和深度缓存
     glLoadIdentity();       // 重置当前的模型观察矩阵
     glTranslatef(0.0f,0.0f,-5.0f);      // 移入屏幕 5 个单位

       
    下面三行使立方体绕X、Y、Z轴旋转。旋转多少依赖于变量 xrot , yrot 和 zrot 的值。  
       

     glRotatef(xrot,1.0f,0.0f,0.0f);      // 绕X轴旋转
     glRotatef(yrot,0.0f,1.0f,0.0f);      // 绕Y轴旋转
     glRotatef(zrot,0.0f,0.0f,1.0f);      // 绕Z轴旋转

       
    下一行代码选择我们使用的纹理。如果您在您的场景中使用多个纹理,您应该使用来 glBindTexture(GL_TEXTURE_2D, texture[ 所使用纹理对应的数字 ]) 选择要绑定的纹理。当您想改变纹理时,应该绑定新的纹理。有一点值得指出的是,您不能在 glBegin() 和 glEnd() 之间绑定纹理,必须在 glBegin() 之前或 glEnd() 之后绑定。注意我们在后面是如何使用 glBindTexture 来指定和绑定纹理的。  
       

     glBindTexture(GL_TEXTURE_2D, texture[0]);    // 选择纹理

       
    为了将纹理正确的映射到四边形上,您必须将纹理的右上角映射到四边形的右上角,纹理的左上角映射到四边形的左上角,纹理的右下角映射到四边形的右下角,纹理的左下角映射到四边形的左下角。如果映射错误的话,图像显示时可能上下颠倒,侧向一边或者什么都不是。
    glTexCoord2f 的第一个参数是X坐标。 0.0f 是纹理的左侧。 0.5f 是纹理的中点, 1.0f 是纹理的右侧。 glTexCoord2f 的第二个参数是Y坐标。 0.0f 是纹理的底部。 0.5f 是纹理的中点, 1.0f 是纹理的顶部。

    所以纹理的左上坐标是 X:0.0f,Y:1.0f ,四边形的左上顶点是 X: -1.0f,Y:1.0f 。其余三点依此类推。

    试着玩玩 glTexCoord2f 的X,Y坐标参数。把 1.0f 改为 0.5f 将只显示纹理的左半部分,把 0.0f 改为 0.5f 将只显示纹理的右半部分。
      
       

     glBegin(GL_QUADS);
      // 前面
      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); // 纹理和四边形的左上
      // 后面
      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); // 纹理和四边形的左下
      // 顶面
      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); // 纹理和四边形的右上
      // 底面
      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); // 纹理和四边形的右下
      // 右面
      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); // 纹理和四边形的左下
      // 左面
      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();

       
    现在增加 xrot , yrot 和 zrot 的值。尝试变化每次各变量的改变值来调节立方体的旋转速度,或改变+/-号来调节立方体的旋转方向。  
       

     xrot+=0.3f;        // X 轴旋转
     yrot+=0.2f;        // Y 轴旋转
     zrot+=0.4f;        // Z 轴旋转
     return true;        // 继续运行
    }

       
    现在您应该比较好的理解纹理映射(贴图)了。您应该掌握了给任意四边形表面贴上您所喜爱的图像的技术。一旦您对2D纹理映射的理解感到自信的时候,试试给立方体的六个面贴上不同的纹理。
    当您理解纹理坐标的概念后,纹理映射并不难理解。!如果您有什么意见或建议请给我EMAIL。如果您认为有什么不对或可以改进,请告诉我。

    ----------------------------------------------
    越学越无知

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/10/13 9:57:00
     
     一分之千 帅哥哟,离线,有人找我吗?射手座1984-11-30
      
      
      威望:1
      等级:研一(随老板参加了WWW大会还和Tim Berners-Lee合了影^_^)
      文章:632
      积分:4379
      门派:XML.ORG.CN
      注册:2006/12/31

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给一分之千发送一个短消息 把一分之千加入好友 查看一分之千的个人资料 搜索一分之千在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看一分之千的博客4
    发贴心情 
    Lesson 06
       
    Learning how to texture map has many benefits. Lets say you wanted a missile to fly across the screen. Up until this tutorial we'd probably make the entire missile out of polygons, and fancy colors. With texture mapping, you can take a real picture of a missile and make the picture fly across the screen. Which do you think will look better? A photograph or an object made up of triangles and squares? By using texture mapping, not only will it look better, but your program will run faster. The texture mapped missile would only be one quad moving across the screen. A missile made out of polygons could be made up of hundreds or thousands of polygons. The single texture mapped quad will use alot less processing power.

    Lets start off by adding five new lines of code to the top of lesson one. The first new line is #include <stdio.h>. Adding this header file allows us to work with files. In order to use fopen() later in the code we need to include this line. Then we add three new floating point variables... xrot, yrot and zrot. These variables will be used to rotate the cube on the x axis, the y axis, and the z axis. The last line GLuint texture[1] sets aside storage space for one texture. If you wanted to load in more than one texture, you would change the number one to the number of textures you wish to load.   
       

    #include <windows.h>       // Header File For Windows
    #include <stdio.h>       // Header File For Standard Input/Output ( NEW )
    #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

    HDC  hDC=NULL;       // Private GDI Device Context
    HGLRC  hRC=NULL;       // Permanent Rendering Context
    HWND  hWnd=NULL;       // Holds Our Window Handle
    HINSTANCE hInstance;       // Holds The Instance Of The Application

    bool  keys[256];       // Array Used For The Keyboard Routine
    bool  active=TRUE;       // Window Active Flag
    bool  fullscreen=TRUE;      // Fullscreen Flag

    GLfloat  xrot;        // X Rotation ( NEW )
    GLfloat  yrot;        // Y Rotation ( NEW )
    GLfloat  zrot;        // Z Rotation ( NEW )

    GLuint  texture[1];       // Storage For One Texture ( NEW )

    LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);    // Declaration For WndProc

       
    Now immediately after the above code, and before ReSizeGLScene(), we want to add the following section of code. The job of this code is to load in a bitmap file. If the file doesn't exist NULL is sent back meaning the texture couldn't be loaded. Before I start explaining the code there are a few VERY important things you need to know about the images you plan to use as textures. The image height and width MUST be a power of 2. The width and height must be at least 64 pixels, and for compatability reasons, shouldn't be more than 256 pixels. If the image you want to use is not 64, 128 or 256 pixels on the width or height, resize it in an art program. There are ways around this limitation, but for now we'll just stick to standard texture sizes.

    First thing we do is create a file handle. A handle is a value used to identify a resource so that our program can access it. We set the handle to NULL to start off.   
       

    AUX_RGBImageRec *LoadBMP(char *Filename)     // Loads A Bitmap Image
    {
     FILE *File=NULL;       // File Handle

       
    Next we check to make sure that a filename was actually given. The person may have use LoadBMP() without specifying the file to load, so we have to check for this. We don't want to try loading nothing :)   
       

     if (!Filename)        // Make Sure A Filename Was Given
     {
      return NULL;       // If Not Return NULL
     }

       
    If a filename was given, we check to see if the file exists. The line below tries to open the file.   
       

     File=fopen(Filename,"r");      // Check To See If The File Exists

       
    If we were able to open the file it obviously exists. We close the file with fclose(File) then we return the image data. auxDIBImageLoad(Filename) reads in the data.   
       

     if (File)        // Does The File Exist?
     {
      fclose(File);       // Close The Handle
      return auxDIBImageLoad(Filename);    // Load The Bitmap And Return A Pointer
     }

       
    If we were unable to open the file we'll return NULL. which means the file couldn't be loaded. Later on in the program we'll check to see if the file was loaded. If it wasn't we'll quit the program with an error message.   
       

     return NULL;        // If Load Failed Return NULL
    }

       
    This is the section of code that loads the bitmap (calling the code above) and converts it into a texture.   
       

    int LoadGLTextures()        // Load Bitmaps And Convert To Textures
    {

       
    We'll set up a variable called Status. We'll use this variable to keep track of whether or not we were able to load the bitmap and build a texture. We set Status to FALSE (meaning nothing has been loaded or built) by default.   
       

     int Status=FALSE;       // Status Indicator

       
    Now we create an image record that we can store our bitmap in. The record will hold the bitmap width, height, and data.   
       

     AUX_RGBImageRec *TextureImage[1];     // Create Storage Space For The Texture

       
    We clear the image record just to make sure it's empty.   
       

     memset(TextureImage,0,sizeof(void *)*1);    // Set The Pointer To NULL

       
    Now we load the bitmap and convert it to a texture. TextureImage[0]=LoadBMP("Data/NeHe.bmp") will jump to our LoadBMP() code. The file named NeHe.bmp in the Data directory will be loaded. If everything goes well, the image data is stored in TextureImage[0], Status is set to TRUE, and we start to build our texture.   
       

     // Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit
     if (TextureImage[0]=LoadBMP("Data/NeHe.bmp"))
     {
      Status=TRUE;       // Set The Status To TRUE

       
    Now that we've loaded the image data into TextureImage[0], we will build a texture using this data. The first line glGenTextures(1, &texture[0]) tells OpenGL we want to generate one texture name (increase the number if you load more than one texture). Remember at the very beginning of this tutorial we created room for one texture with the line GLuint texture[1]. Although you'd think the first texture would be stored at &texture[1] instead of &texture[0], it's not. The first actual storage area is 0. If we wanted two textures we would use GLuint texture[2] and the second texture would be stored at texture[1].

    The second line glBindTexture(GL_TEXTURE_2D, texture[0]) tells OpenGL to bind the named texture texture[0] to a texture target. 2D textures have both height (on the Y axes) and width (on the X axes). The main function of glBindTexture is to assign a texture name to texture data. In this case we're telling OpenGL there is memory available at &texture[0]. When we create the texture, it will be stored in the memory that &texture[0] references.   
       

      glGenTextures(1, &texture[0]);     // Create The Texture

      // Typical Texture Generation Using Data From The Bitmap
      glBindTexture(GL_TEXTURE_2D, texture[0]);

       
    Next we create the actual texture. The following line tells OpenGL the texture will be a 2D texture (GL_TEXTURE_2D). Zero represents the images level of detail, this is usually left at zero. Three is the number of data components. Because the image is made up of red data, green data and blue data, there are three components. TextureImage[0]->sizeX is the width of the texture. If you know the width, you can put it here, but it's easier to let the computer figure it out for you. TextureImage[0]->sizey is the height of the texture. zero is the border. It's usually left at zero. GL_RGB tells OpenGL the image data we are using is made up of red, green and blue data in that order. GL_UNSIGNED_BYTE means the data that makes up the image is made up of unsigned bytes, and finally... TextureImage[0]->data tells OpenGL where to get the texture data from. In this case it points to the data stored in the TextureImage[0] record.   
       

      // Generate The Texture
      glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);

       
    The next two lines tell OpenGL what type of filtering to use when the image is larger (GL_TEXTURE_MAG_FILTER) or stretched on the screen than the original texture, or when it's smaller (GL_TEXTURE_MIN_FILTER) on the screen than the actual texture. I usually use GL_LINEAR for both. This makes the texture look smooth way in the distance, and when it's up close to the screen. Using GL_LINEAR requires alot of work from the processor/video card, so if your system is slow, you might want to use GL_NEAREST. A texture that's filtered with GL_NEAREST will appear blocky when it's stretched. You can also try a combination of both. Make it filter things up close, but not things in the distance.   
       

      glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // Linear Filtering
      glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // Linear Filtering
     }

       
    Now we free up any ram that we may have used to store the bitmap data. We check to see if the bitmap data was stored in TextureImage[0]. If it was we check to see if the data has been stored. If data was stored, we erase it. Then we free the image structure making sure any used memory is freed up.   
       

     if (TextureImage[0])       // If Texture Exists
     {
      if (TextureImage[0]->data)     // If Texture Image Exists
      {
       free(TextureImage[0]->data);    // Free The Texture Image Memory
      }

      free(TextureImage[0]);      // Free The Image Structure
     }

       
    Finally we return the status. If everything went OK, the variable Status will be TRUE. If anything went wrong, Status will be FALSE.   
       

     return Status;        // Return The Status
    }

       
    I've added a few lines of code to InitGL. I'll repost the entire section of code, so it's easy to see the lines that I've added, and where they go in the code. The first line if (!LoadGLTextures()) jumps to the routine above which loads the bitmap and makes a texture from it. If LoadGLTextures() fails for any reason, the next line of code will return FALSE. If everything went OK, and the texture was created, we enable 2D texture mapping. If you forget to enable texture mapping your object will usually appear solid white, which is definitely not good.   
       

    int InitGL(GLvoid)        // All Setup For OpenGL Goes Here
    {
     if (!LoadGLTextures())       // Jump To Texture Loading Routine ( NEW )
     {
      return FALSE;       // If Texture Didn't Load Return FALSE ( NEW )
     }

     glEnable(GL_TEXTURE_2D);      // Enable Texture Mapping ( NEW )
     glShadeModel(GL_SMOOTH);      // Enable Smooth Shading
     glClearColor(0.0f, 0.0f, 0.0f, 0.5f);     // Black Background
     glClearDepth(1.0f);       // Depth Buffer Setup
     glEnable(GL_DEPTH_TEST);      // Enables Depth Testing
     glDepthFunc(GL_LEQUAL);       // The Type Of Depth Testing To Do
     glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);   // Really Nice Perspective Calculations
     return TRUE;        // Initialization Went OK
    }

       
    Now we draw the textured cube. You can replace the DrawGLScene code with the code below, or you can add the new code to the original lesson one code. This section will be heavily commented so it's easy to understand. The first two lines of code glClear() and glLoadIdentity() are in the original lesson one code. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) will clear the screen to the color we selected in InitGL(). In this case, the screen will be cleared to black. The depth buffer will also be cleared. The view will then be reset with glLoadIdentity().   
       

    int DrawGLScene(GLvoid)        // Here's Where We Do All The Drawing
    {
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);   // Clear Screen And Depth Buffer
     glLoadIdentity();       // Reset The Current Matrix
     glTranslatef(0.0f,0.0f,-5.0f);      // Move Into The Screen 5 Units


       
    The following three lines of code will rotate the cube on the x axis, then the y axis, and finally the z axis. How much it rotates on each axis will depend on the value stored in xrot, yrot and 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

       
    The next line of code selects which texture we want to use. If there was more than one texture you wanted to use in your scene, you would select the texture using glBindTexture(GL_TEXTURE_2D, texture[number of texture to use]). If you wanted to change textures, you would bind to the new texture. One thing to note is that you can NOT bind a texture inside glBegin() and glEnd(), you have to do it before or after glBegin(). Notice how we use glBindTextures to specify which texture to create and to select a specific texture.   
       

     glBindTexture(GL_TEXTURE_2D, texture[0]);    // Select Our Texture

       
    To properly map a texture onto a quad, you have to make sure the top right of the texture is mapped to the top right of the quad. The top left of the texture is mapped to the top left of the quad, the bottom right of the texture is mapped to the bottom right of the quad, and finally, the bottom left of the texture is mapped to the bottom left of the quad. If the corners of the texture do not match the same corners of the quad, the image may appear upside down, sideways, or not at all.

    The first value of glTexCoord2f is the X coordinate. 0.0f is the left side of the texture. 0.5f is the middle of the texture, and 1.0f is the right side of the texture. The second value of glTexCoord2f is the Y coordinate. 0.0f is the bottom of the texture. 0.5f is the middle of the texture, and 1.0f is the top of the texture.

    So now we know the top left coordinate of a texture is 0.0f on X and 1.0f on Y, and the top left vertex of a quad is -1.0f on X, and 1.0f on Y. Now all you have to do is match the other three texture coordinates up with the remaining three corners of the quad.

    Try playing around with the x and y values of glTexCoord2f. Changing 1.0f to 0.5f will only draw the left half of a texture from 0.0f (left) to 0.5f (middle of the texture). Changing 0.0f to 0.5f will only draw the right half of a texture from 0.5f (middle) to 1.0f (right).   
       

     glBegin(GL_QUADS);
      // Front Face
      glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f); // Bottom Left Of The Texture and Quad
      glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f); // Bottom Right Of The Texture and Quad
      glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f); // Top Right Of The Texture and Quad
      glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f); // Top Left Of The Texture and Quad
      // Back Face
      glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // Bottom Right Of The Texture and Quad
      glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f); // Top Right Of The Texture and Quad
      glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f); // Top Left Of The Texture and Quad
      glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // Bottom Left Of The Texture and Quad
      // Top Face
      glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f); // Top Left Of The Texture and Quad
      glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,  1.0f,  1.0f); // Bottom Left Of The Texture and Quad
      glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,  1.0f,  1.0f); // Bottom Right Of The Texture and Quad
      glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f); // Top Right Of The Texture and Quad
      // Bottom Face
      glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // Top Right Of The Texture and Quad
      glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // Top Left Of The Texture and Quad
      glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f); // Bottom Left Of The Texture and Quad
      glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f); // Bottom Right Of The Texture and Quad
      // Right face
      glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // Bottom Right Of The Texture and Quad
      glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f); // Top Right Of The Texture and Quad
      glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f); // Top Left Of The Texture and Quad
      glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f); // Bottom Left Of The Texture and Quad
      // Left Face
      glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // Bottom Left Of The Texture and Quad
      glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f); // Bottom Right Of The Texture and Quad
      glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f); // Top Right Of The Texture and Quad
      glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f); // Top Left Of The Texture and Quad
     glEnd();

       
    Now we increase the value of xrot, yrot and zrot. Try changing the number each variable increases by to make the cube spin faster or slower, or try changing a + to a - to make the cube spin the other direction.   
       

     xrot+=0.3f;        // X Axis Rotation
     yrot+=0.2f;        // Y Axis Rotation
     zrot+=0.4f;        // Z Axis Rotation
     return true;        // Keep Going
    }

       
    You should now have a better understanding of texture mapping. You should be able to texture map the surface of any quad with an image of your choice. Once you feel confident with your understanding of 2D texture mapping, try adding six different textures to the cube.

    Texture mapping isn't to difficult to understand once you understand texture coordinates. If you're having problems understanding any part of this tutorial, let me know. Either I'll rewrite that section of the tutorial, or I'll reply back to you in email. Have fun creating texture mapped scenes of your own :)

    Jeff Molofee (NeHe)

    ----------------------------------------------
    越学越无知

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/10/13 10:03:00
     
     skflip 帅哥哟,离线,有人找我吗?
      
      
      等级:大一新生
      文章:2
      积分:58
      门派:XML.ORG.CN
      注册:2009/2/13

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给skflip发送一个短消息 把skflip加入好友 查看skflip的个人资料 搜索skflip在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看skflip的博客5
    发贴心情 
    好好好!没下载一份都觉得特感激,楼主真的是辛苦了!
    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2009/2/13 12:39:00
     
     jisuanjituxingxue 帅哥哟,离线,有人找我吗?
      
      
      等级:大一新生
      文章:1
      积分:54
      门派:XML.ORG.CN
      注册:2009/4/11

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给jisuanjituxingxue发送一个短消息 把jisuanjituxingxue加入好友 查看jisuanjituxingxue的个人资料 搜索jisuanjituxingxue在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看jisuanjituxingxue的博客6
    发贴心情 
    非常感谢,努力学习中
    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2009/4/11 16:35:00
     
     guhl112233 帅哥哟,离线,有人找我吗?
      
      
      等级:大一新生
      文章:2
      积分:60
      门派:XML.ORG.CN
      注册:2010/9/7

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给guhl112233发送一个短消息 把guhl112233加入好友 查看guhl112233的个人资料 搜索guhl112233在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看guhl112233的博客7
    发贴心情 
    如果六个面要六个不同的纹理,楼主该怎么弄啊?
    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2010/9/14 14:45:00
     
     陆仁贾 帅哥哟,离线,有人找我吗?
      
      
      等级:大一新生
      文章:1
      积分:55
      门派:XML.ORG.CN
      注册:2012/8/11

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给陆仁贾发送一个短消息 把陆仁贾加入好友 查看陆仁贾的个人资料 搜索陆仁贾在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看陆仁贾的博客8
    发贴心情 
    TextureImage[0]=LoadBMP("Data/NeHe.bmp")
    loadbmp(),()中的部分该怎么写啊,为什么自己编写的,不行,求指导
    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2012/8/19 0:42:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 C/C++编程思想 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/11/25 22:59:23

    本主题贴数8,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    203.125ms