OpenGL(二十二) gluBuild2DMipmaps 加载Mip纹理贴图
当纹理被用于渲染一个面积比它本身小很多的对象时,会由于纹理图像的降采样率不足而导致混叠现象,主要的表现特征是纹理图像的闪烁,出现纹理躁动。特别是在场景远近移动变换时,这种闪烁情况更为明显,严重可能会影响到模型的视觉质量。一个纹理躁动的示例如下:
上图中可以看到,近处的场景渲染比较清晰,但是远处的纹理出现很多碎点,并且随着场景的远近变化,这种碎点闪烁现象也在无规律的躁动变化,影响视觉体验。
针对这种情况,可以采用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纹理贴图的更多相关文章
- jQuery-瀑布流-绝对定位布局(二)(延迟AJAX加载图片)
jQuery-瀑布流-绝对定位布局(二)(延迟AJAX加载图片) 瀑布流-绝对定位布局,与浮动布局的区别在于 1.布局不一样: 绝对定位:一个UL里面放置所有的绝对定位的LI: 浮动布局:多个(一 ...
- Android4.0图库Gallery2代码分析(二) 数据管理和数据加载
Android4.0图库Gallery2代码分析(二) 数据管理和数据加载 2012-09-07 11:19 8152人阅读 评论(12) 收藏 举报 代码分析android相册优化工作 Androi ...
- Android Volley完全解析(二),使用Volley加载网络图片
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17482165 在上一篇文章中,我们了解了Volley到底是什么,以及它的基本用法. ...
- JAVA基础知识总结:一到二十二全部总结
>一: 一.软件开发的常识 1.什么是软件? 一系列按照特定顺序组织起来的计算机数据或者指令 常见的软件: 系统软件:Windows\Mac OS \Linux 应用软件:QQ,一系列的播放器( ...
- FreeSql (二十二)Dto 映射查询
适合喜欢使用 dto 的朋友,很多时候 entity 与 dto 属性名相同,属性数据又不完全一致. 有的人先查回所有字段数据,再使用 AutoMapper 映射. 我们的功能是先映射,再只查询映射好 ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(二十二):如何安装 Nuget(dll) 后使用项目源代码调试
最近碰到开发者问:我使用 nuget 安装了 Senparc.Weixin SDK,但是有一些已经封装好的过程想要调试,我又不想直接附加源代码项目,这样就没有办法同步更新了,我应该怎么办? 这其实是一 ...
- Bootstrap入门(二十二)组件16:列表组
Bootstrap入门(二十二)组件16:列表组 列表组是灵活又强大的组件,不仅能用于显示一组简单的元素,还能用于复杂的定制的内容. 1.默认样式列表组 2.加入徽章 3.链接 4.禁用的列表组 5. ...
- JAVA之旅(二十二)——Map概述,子类对象特点,共性方法,keySet,entrySet,Map小练习
JAVA之旅(二十二)--Map概述,子类对象特点,共性方法,keySet,entrySet,Map小练习 继续坚持下去吧,各位骚年们! 事实上,我们的数据结构,只剩下这个Map的知识点了,平时开发中 ...
- 使用Typescript重构axios(二十二)——请求取消功能:收尾
0. 系列文章 1.使用Typescript重构axios(一)--写在最前面 2.使用Typescript重构axios(二)--项目起手,跑通流程 3.使用Typescript重构axios(三) ...
随机推荐
- 解读Java中BigDecimal.ZERO.compareTo()的返回值含义
Java compareTo() 用法 例如: public static void main(String[] args) { BigDecimal bnum1, bnum2; bnum1 ...
- 与Eclipse关于"Call Hierarchy"和"Find Reference"功能比较
"Call Hierarchy"功能比较 Eclipse的"Call Hierarchy"可以查看一个Java方法或类成员变量的调用树(caller和calle ...
- 关于用strace工具定位vrrpd进程有时会挂死的bug
只做工作总结备忘之用. 正在烧镜像,稍总结一下进来改bug遇到的问题. 一个项目里要用到L3 switch的nat,vrrp功能,但实地测试中偶然出现write file挂死的情况,但不是必现.交付在 ...
- Vue.js开发环境搭建的介绍
包含了最基础的Vue.js的框架,包含了打包工具和测试工具,开发调试的最基本的服务器,不需要关注细节,只需关注Vuejs对项目的实现 npm在国内的网络使用较慢,所以推荐下载安装淘宝的镜像 1: 2: ...
- [转] Python 爬虫的工具列表 附Github代码下载链接
转自http://www.36dsj.com/archives/36417 这个列表包含与网页抓取和数据处理的Python库 网络 通用 urllib -网络库(stdlib). requests - ...
- Android UI:看看Google官方自定义带旋转动画的ImageView-----RotateImageView怎么写(附 图片淡入淡...)
众所周知,想要让ImageView旋转的话,可以用setRotation()让其围绕中心点旋转,但这个旋转是不带动画的,也就是旋转屏幕时图片噌的一下就转过去了,看不到旋转的过程,此UI体验不大好,为此 ...
- hibernate框架简单步骤
Demo.java package com.itheima.test; import org.hibernate.Session; import org.hibernate.SessionFactor ...
- hibernate框架配置文件
配置文件:和自己封装的工具类放在同一个包中 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE ...
- Uncaught SyntaxError: Unexpected end of input 解决办法
Unexpected end of input 的英文意思是"意外的终止输入" 他通常表示我们浏览器在读取我们的js代码时,碰到了不可预知的错误,导致浏览器 无语进行下面的读取 ...
- php标准库中QplQueue队列如何使用?
php标准库中QplQueue队列如何使用? 一.总结 1.new对象,然后通过enqueue方法和dequeue方法使用. 二.php标准库中QplQueue队列如何使用? 队列这种数据结构更简单, ...