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设置的值
*/
OpenGL像素格式

像素数据的数据类型
  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


    金字塔5个顶点坐标
  • 各个顶点坐标
    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纹理坐标如下(第一个0是纹理,后面的表示坐标(s,t)):
    vBackLeft(0,0.0,0.0)
    vBackRight(0,1.0,0.0)
    vFrontRight(0,1.0,1.0)
  • 三⻆形Y的2D纹理坐标如下:
    vFrontLeft(0,0.0,1.0)
    vBackLeft(0,0.0,0.0)
    vFrontRight(0,1.0,1.0)
5、Mip贴图(多级渐远纹理)
  1. 介绍:
  • (节⾃<OpenGL 编程指南第9版 >—第6章纹理与帧缓存)


    Mip贴图介绍1
Mip贴图介绍2
Mip贴图介绍3

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


多级渐远纹理

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

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


Mip贴图
  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贴图过滤


    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表示常规的纹理过滤(各向同性过滤)。
各向异性过滤会增加额外的工作,包括其他纹理单元。很可能对性能造成影响,但是在现代硬件上,应用这个特性对速度造成影响不大。
最重要的是,目前它已经成为流行游戏、动画和模拟程序的一个标准特性。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容

  • 1 纹理基础 纹理是一种结构化的存储形式(Textures are a structured form of st...
    RichardJieChen阅读 15,733评论 0 9
  • 本文首发于个人博客:Lam's Blog - 【OpenGL-ES】二维纹理,文章由MarkDown语法编写,可能...
    格子林ll阅读 3,758评论 0 9
  • 纹理的基础知识 2D 纹理 2d纹理是OpenGlES中最基础和普遍的一种纹理结构。一个2d纹理,就是图片的数据的...
    Zsj_Sky阅读 5,387评论 0 8
  • 一、纹理基础 3D图形渲染中最基本的操作就是对一个表面应用纹理。纹理可以表现只从网格的几何形状无法得到的附加细节。...
    cain_huang阅读 8,731评论 0 7
  • 写在前面的话 现实生活中,纹理最通常的作用是装饰我们的物体模型,它就像是贴纸一样贴在物体表面,使得物体表面拥有图案...
    猿基地阅读 26,439评论 0 36