OpenGL ES 纹理

一个像素点的颜色信息,一般需要4个通道,RGBA,每个通道需要8位,用于保存一个0-255的值。所以想要表示一个像素点的颜色信息,如果不压缩的情况下需要32bit,也就是4byte
缩小包体通常会缩小纹理对应的原始图片文件的大小,通常压缩为png、jpg、tga格式,而不使用bmp这种无压缩的格式

生成纹理:从原始图片获取宽高和颜色信息及保存信息的空间(cpu端的内存),以该内存的数据在gpu端生成纹理
cpu的数据是有几行就有几组,每行占据的字节根据一行宽度及每个像素占的位数相乘获得。在读取时要注意字节对齐,否则会越界。例如图片宽度为15,每个像素3字节,这一行45字节,只能被1整除。若以8字节读取,会越界。所以使用纹理尽量使用POT,即2的幂次方作为宽度。

void glPixelStorei(GLenum pname, GLint param);

设置像素的存储模式,可理解为设置读写像素的对齐规则
第一个参数只能是GL_UNPACK_ALIGNMENT或者GL_PACK_ALIGNMENT。将客户端颜色数据传输至GL服务端为解包unpack,将服务器像素读取到客户端为打包pack。GL_UNPACK_ALIGNMENT是数据从CPU端解包的对齐规则,GL_PACK_ALIGNMENT是数据GPU读取的对齐规则
第二个参数默认为4,可设置1、2、4、8

texture对象与buffer对象类似,都是GPU的一块buffer
glGenBuffers、glBindBuffer、glBufferData、glBufferSubData、glDeleteBuffers与glGenTextures、glBindTexture、glTexImage2D、glTexSubImage2D和glDeleteTexture功能类似

void glGenTextures(GLsizei n, GLuint * textures);

创建name,未真正创建object,绑定后创建
第一个参数:生成texture object name的数量
第二个参数:保存生成的texture object name(0被预留,生成数一定大于0)

void glBindTexture(GLenum target, GLuint texture);

第一个参数:指定texture对象类型,取值GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D对应2D texture,GL_TEXTURE_CUBE_MAP对应cubemap texture(组成立方体的纹理即6张2D texture)
第二个参数为:纹理的名称,并且,该纹理的名称在当前的应用中不能被再次使用。输入0为解绑当前使用的纹理对象。
GPU同一时间一个线程的一个context只能有一个纹理单元(存放纹理,一个纹理单元一个类型的纹理最多只能有一个,即最多只能有2个纹理,因为纹理2个类型)处于active状态。texture object被纹理单元使用,而纹理单元被GPU使用,所以glBindTexture,只是将texture提交给了纹理单元,若该纹理单元不再被使用,则纹理也不再被GPU使用。默认的纹理单元是GL_TEXTURE0,通过glActiveTexture切换使用的纹理单元
因此若传入的纹理已绑定旧的纹理单元,将不再属于旧的纹理单元,与新的当前GPU正使用的纹理单元绑定。已经创建好的纹理单元不能修改类型,即已绑定2D texture的无法与cubemap绑定。
一个纹理对象可以放在多个纹理单元中,任何对该对象的操作,会影响到所有与其关联的纹理对象

void glActiveTexture(GLenum texture);

指定某个纹理单元设置为被使用状态
第一个参数是GL_TEXTURE0、GL_TEXTURE1。。。到GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS -1。(GPU中最多8个纹理,每个GPU不同,通过glGet传入GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS获取最大纹理数)。初始GL_TEXTURE0是active的。

纹理在shader只是个普通的sample变量(uniform变量),可传入多个uniform,因此一次绘制可使用多个纹理。即通过glActiveTexture依次将纹理单元active,然后各单元传入纹理对象,再把纹理单元通过glUniform传入shader。shader便可使用多个纹理。
在shader中一个纹理单元只能使用其中的一个纹理。若将纹理单元传给sample2D,则shader只能使用该单元的2D texture,若传给sampleCube,则shader只能使用Cubemap texture

Mipmap是把纹理按照2的倍数进行缩放,直到图像为1x1的大小,然后把这些图都存储起来,当要使用的就选择一个合适的图像,会增加额外的内存,可解决闪烁(屏幕上被渲染物体与所应用纹理图像相比非常小时)和性能问题(加载大量纹理,还要进行过滤处理/缩小)

void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid * data);

将CPU的保存的图像数据,转变为闭区间[0,1]的float型RGBA值,保存至GPU的纹理对象中。

