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

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

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

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

  1. int APIENTRY gluBuild2DMipmaps (
  2. GLenum target,
  3. GLint components,
  4. GLint width,
  5. GLint height,
  6. GLenum format,
  7. GLenum type,
  8. 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贴图模式加载纹理,使用键盘上下方向键控制在隧道内前进和后退,使用左右方向键控制视角选择变换:

  1. #define WindowWidth 400
  2. #define WindowHeight 400
  3. #define WindowTitle "OpenGL Mip纹理贴图"
  4. #include <Windows.h>
  5. #include <freeglut.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. //定义纹理对象编号
  9. GLuint texGround;
  10. GLuint texWall;
  11. GLuint texSky;
  12. #define BMP_Header_Length 54 //图像数据在内存块中的偏移量
  13. static GLfloat angle = 0.0f; //旋转角度
  14. static GLfloat zPosition=10;
  15. // 函数power_of_two用于判断一个整数是不是2的整数次幂
  16. int power_of_two(int n)
  17. {
  18. if( n <= 0 )
  19. return 0;
  20. return (n & (n-1)) == 0;
  21. }
  22. /* 函数load_texture
  23. * 读取一个BMP文件作为纹理
  24. * 如果失败,返回0,如果成功,返回纹理编号
  25. */
  26. GLuint load_texture(const char* file_name)
  27. {
  28. GLint width, height, total_bytes;
  29. GLubyte* pixels = 0;
  30. GLuint last_texture_ID=0, texture_ID = 0;
  31. // 打开文件,如果失败,返回
  32. FILE* pFile = fopen(file_name, "rb");
  33. if( pFile == 0 )
  34. return 0;
  35. // 读取文件中图象的宽度和高度
  36. fseek(pFile, 0x0012, SEEK_SET);
  37. fread(&width, 4, 1, pFile);
  38. fread(&height, 4, 1, pFile);
  39. fseek(pFile, BMP_Header_Length, SEEK_SET);
  40. // 计算每行像素所占字节数,并根据此数据计算总像素字节数
  41. {
  42. GLint line_bytes = width * 3;
  43. while( line_bytes % 4 != 0 )
  44. ++line_bytes;
  45. total_bytes = line_bytes * height;
  46. }
  47. // 根据总像素字节数分配内存
  48. pixels = (GLubyte*)malloc(total_bytes);
  49. if( pixels == 0 )
  50. {
  51. fclose(pFile);
  52. return 0;
  53. }
  54. // 读取像素数据
  55. if( fread(pixels, total_bytes, 1, pFile) <= 0 )
  56. {
  57. free(pixels);
  58. fclose(pFile);
  59. return 0;
  60. }
  61. // 对就旧版本的兼容,如果图象的宽度和高度不是的整数次方,则需要进行缩放
  62. // 若图像宽高超过了OpenGL规定的最大值,也缩放
  63. {
  64. GLint max;
  65. glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);
  66. if( !power_of_two(width)
  67. || !power_of_two(height)
  68. || width > max
  69. || height > max )
  70. {
  71. const GLint new_width = 256;
  72. const GLint new_height = 256; // 规定缩放后新的大小为边长的正方形
  73. GLint new_line_bytes, new_total_bytes;
  74. GLubyte* new_pixels = 0;
  75. // 计算每行需要的字节数和总字节数
  76. new_line_bytes = new_width * 3;
  77. while( new_line_bytes % 4 != 0 )
  78. ++new_line_bytes;
  79. new_total_bytes = new_line_bytes * new_height;
  80. // 分配内存
  81. new_pixels = (GLubyte*)malloc(new_total_bytes);
  82. if( new_pixels == 0 )
  83. {
  84. free(pixels);
  85. fclose(pFile);
  86. return 0;
  87. }
  88. // 进行像素缩放
  89. gluScaleImage(GL_RGB,
  90. width, height, GL_UNSIGNED_BYTE, pixels,
  91. new_width, new_height, GL_UNSIGNED_BYTE, new_pixels);
  92. // 释放原来的像素数据,把pixels指向新的像素数据,并重新设置width和height
  93. free(pixels);
  94. pixels = new_pixels;
  95. width = new_width;
  96. height = new_height;
  97. }
  98. }
  99. // 分配一个新的纹理编号
  100. glGenTextures(1, &texture_ID);
  101. if( texture_ID == 0 )
  102. {
  103. free(pixels);
  104. fclose(pFile);
  105. return 0;
  106. }
  107. // 绑定新的纹理,载入纹理并设置纹理参数
  108. // 在绑定前,先获得原来绑定的纹理编号,以便在最后进行恢复
  109. GLint lastTextureID=last_texture_ID;
  110. glGetIntegerv(GL_TEXTURE_BINDING_2D, &lastTextureID);
  111. glBindTexture(GL_TEXTURE_2D, texture_ID);
  112. /*glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  113. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); */
  114. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
  115. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
  116. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  117. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  118. glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
  119. /* glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
  120. GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels); */
  121. gluBuild2DMipmaps(GL_TEXTURE_2D,3,width,height,GL_BGR_EXT,GL_UNSIGNED_BYTE,pixels);
  122. glBindTexture(GL_TEXTURE_2D, lastTextureID); //恢复之前的纹理绑定
  123. free(pixels);
  124. return texture_ID;
  125. }
  126. void Display(void)
  127. {
  128. // 清除屏幕
  129. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  130. // 设置视角
  131. glMatrixMode(GL_PROJECTION);
  132. glLoadIdentity();
  133. gluPerspective(65, 1, 1, 100);
  134. glMatrixMode(GL_MODELVIEW);
  135. glLoadIdentity();
  136. gluLookAt(0, 0,zPosition, 0, 0, 0, 0, 1, 0);
  137. glRotatef(angle, 0.0f, 1.0f, 0.0f); //旋转
  138. // 绘制左侧墙壁以及纹理
  139. glBindTexture(GL_TEXTURE_2D, texWall);
  140. glBegin(GL_QUADS);
  141. glTexCoord2f(0.0f, 0.0f); glVertex3f(-5.0f, -5.0f, 100.0f);
  142. glTexCoord2f(30.0f, 0.0f); glVertex3f(-5.0f, -5.0f, -100.0f);
  143. glTexCoord2f(30.0f, 2.0f); glVertex3f(-5.0f, 5.0f, -100.0f);
  144. glTexCoord2f(0.0f, 2.0f); glVertex3f(-5.0f, 5.0f, 100.0f);
  145. glEnd();
  146. //绘制右侧墙
  147. glBegin(GL_QUADS);
  148. glTexCoord2f(0.0f, 0.0f); glVertex3f(5.0f, -5.0f, 100.0f);
  149. glTexCoord2f(30.0f, 0.0f); glVertex3f(5.0f, -5.0f, -100.0f);
  150. glTexCoord2f(30.0f, 2.0f); glVertex3f(5.0f, 5.0f, -100.0f);
  151. glTexCoord2f(0.0f, 2.0f); glVertex3f(5.0f, 5.0f, 100.0f);
  152. glEnd();
  153. //绘制地板
  154. glBindTexture(GL_TEXTURE_2D, texGround);
  155. glBegin(GL_QUADS);
  156. glTexCoord2f(0.0f, 0.0f); glVertex3f(-5.0f, -5.0f, 100.0f);
  157. glTexCoord2f(0.0f, 1.0f); glVertex3f(5.0f, -5.0f, 100.0f);
  158. glTexCoord2f(25.0f, 1.0f); glVertex3f(5.0f, -5.0f, -100.0f);
  159. glTexCoord2f(25.0f, 0.0f); glVertex3f(-5.0f, -5.0f, -100.0f);
  160. glEnd();
  161. //绘制顶层
  162. glBindTexture(GL_TEXTURE_2D, texSky);
  163. glBegin(GL_QUADS);
  164. glTexCoord2f(0.0f, 0.0f); glVertex3f(-5.0f, 5.0f, 100.0f);
  165. glTexCoord2f(0.0f, 3.0f); glVertex3f(5.0f, 5.0f, 100.0f);
  166. glTexCoord2f(35.0f, 3.0f); glVertex3f(5.0f, 5.0f, -100.0f);
  167. glTexCoord2f(35.0f, 0.0f); glVertex3f(-5.0f, 5.0f, -100.0f);
  168. glEnd();
  169. glutSwapBuffers();
  170. }
  171. void SpecialKey(GLint key,GLint x,GLint y)
  172. {
  173. if(key==GLUT_KEY_UP)
  174. {
  175. zPosition+=1.0f;
  176. }
  177. if(key==GLUT_KEY_DOWN)
  178. {
  179. zPosition-=1.0f;
  180. }
  181. if(key==GLUT_KEY_LEFT)
  182. {
  183. angle+=0.5f;
  184. }
  185. if(key==GLUT_KEY_RIGHT)
  186. {
  187. angle-=0.5f;
  188. }
  189. glutPostRedisplay();
  190. }
  191. int main(int argc, char* argv[])
  192. {
  193. // GLUT初始化
  194. glutInit(&argc, argv);
  195. glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
  196. glutInitWindowPosition(100, 100);
  197. glutInitWindowSize(WindowWidth, WindowHeight);
  198. glutCreateWindow(WindowTitle);
  199. glEnable(GL_DEPTH_TEST);
  200. glEnable(GL_TEXTURE_2D); // 启用纹理
  201. texGround = load_texture("ground.bmp"); //加载纹理
  202. texWall = load_texture("wall.bmp");
  203. texSky=load_texture("sky.bmp");
  204. glutDisplayFunc(&Display); //回调函数
  205. glutSpecialFunc(&SpecialKey);
  206. glutMainLoop(); //循环调用
  207. return 0;
  208. }



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

