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

powerman 2003-12-28 08:49:34
请问在OPENGL中VC给立方体添加纹理的最简单的方法。
...全文
476 5 打赏 收藏 转发到动态 举报
写回复
用AI写文章
5 条回复
切换为时间正序
请发表友善的回复…
发表回复
wangweintk 2003-12-29
  • 打赏
  • 举报
回复
只要将纹理坐标顶点坐标和多边形顶点坐标对齐,就可以。

只有一句话,但要实现的话,你得创建OpenGL框架,初始化OPenGL环境,这可不是一句话可以说完的,建议看一些Opengl的基础书籍。

另外,建议把贴图的过程放在列表中,初始化时完成,以后调用这个列表就可以了,这可以大大优化你的速度。

doubhua 2003-12-28
  • 打赏
  • 举报
回复
为了将纹理正确的映射到四边形上,您必须将纹理的右上角映射到四边形的右上角,纹理的左上角映射到四边形的左上角,纹理的右下角映射到四边形的右下角,纹理的左下角映射到四边形的左下角。如果映射错误的话,图像显示时可能上下颠倒,侧向一边或者什么都不是。
  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;                    // 继续运行
  }
【完】
jackwuwei 2003-12-28
  • 打赏
  • 举报
回复
给个邮箱,给你发过去。
doubhua 2003-12-28
  • 打赏
  • 举报
回复
下来我们创建真正的纹理。下面一行告诉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]);     // 选择纹理
doubhua 2003-12-28
  • 打赏
  • 举报
回复
下面是 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]);

19,469

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 图形处理/算法
社区管理员
  • 图形处理/算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