OpenGL纹理简介

1、了解纹理
  • 图像的存储空间 = 图片width * 图片height * 每个像素的字节数
  • OpenGL纹理文件是.tga文件,.tga特点是一个字节一个字节排列起来的,不会有多余的空间浪费。压缩图片.png和.jpeg也可以当做纹理使用,系统会把压缩图片还原成位图供纹理使用,位图每一个像素点都会有其颜色空间。
2、纹理常用API
  1. 关于像素存储方式
void glPixelStorei(GLenum pname, GLint param); //改变像素存储方式
void glPixelStoref(GLenum pname, GLint param); //恢复像素存储方式
//例子
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
/*
参数1:指定OpenGL如何从数据缓存区解包图像数据,
GL_UNPACK_ALIGNMENT指内存中每个像素行起点的排列请求,允许设置为1(byte排列)、2(排列为偶数byte的行)、4(字word排列)、8(行从双字节边界开始)
参数2:表示参数GL_UNPACK_ALIGNMENT设置的值
*/

  1. 从颜色缓冲区读取纹理文件.tga
void glReadPixels(GLint x, GLint y, GLSizei width, GLSizei height, GLenum format, GLenum type, const void *pixels);
/*
参数1/2:x/y,矩形左下角的窗口座标
参数3/4:width/height,矩形的宽高,以像素为单位
参数5:format,OpenGL的像素格式,如RGBA
参数6:type,参数pixels指向的数据,读取出来的颜色分量,像素数据的数据类型
参数7:pixels,指向图形数据的指针
*/
glReadBuffer(mode); //指定读取的缓存,从缓冲区读取
glWriteBuffer(mode); //指定写入的缓存,写入到缓存区
  1. 载入纹理图像
void glTexImage1D(GLenum target, GLint level, GLint internalformat, GLSizei width, GLint border, GLenum format, GLenum type, const void *data);
void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLSizei width, GLSizei height, GLint border, GLenum format, GLenum type, const void *data);
void glTexImage3D(GLenum target, GLint level, GLint internalformat, GLSizei width, GLSizei height, GLSizei depth, GLint border, GLenum format, GLenum type, const void *data);
/*
常用的是第二个二维纹理函数
参数1:target,GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
参数2:level,指定所加载的mip贴图层次,一般设为0
参数3:internalformat,每个纹理单元中存储多少颜色成分
参数4:width/height/depth,纹理的宽、高、深度,值设置为2的整数次方
参数5:border,为纹理贴图指定一个边框
参数6:format/type/data,同上一个函数
*/
  1. 更新纹理
void glTexSubImage1D(GLenum target, GLint level, GLint xOffset, GLSizei width, GLenum format, GLenum type, const void *data);
void glTexSubImage2D(GLenum target, GLint level,  GLint xOffset,  GLint yOffset, GLSizei width, GLSizei height, GLenum format, GLenum type, const void *data);
void glTexSubImage3D(GLenum target, GLint level, GLint xOffset,  GLint yOffset, GLint zOffset, GLSizei width, GLSizei height, GLSizei depth, GLenum format, GLenum type, const void *data);
  1. 插入替换纹理
void glCopyTexSubImage1D(GLenum target, GLint level, GLint xOffset, GLint x, GLSizei width);
void glCopyTexSubImage2D(GLenum target, GLint level,  GLint xOffset,  GLint yOffset, GLint x, GLint y, GLSizei width, GLSizei height);
void glCopyTexSubImage3D(GLenum target, GLint level, GLint xOffset,  GLint yOffset, GLint zOffset, GLint x, GLint y, GLint z,  GLSizei width, GLSizei height, GLSizei depth);
  1. copy纹理,从颜色缓冲区加载数据
void glCopyTexImage1D(GLenum target, GLint level, GLint internalformat, GLint x, GLSizei width, GLint border);
void glCopyTexImage2D(GLenum target, GLint level,  GLint internalformat, GLint x, GLint y, GLSizei width, GLSizei height, GLint border);
/*
x,y,在颜色缓冲区中指定了开始读取纹理数据的位置;
缓存区里的数据,是源缓存区通过glReadBuffer设置的。
***不存在glCopyTexImage3D,因为无法从2D颜色缓存区中获取体积数据***
*/
  1. 纹理对象
