当纹理被用于渲染一个面积比它本身小很多的对象时,会由于纹理图像的降采样率不足而导致混叠现象,主要的表现特征是纹理图像的闪烁,出现纹理躁动。特别是在场景远近移动变换时,这种闪烁情况更为明显,严重可能会影响到模型的视觉质量。一个纹理躁动的示例如下:

上图中可以看到,近处的场景渲染比较清晰,但是远处的纹理出现很多碎点,并且随着场景的远近变化,这种碎点闪烁现象也在无规律的躁动变化,影响视觉体验。

针对这种情况,可以采用Mip贴图模式。Mip贴图的原理是在加载纹理时加载本纹理图像在不同压缩尺度下的多幅纹理图像,从原始的纹理开始,依次降低纹理的宽高为上一个纹理的一半,直到最后纹理的面积为1*1为止。加载的一系列纹理图像类似于图像金字塔,在渲染上,OpenGL自动根据对象模型的状态加载不同等级的纹理对象。

实现Mip纹理贴图可以采用glu中的函数gluBuidl2DMipMaps来实现,函数原型是:

int APIENTRY gluBuild2DMipmaps (GLenum      target, GLint       components, GLint       width, GLint       height, GLenum      format, GLenum      type, const void  *data);

第一个参数target标明加载纹理的维度,这是使用GL_TEXTURE_2D;

第二个参数是颜色分量的组成,3和4分别代表RGB和RGBA;

其他参数与glTexImage2D函数保持一致。

另一点是Mip纹理贴图的过滤模式也有所不同,glTexParameteri中的参数GL_TEXTURE_MIN_FILTER和参数GL_TEXTURE_MAG_FILTER会受到过滤影响,下表列出了Mip贴图的纹理过滤模式:

如果采用选取前两个过滤,表示虽然加载了Mip贴图,但只在基础纹理图像上执行过滤,效果跟使用普通纹理一样,可选的一组过滤模式如:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);  
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);

示例程序使用Mip贴图模式加载纹理,使用键盘上下方向键控制在隧道内前进和后退,使用左右方向键控制视角选择变换:

