请问在OPENGL中VC给立方体添加纹理的最简单的方法。

解决方案 »

  1.   

    下面是  Jeff Molofee(NeHe)的OpenGL教程中关于纹理贴图的,不知道算不算最简单。#include <windows.h>                    // Windows的头文件
      #include <stdio.h>                     // 标准输入/输出库的头文件 ( 新增 )
      #include <gl\gl.h>                     // OpenGL32库的头文件
      #include <gl\glu.h>                     // GLu32库的头文件
      #include <gl\glaux.h>                    // GLaux库的头文件  HGLRC hRC=NULL;                       // 永久着色描述表
      HDC hDC=NULL;                        // 私有GDI设备描述表
      HWND hWnd=NULL;                       // 保存我们的窗口句柄
      HINSTANCE hInstance;                    // 保存程序的实例  bool keys[256];                       // 用于键盘例程的数组
      bool active=TRUE;                      // 窗口的活动标志,缺省为TRUE
      bool fullscreen=TRUE;                    // 全屏标志缺省设定成全屏模式  GLfloat xrot;                        // X 旋转量 ( 新增 )
      GLfloat yrot;                        // Y 旋转量 ( 新增 )
      GLfloat zrot;                        // Z 旋转量 ( 新增 )  GLuint texture[1];                     // 存储一个纹理 ( 新增 )  LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);    // WndProc的定义  紧跟上面的代码在 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;                  // Status 状态指示器  现在我们创建存储位图的图像记录。次记录包含位图的宽度、高度和数据。      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]);
      

  2.   

    下来我们创建真正的纹理。下面一行告诉OpenGL此纹理是一个2D纹理( GL_TEXTURE_2D )。数字零代表图像的详细程度,通常就由它为零去了。数字三是数据的成分数。因为图像是由红色数据,绿色数据,蓝色数据三种组分组成。 TextureImage[0]->sizeX 是纹理的宽度。如果您知道宽度,您可以在这里填入,但计算机可以很容易的为您指出此值。 TextureImage[0]->sizey 是纹理的高度。数字零是边框的值,一般就是零。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]);     // 选择纹理
      

  3.   

    为了将纹理正确的映射到四边形上,您必须将纹理的右上角映射到四边形的右上角,纹理的左上角映射到四边形的左上角,纹理的右下角映射到四边形的右下角,纹理的左下角映射到四边形的左下角。如果映射错误的话,图像显示时可能上下颠倒,侧向一边或者什么都不是。
      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;                    // 继续运行
      }
    【完】
      

  4.   

    只要将纹理坐标顶点坐标和多边形顶点坐标对齐,就可以。只有一句话,但要实现的话,你得创建OpenGL框架,初始化OPenGL环境,这可不是一句话可以说完的,建议看一些Opengl的基础书籍。另外,建议把贴图的过程放在列表中,初始化时完成,以后调用这个列表就可以了,这可以大大优化你的速度。