//使用函数分配纹理对象,分配对象标识符
//指定纹理对象的数量和指针(指针指向一个无符号整型数据,由纹理对象标识符填充)
void glGenTextures(GLsizei n, GLuint *textures);

//绑定纹理状态
void glBindTexture(GLenum target, GLuint texture);
/*
参数1:target,GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
参数2:texture,需要绑定的纹理对象
*/

//删除绑定纹理对象
//纹理对象以及纹理对象指针
void glDeleteTextures(GLsizei n, GLuint *textures);

//测试纹理对象是否有效
GLboolean glIsTexture(GLuint texture);
/*如果texture是一个已经分配空间的纹理对象,那么这个函数会返回GL_TRUE,否则会返回GL_FALSE*/
  1. 设置纹理参数
glTexParameterf(GLenum target, GLenum pname, GLFloat param);
glTexParameteri(GLenum target, GLenum pname, GLint param);
glTexParameterfv(GLenum target, GLenum pname, GLFloat *param);
glTexParameteriv(GLenum target, GLenum pname, GLFloat *param);
/*
参数1:target,指定这些参数用在哪个纹理模式上,比如GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
参数2:pname,指定需要设置哪个纹理参数,设置纹理属性
参数3:param,设定特定的纹理参数的值,属性的值
*/
  • 过滤方式:
    邻近过滤(GL_NEAREST):座标点最近的颜色
    线性过滤(GL_LINEAR):颜色混合
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); //建议纹理缩小时,使用邻近过滤
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //建议纹理放大时,使用线性过滤
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
  • 环绕方式:

GL_REPEAT,默认,重复纹理图像
GL_MIRRORED_REPEAT,重复纹理图像,每次重复图片是镜像放置的
GL_CLAMP_TO_EDGE,纹理座标会被约束在0-1之间,超出的部分会重复纹理座标的边缘,产生一种边缘被拉伸的效果
GL_CLAMP_TO_BORDER,超出的座标为用户指定的边缘颜色

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAR_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAR_T, GL_CLAMP_TO_EDGE);
/*
参数1:GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
参数2:GL_TEXTURE_WRAR_S、GL_TEXTURE_WRAR_T、GL_TEXTURE_WRAR_R,对应s、t、r座标
参数3:GL_REPEAT(纹理座标超过1.0的方向上对纹理进行重复)、
      GL_CLAMP(所需的纹理单元取自纹理边界或TEXTURE_BORDER_COLOR)、
      GL_CLAMP_TO_EDGE(强制对范围之外的纹理座标沿着合法的纹理单元的最后一行或最后一列进行采样)、
      GL_CLAMP_TO_BORDER(纹理座标在0.0到1.0范围之外的只使用边界纹理单元。边界纹理单元是作为围绕基本图像的额外的行和列,并与基本纹理图像一起加载)
*/
3、使用纹理的流程
  1. 读取纹理文件
void glReadPixels(GLint x, GLint y, GLSizei width, GLSizei height, GLenum format, GLenum type, const void *pixels);
  1. 载入纹理
void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLSizei width, GLSizei height, GLint border, GLenum format, GLenum type, const void *data);
  1. 生成纹理对象
void glGenTextures(GLsizei n, GLuint *textures);
void glBindTexture(GLenum target, GLuint texture);
void glDeleteTextures(GLsizei n, GLuint *textures);
GLboolean glIsTexture(GLuint texture);
  1. 设置纹理相关参数
  • 设置过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  • 设置x轴和y轴上的环绕方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAR_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAR_T, GL_CLAMP_TO_EDGE);