target:texture对象类型。GL_TEXTURE_2D或着GL_TEXTURE_CUBE_MAP_POSITIVE_X、
GL_TEXTURE_CUBE_MAP_NEGATIVE_X、GL_TEXTURE_CUBE_MAP_POSITIVE_Y、GL_TEXTURE_CUBE_MAP_NEGATIVE_Y、GL_TEXTURE_CUBE_MAP_POSITIVE_Z、GL_TEXTURE_CUBE_MAP_NEGATIVE_Z指定cubemap texture的其中一面
level:表示多级分辨率的纹理图像的级数即mipmap的层级,从0开始,1,2,3递增,给texture的第几层赋值(mipmap指的是平面纹理的清晰程度。等级越高,越清晰。没有mipmap的texture相当于只有一层mipmap,有mipmap的texture即多层,每层都需赋值。第一层mipmap为纹理原始尺寸,第二层为原始尺寸除2,以此类推,最后一层宽高均为1)若只有一种分辨率,则level设为0。就是给第一层赋值。如果宽高不是2 的幂,则不支持mipmap,level只能为0
internalformat:指定纹理在GPU端的格式,只能为GL_ALPHA(每个像素点只有alpha通道,即RGB通道都为0), GL_LUMINANCE(每个像素点只有一个luminance值,即RGB都为luminance值,alpha为1), GL_LUMINANCE_ALPHA(每个像素点有一个luminance值和一个alpha值,即RGB为luminance,alpha值不变), GL_RGB(RGB值不变,alpha为1), GL_RGBA(RGBA值都不变)
format:指定通道信息,GL_ALPHA, GL_RGB, GL_RGBA, GL_LUMINANCE, 和GL_LUMINANCE_ALPHA,与internalformat需一一对应
type:指每个通道的位数及保存方式,以byte还是short读取,只能是GL_UNSIGNED_BYTE(每个byte保存一个颜色通道的值), GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_4_4_4_4, 和 GL_UNSIGNED_SHORT_5_5_5_1,后三者每个short值包含一个像素点所有颜色通道的值。(internalformat和format为GL_RGB的时候,type只能是GL_UNSIGNED_SHORT_5_6_5或者GL_UNSIGNED_BYTE。而internalformat和format为GL_ALPHA的时候,type只能是GL_UNSIGNED_BYTE,internalformat和format为GL_RGBA,type是GL_UNSIGNED_SHORT_4_4_4_4 或者 GL_UNSIGNED_SHORT_5_5_5_1 )

format、type指定生成纹理所需信息在CPU的存储格式,从CPU想GPU传输数据生成纹理时,会把byte/short转为float值,即byte除255

width、height:原始图片的宽高,即新生成纹理的宽高
border:纹理是否有边线,必须为0,即无边线
data:data是CPU中指向图像数据的指针。
会有widthheight个像素的数据从CPU端的data地址开始读取,若data值null,则会给GPU的texture对象分配可保存widthheight像素信息的内存,但未初始化,绘制的颜色为undefine

void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid * data);

与glTexImage2D,给纹理对象的部分传入数据,不会改变纹理对象的internalformat、width、height
xoffset,yoffset,width,height:以纹理对象为起点,宽度偏移xoffset,高度偏移yoffset的位置为左下角,宽度width高度height的内存进行赋值

纹理坐标的(0,0)点是左下角,修改纹理数据的时候是从左下角开始绘制的。
顺序先沿着X轴从左至右,再沿着Y轴从下至上,即一行一行像素的绘制。

void glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);

直接从绘制buffer中读取一块数据,根据绘制buffer的格式和目标纹理的格式进行转换,在GPU中生成一个纹理(例如截图,其实是从前一帧的绘制buffer读取信息,保存在纹理中,在下一帧,将该纹理绘制出来)
x,y,width,height:以绘制buffer的左下角进行宽度x偏移高度y偏移为起点,从宽width高height的内存取值,以生成宽width高height的纹理(若组成的方形超过绘制buffer的区域,则区域外读取到的值为undefine,若width和height为0,则创建一个null纹理)

void glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);

与glCopyTexImage2D类似
x,y:从绘制buffer获取的内存区域的左下角
xoffset,yoffset:写入纹理区域的左下角
width,height为公用,应为获取与写入的像素点一一对应

纹理坐标:uv坐标,指定顶点对应纹理中的点的位置,一个顶点做纹理中的坐标用(u,v)表示。用于在纹理中限定一块要显示到绘制buffer指定区域的区域坐标。
通过attribute将纹理坐标传入VS与顶点对应,光栅化时,会将uv坐标归一化生成st,取值为[0,1]。生成像素的顶点坐标,用插值smooth或非插值flat方法生成每个像素点的纹理坐标(颜色,深度等),使屏幕点可与纹理点对应,再将纹理已uniform sample2D/sampleCube形式传入PS,PS中通过纹理坐标对纹理采样,得到纹理对应颜色,映射到光栅化生成的像素点,对像素点着色

void glTexParameter*(GLenum target, GLenum pname, GLint param);