OpenGL(二十二) gluBuild2DMipmaps 加载Mip纹理贴图的更多相关文章

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

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

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

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

  3. Android Volley完全解析(二),使用Volley加载网络图片

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17482165 在上一篇文章中,我们了解了Volley到底是什么,以及它的基本用法. ...

  4. JAVA基础知识总结:一到二十二全部总结

    >一: 一.软件开发的常识 1.什么是软件? 一系列按照特定顺序组织起来的计算机数据或者指令 常见的软件: 系统软件:Windows\Mac OS \Linux 应用软件:QQ,一系列的播放器( ...

  5. FreeSql (二十二)Dto 映射查询

    适合喜欢使用 dto 的朋友,很多时候 entity 与 dto 属性名相同,属性数据又不完全一致. 有的人先查回所有字段数据,再使用 AutoMapper 映射. 我们的功能是先映射,再只查询映射好 ...

  6. Senparc.Weixin.MP SDK 微信公众平台开发教程(二十二):如何安装 Nuget(dll) 后使用项目源代码调试

    最近碰到开发者问:我使用 nuget 安装了 Senparc.Weixin SDK,但是有一些已经封装好的过程想要调试,我又不想直接附加源代码项目,这样就没有办法同步更新了,我应该怎么办? 这其实是一 ...

  7. Bootstrap入门(二十二)组件16:列表组

    Bootstrap入门(二十二)组件16:列表组 列表组是灵活又强大的组件,不仅能用于显示一组简单的元素,还能用于复杂的定制的内容. 1.默认样式列表组 2.加入徽章 3.链接 4.禁用的列表组 5. ...

  8. JAVA之旅(二十二)——Map概述,子类对象特点,共性方法,keySet,entrySet,Map小练习

    JAVA之旅(二十二)--Map概述,子类对象特点,共性方法,keySet,entrySet,Map小练习 继续坚持下去吧,各位骚年们! 事实上,我们的数据结构,只剩下这个Map的知识点了,平时开发中 ...

  9. 使用Typescript重构axios(二十二)——请求取消功能:收尾

    0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...

随机推荐

  1. Fragment的基本应用

    转载请注明出处:http://blog.csdn.net/crazy1235/article/details/50933621 Fragment 还是先来基本介绍. Fragment –> 片段 ...

  2. cocos2d-x游戏开发 跑酷(九) 源代码下载及小结

    这个东西零零碎碎写了一个礼拜吧. 事实上也没多少东西在里面.文章后附下载地址 博客地址:http://blog.csdn.net/dawn_moon 由于我没用过chipmunk,并且它是面向过程的东 ...

  3. 使用ng-content进行组件内容投射

    原文 https://www.jianshu.com/p/c0a39b1776c0 大纲 1.认识内容投射 2.一个简单组件 3.简单投射 4.针对性投射 5.ngProjectAs 6.代码资源 认 ...

  4. ZOJ Special AC String 水

    http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=3702 题目大意: 对于给定的一个字符串,满足如下要求输出AC,否则WA(好吧我 ...

  5. ds finder 唤醒

    http://www.hangge.com/blog/cache/detail_594.html

  6. 【27.85%】【codeforces 743D】Chloe and pleasant prizes

    time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...

  7. [Http] Understand what an HTTP Request is

    Let's look at several HTTP requests to learn the basic structure of these messages, and how the vari ...

  8. [Java][Spring]Spring事务不起作用 问题汇总

    [Java][Spring]Spring事务不起作用 问题汇总 http://blog.csdn.net/szwangdf/article/details/41516239

  9. ios开发瀑布流框架的封装

    一:瀑布流框架封装的实现思路:此瀑布流框架的封装仿照tableView的底层实现,1:每个cell的frame的设置都是找出每列的最大y值,比较每列的最大y值,将下一个cell放在最大y值最小的那一列 ...

  10. Colder框架硬核更新(Sharding+IOC)

    目录 引言 控制反转 读写分离分库分表 理论基础 设计目标 现状调研 设计思路 实现之过五关斩六将 动态对象 动态模型缓存 数据源移植 查询表达式树深度移植 数据合并算法 事务支持 实际使用 展望未来 ...