void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid * data);
此函数式完成纹理ID申请后数据绑定功能。这个函数的第一个输入参数的意思是指定texture object的类型,可以是GL_TEXTURE_2D,又或者是cubemap texture6面中的其中一面,通过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, or GL_TEXTURE_CUBE_MAP_NEGATIVE_Z来指定,我们说了cubemap的texture其实也就是由6个2D texture组成的,所以这个函数实际上是用于给一张2D texture赋值。刚才我们已经说过了,在bindTexture之后,对texture object的操作,需要先active对应的纹理单元,然后再指定texture target来确定是哪个纹理,而不再通过buffer object name了(除非是对buffer object进行删除),由于GPU中同一时间一个thread的一个context中只能有一个纹理单元是处于被使用状态,而一个纹理单元最多只能有一个2D texture和一个cubemap的texture,所以在active了对应的纹理单元之后,在这里通过target指定我们是操作2D texture还是cubemap texture的哪一面,就能精确的指定到我们实际操作的是哪个texture object。如果传入其他的参数,就会报INVALID_ENUM的错误。第二个是指给该texture的第几层赋值。上个课时我们也简单的介绍过,没有mipmap的texture,就相当于只有一层mipmap,而有mipmap的texture就好比一层一层塔一样,每一层都需要赋值。所以在这里需要确认我们是给纹理的第几层赋值,绝大多数情况是给第一层赋值,因为即使纹理需要mipmap,我们也经常会使用glGenerateMipmap这个API去生成mipmap信息,而不直接赋值。glGenerateMipmap这个API一会我们再说。mipmap又称LOD,level 0就是第一层mipmap,也就是图像的基本层。如果level小于0,则会出现GL_INVALID_VALUE的错误。而且level也不能太大,因为texture是由最大尺寸限制的,而第一层mipmap就是纹理的原始尺寸,而第二层mipmap的尺寸为原始宽高各除以2,依次类推,最后一层mipmap的尺寸为宽高均为1。所以如果level超过了log2(max),则会出现GL_INVALID_VALUE的错误。这里的max,当target为GL_TEXTURE_2D的时候,指的是GL_MAX_TEXTURE_SIZE,而当target为其他情况的时候,指的是GL_MAX_CUBE_MAP_TEXTURE_SIZE。这里的GL_MAX_TEXTURE_SIZE和GL_MAX_CUBE_MAP_TEXTURE_SIZE,都可以通过glGet这个API获取到。而且还有,假如图像的宽或者高不是2的幂,那么有个专业术语叫做NPOT,non power of two。在OpenGL ES2.0中NPOT的texture是不支持mipmap的,所以针对NPOT的texture,如果level大于0,也就会出现GL_INVALID_VALUE的错误。第三个参数internalformat,第七个参数format和第八个参数type我们放在一起来说,就是指定原始数据在从CPU传入GPU之前,在CPU中的格式信息,以及传入GPU之后,在GPU中的格式。internalformat,是用于指定纹理在GPU端的格式,只能是GL_ALPHA, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA。GL_ALPHA指的是每个像素点只有alpha通道,相当于RGB通道全为0。GL_LUMINANCE指的是每个像素点只有一个luminance值,相当于RGB的值全为luminance的值,alpha为1。GL_LUMINANCE_ALPHA指的是每个像素点有一个luminance值和一个alpha值,相当于RGB的值全为luminance的值,alpha值保持不变。GL_RGB指的是每个像素点有一个red、一个green值和一个blue值,相当于RGB的值保持不变,alpha为1。GL_RGBA指的是每个像素点有一个red、一个green值、一个blue值和一个alpha值,相当于RGBA的值都保持不变。如果internalformat是其他值,则会出现GL_INVALID_VALUE的错误。第七个参数format和第八个参数type,用于指定将会生成的纹理在所需要的信息在CPU中的存储格式,其中format指定通道信息,只能是GL_ALPHA, GL_RGB, GL_RGBA, GL_LUMINANCE, and GL_LUMINANCE_ALPHA。type指的每个通道的位数以及按照什么方式保存,到时候读取数据的时候是以byte还是以short来进行读取。只能是GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_4_4_4_4, and GL_UNSIGNED_SHORT_5_5_5_1。当type为GL_UNSIGNED_BYTE的时候,每一个byte都保存的是一个颜色通道中的值,当type为GL_UNSIGNED_SHORT_5_6_5, GL_UNSIGNED_SHORT_4_4_4_4, and GL_UNSIGNED_SHORT_5_5_5_1的时候,每个short值中将包含了一个像素点的所有颜色信息,也就是包含了所有的颜色通道的值。从CPU往GPU传输数据生成纹理的时候,会将这些格式的信息转成float值,方法是比如byte,那么就把值除以255,比如GL_UNSIGNED_SHORT_5_6_5,就把red和blue值除以31,green值除以63,然后再全部clamp到闭区间[0,1],设计这种type使得绿色更加精确,是因为人类的视觉系统对绿色更敏感。而type为GL_UNSIGNED_SHORT_5_5_5_1使得只有1位存储透明信息,使得每个像素要么透明要么不透明,这种格式比较适合字体,这样可以使得颜色通道有更高的精度。如果format和type不是这些值,那么就会出现GL_INVALID_ENUM的错误。
同样的format在OpenGL ES2.0中,将对应相同的internalformat,比如format GL_RGBA就对应着internalformat GL_RGBA,format GL_ALPHA就对应着internalformat GL_ALPHA,这里一共有5种format,也对应着5种internalformat,分别是GL_RGBA,GL_RGB,GL_ALPHA,GL_LUMINANCE,GL_LUMINANCE_ALPHA。internalformat和format需要一一对应,而且确定了internalformat和format之后,type的选择也受到了限制,比如针对internalformat和format为GL_RGB的时候,type只能是GL_UNSIGNED_SHORT_5_6_5或者GL_UNSIGNED_BYTE。而internalformat和format为GL_ALPHA的时候,type只能是GL_UNSIGNED_BYTE。internal format、format和type必须要对应着使用。
第四个参数width和第五个参数height就是原始图片的宽和高,也是新生成纹理的宽和高。因为两者是一样的,图片信息以数据的形式从CPU传到GPU,可能每个像素点格式和包含的信息会发生变化,但是图片的大小,也就是像素点的数量,每行多少个像素点,一共多少行,这个信息是不会发生变化的,这里我们说的像素点其实在纹理的相关知识中还有一个专业术语叫做纹理像素texels,简称纹素。width和height不能小于0,也不能当target为GL_TEXTURE_2D的时候,超过GL_MAX_TEXTURE_SIZE,或者当target为其他情况的时候,超过GL_MAX_CUBE_MAP_TEXTURE_SIZE,否则,就会出现GL_INVALID_VALUE的错误。第6个参数border,代表着纹理是否有边线,在这里必须写成0,也就是没有边线,如果写成其他值,则会出现GL_INVALID_VALUE的错误。最后一个输入参数也是上面准备好的信息,意思是:data是CPU中一块指向保存实际数据的内存。如果data不为null,那么将会有width*height个像素的data从CPU端的data location开始读取,然后会被从CPU端传输并且更新格式保存到GPU端的texture object中。当然,从CPU读取数据的时候要遵守刚才glPixelStorei设置的对齐规则。其中第一个数据对应的是纹理中左下角那个顶点。然后第二个数据对应的是纹理最下面一行左边第二个点,依次类推,按照从左到右的顺序,然后一行完毕,从下往上再赋值下一行的顺序,一直到最后一个数据对应纹理中右上角那个顶点。如果data为null,那么执行完这个API之后,依然会给texture object分配可以保存width*height那么多像素信息的内存,但是没有对这块内存进行初始化,如果使用这个texture去绘制到图片上,那么绘制出来的颜色值为undefine。可以通过glTexSubImage2D给这块没有初始化的内存赋值。
这个函数没有输出参数,但是有以下几种情况会出错,除了刚才说的那些参数输入错误之外,还有如果target是CubeMap texture的一个面,但是width和height不相同,则会出现GL_INVALID_VALUE的错误。如果format与internalformat不匹配,或者type与format不匹配(比如type为GL_UNSIGNED_SHORT_5_6_5 但是format 不是GL_RGB,或者type 是 GL_UNSIGNED_SHORT_4_4_4_4 或者 GL_UNSIGNED_SHORT_5_5_5_1 而 format 不是 GL_RGBA),则会出现GL_INVALID_OPERATION的错误。
总结一下,这个命令的输入为CPU内存中以某种方式保存的像素数据,转变成闭区间[0,1]的浮点型RGBA像素值,保存在GPU中的texture object内。
一旦该命令被执行,会立即将图像像素数据从CPU传输到GPU的内存中,后续对客户端数据的修改不会影响服务器中的texture object相关信息。所以在这个API执行之后,客户端中的图像数据就可以被删掉了。
如果一个texture object中已经包含有内容了,那么依然可以使用glTexImage2D对这个texture object中的内容进行替换。
网友评论