#define WindowWidth  400
#define WindowHeight 400
#define WindowTitle  "OpenGL Mip纹理贴图"  #include <Windows.h>
#include <freeglut.h>
#include <stdio.h>
#include <stdlib.h>  //定义纹理对象编号
GLuint texGround;
GLuint texWall;
GLuint texSky;#define BMP_Header_Length 54  //图像数据在内存块中的偏移量
static GLfloat angle = 0.0f;   //旋转角度
static GLfloat zPosition=10;// 函数power_of_two用于判断一个整数是不是2的整数次幂
int power_of_two(int n)
{  if( n <= 0 )  return 0;  return (n & (n-1)) == 0;
}  /* 函数load_texture
* 读取一个BMP文件作为纹理
* 如果失败,返回0,如果成功,返回纹理编号
*/
GLuint load_texture(const char* file_name)
{  GLint width, height, total_bytes;  GLubyte* pixels = 0;  GLuint last_texture_ID=0, texture_ID = 0;  // 打开文件,如果失败,返回  FILE* pFile = fopen(file_name, "rb");  if( pFile == 0 )  return 0;  // 读取文件中图象的宽度和高度  fseek(pFile, 0x0012, SEEK_SET);  fread(&width, 4, 1, pFile);  fread(&height, 4, 1, pFile);  fseek(pFile, BMP_Header_Length, SEEK_SET);  // 计算每行像素所占字节数,并根据此数据计算总像素字节数  {  GLint line_bytes = width * 3;  while( line_bytes % 4 != 0 )  ++line_bytes;  total_bytes = line_bytes * height;  }  // 根据总像素字节数分配内存  pixels = (GLubyte*)malloc(total_bytes);  if( pixels == 0 )  {  fclose(pFile);  return 0;  }  // 读取像素数据  if( fread(pixels, total_bytes, 1, pFile) <= 0 )  {  free(pixels);  fclose(pFile);  return 0;  }  // 对就旧版本的兼容,如果图象的宽度和高度不是的整数次方,则需要进行缩放  // 若图像宽高超过了OpenGL规定的最大值,也缩放  {  GLint max;  glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);  if( !power_of_two(width)  || !power_of_two(height)  || width > max  || height > max )  {  const GLint new_width = 256;  const GLint new_height = 256; // 规定缩放后新的大小为边长的正方形  GLint new_line_bytes, new_total_bytes;  GLubyte* new_pixels = 0;  // 计算每行需要的字节数和总字节数  new_line_bytes = new_width * 3;  while( new_line_bytes % 4 != 0 )  ++new_line_bytes;  new_total_bytes = new_line_bytes * new_height;  // 分配内存  new_pixels = (GLubyte*)malloc(new_total_bytes);  if( new_pixels == 0 )  {  free(pixels);  fclose(pFile);  return 0;  }  // 进行像素缩放  gluScaleImage(GL_RGB,  width, height, GL_UNSIGNED_BYTE, pixels,  new_width, new_height, GL_UNSIGNED_BYTE, new_pixels);  // 释放原来的像素数据,把pixels指向新的像素数据,并重新设置width和height  free(pixels);  pixels = new_pixels;  width = new_width;  height = new_height;  }  }  // 分配一个新的纹理编号  glGenTextures(1, &texture_ID);  if( texture_ID == 0 )  {  free(pixels);  fclose(pFile);  return 0;  }  // 绑定新的纹理,载入纹理并设置纹理参数  // 在绑定前,先获得原来绑定的纹理编号,以便在最后进行恢复  GLint lastTextureID=last_texture_ID;  glGetIntegerv(GL_TEXTURE_BINDING_2D, &lastTextureID);  glBindTexture(GL_TEXTURE_2D, texture_ID);  /*glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);  glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);  /* glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,  GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels); */ gluBuild2DMipmaps(GL_TEXTURE_2D,3,width,height,GL_BGR_EXT,GL_UNSIGNED_BYTE,pixels);glBindTexture(GL_TEXTURE_2D, lastTextureID);  //恢复之前的纹理绑定  free(pixels);  return texture_ID;
}  void Display(void)
{  // 清除屏幕  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  // 设置视角  glMatrixMode(GL_PROJECTION);  glLoadIdentity();  gluPerspective(65, 1, 1, 100);  glMatrixMode(GL_MODELVIEW);  glLoadIdentity();  gluLookAt(0, 0,zPosition, 0, 0, 0, 0, 1, 0);  glRotatef(angle, 0.0f, 1.0f, 0.0f); //旋转  // 绘制左侧墙壁以及纹理  glBindTexture(GL_TEXTURE_2D, texWall);  glBegin(GL_QUADS);  glTexCoord2f(0.0f, 0.0f); glVertex3f(-5.0f, -5.0f, 100.0f);  glTexCoord2f(30.0f, 0.0f); glVertex3f(-5.0f, -5.0f, -100.0f);  glTexCoord2f(30.0f, 2.0f); glVertex3f(-5.0f, 5.0f, -100.0f);  glTexCoord2f(0.0f, 2.0f); glVertex3f(-5.0f, 5.0f, 100.0f);  glEnd(); //绘制右侧墙glBegin(GL_QUADS);  glTexCoord2f(0.0f, 0.0f); glVertex3f(5.0f, -5.0f, 100.0f);  glTexCoord2f(30.0f, 0.0f); glVertex3f(5.0f, -5.0f, -100.0f);  glTexCoord2f(30.0f, 2.0f); glVertex3f(5.0f, 5.0f, -100.0f);  glTexCoord2f(0.0f, 2.0f); glVertex3f(5.0f, 5.0f, 100.0f);  glEnd();  //绘制地板glBindTexture(GL_TEXTURE_2D, texGround); glBegin(GL_QUADS);  glTexCoord2f(0.0f, 0.0f); glVertex3f(-5.0f, -5.0f, 100.0f);  glTexCoord2f(0.0f, 1.0f); glVertex3f(5.0f, -5.0f, 100.0f);  glTexCoord2f(25.0f, 1.0f); glVertex3f(5.0f, -5.0f, -100.0f);  glTexCoord2f(25.0f, 0.0f); glVertex3f(-5.0f, -5.0f, -100.0f);  glEnd();  //绘制顶层glBindTexture(GL_TEXTURE_2D, texSky); glBegin(GL_QUADS);  glTexCoord2f(0.0f, 0.0f); glVertex3f(-5.0f, 5.0f, 100.0f);  glTexCoord2f(0.0f, 3.0f); glVertex3f(5.0f, 5.0f, 100.0f);  glTexCoord2f(35.0f, 3.0f); glVertex3f(5.0f, 5.0f, -100.0f);  glTexCoord2f(35.0f, 0.0f); glVertex3f(-5.0f, 5.0f, -100.0f);  glEnd();    glutSwapBuffers();
}  void SpecialKey(GLint key,GLint x,GLint y)
{if(key==GLUT_KEY_UP){zPosition+=1.0f;}if(key==GLUT_KEY_DOWN){zPosition-=1.0f;}if(key==GLUT_KEY_LEFT){angle+=0.5f;}if(key==GLUT_KEY_RIGHT){angle-=0.5f;}glutPostRedisplay();
}int main(int argc, char* argv[])
{  // GLUT初始化  glutInit(&argc, argv);  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);  glutInitWindowPosition(100, 100);  glutInitWindowSize(WindowWidth, WindowHeight);  glutCreateWindow(WindowTitle);    glEnable(GL_DEPTH_TEST);      glEnable(GL_TEXTURE_2D);    // 启用纹理  texGround = load_texture("ground.bmp");  //加载纹理  texWall = load_texture("wall.bmp");  texSky=load_texture("sky.bmp");glutDisplayFunc(&Display);   //回调函数  glutSpecialFunc(&SpecialKey);glutMainLoop(); //循环调用  return 0;
}  