设置纹理属性(要修改一张纹理的属性,先要通过glActiveTexture,enable一个纹理单元,然后通过glBindTexture,把这个texture绑定到这个纹理单元上。然后保持这个纹理单元处于active的状态,再调用这个API,来修改指定纹理的属性)
第一个参数:GL_TEXTURE_2D或者GL_TEXTURE_CUBE_MAP,确定设置哪张纹理的属性(指定当前active的一张texture)
第二个参数:要设置的纹理属性
第三个参数:要设置的值
纹理属性:
GL_TEXTURE_MIN_FILTER:需将纹理缩小。(GL_NEAREST:临近采样,只在基层mip最邻近过滤。理论返回与纹理像素的中心最接近的纹理元素的值。
GL_LINEAR:线性采样,在基层mip过滤。返回最接近被纹理像素中心的四个纹理元素的加权平均值。(如映射左下角的点根据纹理左下角四个点的值计算)。
GL_NEAREST_MIPMAP_NEAREST:选择最接近映射图片大小的mipmap层,并使用GL_NEAREST标准。
GL_LINEAR_MIPMAP_NEAREST:选择最接近映射图片大小的mipmap层,并使用GL_LINEAR标准。
GL_NEAREST_MIPMAP_LINEAR:选择两张与映射图片大小最接近的mipmap层,从两层中用GL_NEAREST标准取像素信息,计算加权平均值。
GL_LINEAR_MIPMAP_LINEAR:选择两张与映射图片大小最接近的mipmap层,从两层中用GL_LINEAR标准取像素信息,计算加权平均值。)
GL_TEXTURE_MIN_FILTER对应的算法是GL_NEAREST_MIPMAP_LINEAR。
在这里插入图片描述

GL_TEXTURE_MAG_FILTER:需将纹理放大
(GL_NEAREST:临近采样。
GL_LINEAR:线性采样。)
GL_TEXTURE_MAG_FILTER对应的默认算法就是GL_LINEAR。
在这里插入图片描述
GL_TEXTURE_WRAP_S:需将纹理横向填充
(GL_CLAMP_TO_EDGE:假如s坐标超过[0,1],横向部分以纹理的边界值颜色填充。
GL_REPEAT:假如s坐标超过[0,1],横向部分对纹理图片复制填满。
GL_MIRRORED_REPEAT:假如s坐标超过[0,1],横向部分对纹理图片镜像复制填满)
GL_TEXTURE_WRAP_T:需将纹理纵向填充
(GL_TEXTURE_WRAP_S都是进行横向填充,GL_TEXTURE_WRAP_T则是进行纵向填充。GL_TEXTURE_WRAP_S和GL_TEXTURE_WRAP_T的默认算法都是GL_REPEAT。如果纹理为NPOT的话,但是GL_TEXTURE_WRAP_T、GL_TEXTURE_WRAP_S没有使用GL_CLAMP_TO_EDGE,也就相当于用了一张RGBA为(0,0,0,1)的纹理)

void glGenerateMipmap(GLenum target);

将各级mipmap自动生成,并根据0层计算出存储值(一般都是根据四个像素点的值算出一个像素点的值)。(glTexImage2D生成的纹理级数由开发者决定。一个纹理对象可能包含多个内存,mipmap的纹理对象便是。如宽高为3232若生成多级纹理,会多生成5块内存,1616,88,44,22,11)
输入参数:GL_TEXTURE_2D或者GL_TEXTURE_CUBE_MAP

当显示小图片时使用不一样的内容,mipmap需要通过glTexImage2D传入,而非glGenerateMipmap生成。

多级纹理大概会多占用1/3左右的内存

void glHint(GLenum target, GLenum mode);

设置mipmap数据生成方式
第一个参数输入GL_GENERATE_MIPMAP_HINT。
第二个参数输入mipmap数据生成算法 ,GL_DONT_CARE,GL_FASTEST和GL_NICEST

void glDeleteTextures(GLsizei n, const GLuint * textures);

删除n个纹理对象。
被删除的对象,名字也会被释放,可被glGenTextures重新使用。若被删的texture处于bind状态,则相当于先将texture关联的纹理单元active,执行glBindTexture输入0解绑

glGenTextures→glActiveTexture→glBindTexture→glTexParameter→glTexImage2D→glDeleteTextures

传统压缩图片jpg、png只是减小资源大小包体大小。把信息传给gpu时要解包,将压缩图片解压缩成GL_RGBA或GL_RGB等未压缩格式,在GPU所占内存是一样的。

纹理缓存
通过texturecache来创建和销毁texture2D对象
texture cache是单例,创建纹理时,会查看缓存中是否有对应纹理,有直接使用,没有创建,并保存在纹理缓存中。每次创建,texture引用计数加1,若创建n个相容纹理,纹理引用计数n+1,切换场景时清楚纹理,引用计数为n+1-1,即1。无论如何切换场景,纹理缓存为单例,不会release纹理。texture cache通过removeUnusedTextures函数remove纹理(将所有未被使用,引用计数为1的纹理release)。removeTexture和removeTextureForKey单独删除纹理(将引用计数-1,当计数清0时删除)

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值