4、纹理座标
  1. 纹理座标图解


  • 一般设置左上角座标为(0,0),相当于上图顺时针旋转90°
  • 纹理座标的对应可以更换顺序,但是不能交叉
    以下是纹理填充例子:
  • 立体图形可以使用三维纹理,也可以每个面都使用二维纹理填充,常使用给每个面填充的方式
  1. 金字塔图形座标解析:
    金字塔底部四边形 = 三角形X + 三角形Y


  • 各个顶点座标
    vBackLeft (-1.0,-1.0,-1.0)
    vBackRight (1.0,-1.0,-1.0)
    vFrontLeft (-1.0,-1.0,1.0)
    vFrontRight(1.0,-1.0,1.0)
    vApex (0,1.0,0)
  • 三⻆形X的座标如下:
    vBackLeft(-1.0,-1.0,-1.0)
    vBackRight(1.0,-1.0,-1.0)
    vFrontRight(1.0,-1.0,1.0)
  • 三⻆形Y的座标如下:
    vFrontLeft(-1.0,-1.0,1.0)
    vBackLeft(-1.0,-1.0,-1.0)
    vFrontRight(1.0,-1.0,1.0)
  • 三⻆形X的2D纹理座标如下:
    vBackLeft(0.0,0.0,0.0)
    vBackRight(0.0,1.0,0.0)
    vFrontRight(0.0,1.0,1.0)
  • 三⻆形Y的2D纹理座标如下:
    vFrontLeft(0.0,0.0,1.0)
    vBackLeft(0.0,0.0,0.0)
    vFrontRight(0.0,1.0,1.0)
5、Mip贴图(多级渐远纹理)
  1. 介绍:
  • (节⾃<OpenGL 编程指南第9版 >—第6章纹理与帧缓存)


Mip贴图是一种功能强大的纹理技巧,可以提高渲染性能改善场景的显示质量。
当有很多很多个远近不同的物体需要纹理渲染时,其实远处的物体纹理拥有与近处物体同样高的分辨率,但是远处物体可能只产生很少的片段,OpenGL需要从高分辨率纹理中为这个片段跨过纹理很大一部分片段只拾取一个纹理颜色,这是很困难的。小物体上会产生不真实的感觉,同时使用高分辨率纹理也很浪费内存。


Mip贴图只有在缩小过滤时才会使用

Mip纹理是由一系列的纹理图像组成,每个图像大小在每个轴的方向上都减小一半,或者是原来图像像素总和的四分之一。Mip贴图每个图像大小都依次减半,知道最后一个图像大小是1*1的纹理单元为止。


  1. 设置Mip贴图
//设置mip贴图基层 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_BASE_LEVEL,0); 
//设置mip贴图最大层 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAX_LEVEL,0);
/*
level参数:指定图像数据用于哪个Mip层,第一层是0,后面以此类推。如果Mip贴图未使用,那么只有第0层才会被加载。默认情况下,为了能使用Mip贴图,所有的Mip层都要加载。GL_TEXTURE_BASE_LEVEL和GL_TEXTURE_MAX_LEVEL设置需要使用的基层和最大层。使用GL_TEXTURE_MIN_LOD和GL_TEXTURE_MAX_LOD参数限制已加载的Mip层的使用范围。
*/
  1. 什么时候生成Mip贴图

只有minFilter(缩小过滤)等于以下四种模式才可以生成Mip贴图
· GL_NEAREST_MIPMAP_NEAREST,具有非常好的性能,并且闪烁现象非常弱
· GL_LINEAR_MIPMAP_NEAREST,常常用于对游戏进行加速,它使用了高质量的线性过滤器
· GL_LINEAR_MIPMAP_LINEAR和GL_NEAREST_MIPMAP_LINEAR,在Mip层之间执行了一些额外的插值,以消除他们之间的过滤痕迹
· GL_LINEAR_MIPMAP_LINEAR,三线性Mip贴图,纹理过滤的黄金准则,具有最高的精度

if(minFilter == GL_LINEAR_MIPMAP_LINEAR ||
         minFilter == GL_LINEAR_MIPMAP_NEAREST ||
         minFilter == GL_NEAREST_MIPMAP_LINEAR ||
         minFilter == GL_NEAREST_MIPMAP_NEAREST)