远处的墙壁纹理没有了闪烁的变化,只不过显示出来比较模糊(实际显示质量要好一点),可以使用各向异性过滤缓解远处的模糊情况。

OpenGL(二十二) gluBuild2DMipmaps 加载Mip纹理贴图相关推荐

  1. Three.js加载简单纹理贴图并应用到网格(凹凸贴图、法向贴图、移位贴图)

    纹理贴图 简介 简单案例 结果 分析 完整代码 简介 纹理最基础的用法就是作为贴图被添加到材质上,当用这种方法创建网格时,网格的颜色就来源于纹理 UV贴图实质上就是指定模型上的哪一部分需要被映射到纹理 ...

  2. OpenGL教程翻译 第二十二课 使用Assimp加载模型

    第二十二课 使用Assimp加载模型 原文地址:http://ogldev.atspace.co.uk/(源码请从原文主页下载) 背景 到现在为止我们都在使用手动生成的模型.正如你所想的,指明每个顶点 ...

  3. 一位中科院自动化所博士毕业论文的致谢:二十二载风雨求学路,他把自己活成了光.........

    4月18日,中国科学院官方微博发布消息,披露了这篇论文为<人机交互式机器翻译方法研究与实现>,作者是2017年毕业于中国科学院大学的工学博士黄国平. 这篇论文中情感真挚的<致谢> ...

  4. Android4.0图库Gallery2代码分析(二) 数据管理和数据加载

    Android4.0图库Gallery2代码分析(二) 数据管理和数据加载 2012-09-07 11:19 8152人阅读 评论(12) 收藏 举报 代码分析android相册优化工作 Androi ...

  5. 读书二十二载,信念很简单,把书念下去,然后走出去,不枉活一世。

    ‍ ‍ 关注+星标公众号,不错过精彩内容 昨天看了一篇关于博士论文的致谢,看的我想哭.改变命运有很多条路,但是对于大多数人而言,读书是改变命运的捷径. 内容是讲述了自己20多年的求学经历:小时候煤油灯 ...

  6. jQuery-瀑布流-绝对定位布局(二)(延迟AJAX加载图片)

    jQuery-瀑布流-绝对定位布局(二)(延迟AJAX加载图片) 瀑布流-绝对定位布局,与浮动布局的区别在于 1.布局不一样: 绝对定位:一个UL里面放置所有的绝对定位的LI: 浮动布局:多个(一般三 ...

  7. OpenGL(十二)——Qt OpenGL绕着坐标轴旋转多边形

    OpenGL(十二)--Qt OpenGL绕着坐标轴旋转多边形 一.旋转多边形 前两篇介绍了如何绘制多边形,并且给多边形进行上色.本篇介绍如何旋转多边形. 多边形的旋转,在类中增加两个变量来控制这两个 ...

  8. pytorch入门(二):数据加载和处理

    pytorch入门(二):数据加载和处理 小引 数据加载 引包 数据集 编写辅助函数 显示图像及其特征点 定义数据集类 数据处理 组合变换 遍历数据集 其他注意事项 本章对应pytorch官方文档链接 ...

  9. Android 二次封装网络加载框架

    Android 二次封装网络加载框架 写在最前面 开发当中,在请求网络的时候,大家或多或少都会使用一些第三方框架,Android-Async-Http. Volley.XUtils.Okhttp.Re ...

最新文章

  1. http304缓存 php,通过http头设置http缓存
  2. linux学习笔记 第七篇 (samba(一))(iscsi)
  3. 物料自运过帐的模拟和修正
  4. Redis安装(CentOS7/tar.gz)
  5. python label位置_Python3 tkinter基础 Label pack 设置控件在窗体中的位置
  6. 自我总结 (三) --(Java Web学习)
  7. 锁屏快捷键_全面屏 iPhone 锁屏快捷键美化,让你的 iPhone 更特别
  8. vue 后端数据成功返回 页面不渲染_vue能获取到数据数据却渲染不到页面上,为什么?...
  9. eXpressApp Framework 8.1.5 released on 27 Jue 2008 and What'a New
  10. 智能优化算法:自私羊群优化算法-附代码
  11. linux 内核参数解释整理
  12. Linux_OpenSSH远程连接
  13. 网页设计图片向上浮动_CSS实现图片向上浮动
  14. VARCHART XGantt 甘特图中的日期和夏令时
  15. 颜色名称及色样表(HTML版)
  16. 海康线阵相机调试指导
  17. Python提取图片中的文字信息
  18. 片片青艾草,悠悠赤子心
  19. flutter dio网络请求 get post 图片上传
  20. 计算机操作系统原理课件,【】计算机操作系统原理与实例分析PPT课件(完整版 全)精品.ppt...

热门文章

  1. 本周大新闻|Meta AI部门重大重组,疑似Cambira CAD图曝光
  2. 科学计算机和普通计算器,[财经]普通计算器和科学计算器的区别是什么?手机计算器阵亡是怎么回事? - 南方财富网...
  3. linux进程管理 实现管道通信,Linux进程管理(二)管道通信 · lww’s Blog
  4. 计算机进制单位tb以上,计算机容量单位的换算 B,KB,MB,GB,TB
  5. 如何设置Word文档公式序号在右边
  6. python产品发布会_阅读虫师django开发发布会系统以及django开发Web接口分享
  7. 云网融合相关研究现状
  8. 学生项目统计表及计划书模板
  9. ts从入门到进阶—3-4var/let/const声明,解构,展开
  10. 最全的运营数据指标解读