//􏰗􏰘􏰤􏰥纹理生成所有的Mip􏴝层
//􏰠􏰌􏵙参数:GL_TEXTURE_1D􏶰GL_TEXTURE_2D􏶰GL_TEXTURE_3D glGenerateMipmap(GL_TEXTURE_2D);

/*
void glGenerateMipmap􏶳(GLenum target);
glGenerateMipmap函数目的:为纹理对象生成一组完整的mipmap

参数target:指定将生成的mipmap的纹理对象绑定到活动纹理单元的纹理目标,GL_TEXTURE_1D􏶰GL_TEXTURE_2D􏶰GL_TEXTURE_3D 

描述:glGenerateMipmap计算从零级数组派生的一组完整的mipmap数组。无论先前的内容如何,最多包括1*1维度纹理图像的数组级别都将替换为派生数组。零级纹理图像保持不变(原图)。派生的mipmap数组的内部格式都与零级纹理图像的内部格式相匹配。通过将零级纹理图像的宽和高减半来计算派生数组的尺寸,然后将每个阵列级别的尺寸减半,直到达到1*1尺寸的纹理图像。
*/
  1. Mip贴图过滤


tips:多级渐远纹理主要使用在纹理被缩小的情况下,纹理放大不会使用多级渐远纹理,如果使用会产生一个GL_INVALID_ENUM错误代码并且没有任何效果

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); //GL_TEXTURE_MIN_FILTER缩小过滤器,GL_NEAREST最邻近过滤

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //GL_LINEAR线性过滤

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPM AP_NEAREST); //GL_NEAREST_MIPMAP_ NEAREST选择最邻近的Mip层,并执行最邻近过滤

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPM AP_LINEAR); //GL_NEAREST_MIPMAP_ LINEAR在Mip层之间执行线性插补,并执行最邻近过滤

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMA P_NEAREST); //GL_LINEAR_MIPMA P_NEAREST在Mip层之间执行线性插补,并执行线性过滤,又称为三线性过滤
6、各向异性过滤
  • 介绍

各向异性纹理过滤(Anisotropic texture filtering)并不是OpenGL核心规范中的一部分,但它是一种得到广泛使用的扩展,可以极大提高纹理过滤操作的质量。
当一个纹理贴图被过滤时,OpenGL使用纹理座标来判断一个特定的几何片段将落在纹理什么地方,然后紧邻这个位置的纹理单元使用GL_NEAREST和GL_LINEAR过滤操作进行采样。
处理纹理过滤时,考虑了观察角度的过滤方法叫做各向异性过滤。

  • 纹理采样



    各向同性采样:观察方向和观察点垂直时对纹理进行采样;
    各向异性采样:以一定角度倾斜地观察几何图形,对周围纹理单元进行常规采样,这样会导致一些纹理信息丢失,图像看上去模糊。

为了更加逼真和准确的采样,应该沿着包含纹理的平面方向进行延伸。在Mip贴图纹理过滤模型中,或者其他所有的基本纹理过滤都可以应用各向异性过滤。

  • 应用各向异性过滤,2个步骤
//第一,查询得到支持的各向异性过滤的最大数量
GLfloat flargest;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT,&fLargest); 

//第二,设置各向异性过滤
//设置纹理参数(各向异性采样)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAX_ANISOTROPY_EXT,fLargest) 􏷻

//回归同性过滤,设置各向同性过滤,数量为1.0表示各向同性采样
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);

注意:各向异性过滤所应用的数量fLargest越大,沿着最大变化方向(沿着最强的观察点)所采样的纹理单元就越多。值1.0表示常规的纹理过滤(各向同性过滤)。
各向异性过滤会增加额外的工作,包括其他纹理单元。很可能对性能造成影响,但是在现代硬件上,应用这个特性对速度造成影响不大。
最重要的是,目前它已经成为流行游戏、动画和模拟程序的一个标准特性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章