OGL纹理之多维纹理/纹理数组/压缩纹理图像/drawcall优化

来源:互联网 发布:阿里云ecs翻墙 编辑:程序博客网 时间:2024/06/06 15:35

一维纹理

有时候用一维纹理就够了,例如带纹理的镶条。一维纹理就是高度为1的二维纹理图像,并且它的顶部和底部没有边框,左右可以有。所有二维纹理和子纹理所定义的函数都存在二维版本。如果有图像处理子集卷积操作,那么也会受影响,其它像素操作也会对纹理产生影响。

1.定义一维纹理函数:

glTexImage1D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
width大小是2^(m+b),m是任意整数,b是边框0或2.
可以提供mipmap level,纹理代理,和使用相同的过滤操作。
pixels是一维数组。

2.替换一维纹理的部分或全部

glTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels);

3.从帧缓存区创建一个新纹理:

glCopyTexImage1D (GLenum target, GLint level, GLenum internalFormat, GLint x, GLint y, GLsizei width, GLint border);

4.从帧缓存区替换一个现有纹理的部分或全部:

glCopyTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width);

这些参数含义都和二维纹理的操作一样,只是heiht是1不用设置,width大小是2^(m+b),m是任意整数,b是边框0或2.这些一维纹理的操作都会受到glPixelStore*()和glPixelTransfer*()以及其它像素传输操作所设置模式的影响。

三维纹理

三维纹理主要应用于医学和地球科学领域的渲染,例如断层计算成像系统(CT)或核磁共振成像(MRI),岩石地层建模。三维纹理是一大类应用范畴的一部分,称为体渲染(volume rendering)。有些高级体渲染程序需要处理体纹理单元(voxel)。三维纹理可以看做由一层层的二维纹理图像矩形构成,在内存中,这些矩形按顺序排列在一起。图像处理子集并不存在三维卷积,但是二维卷积过滤器也可以作用于三维纹理图像。
绝大多数二维纹理和子纹理定义函数都具有对应的三维版本。

1.定义三维纹理函数:

glTexImage3D(GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels);
定义一个三维纹理或一个二维纹理数组,所有参数都和glTexImage2D有相同的意义。不过pixels是一个三维数组,增加了三维纹理的depth值,OGL2.0以下depth必须是2^(m+b),m是任意整数,b是边框0或2,OGL2.0后 没有这个限制。可以提供mipmap level,纹理代理,和使用相同的过滤操作。
实例:
#define iWidth 16
#define iHeight 16
#define iDepth 16
static GLubyte image[iDepth][iHeight][iWidth][3];
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB, iWidth, iHeight,
iDepth, 0, GL_RGB, GL_UNSIGNED_BYTE, image);

2.替换一维纹理的部分或全部

glTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels);
pixels是一个三维纹理数组。

3.从帧缓存区替换一个现有纹理的部分或全部:

glCopyTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height);
从GL_READ_BUFFER帧缓存读取。
由于帧缓存中读取的是二维图像,所以帧缓存的x,y开始,width height大小的图像数据只是能够替换3D纹理(volume texture)的位于xoffset,yoffset,zoffset指定的zoffset那片的纹理图像。
这些一维纹理的操作都会受到glPixelStore*()和glPixelTransfer*()以及其它像素传输操作所设置模式的影响。
三维纹理的像素存储模式,因为引入了第三维度,所以如果需要访问纹理的不同层,就要*SKIP_IMAGES类型的参数n传递给glTexImage3D,glTexSubImage3D参数使用表示生成或替换第n层开始的纹理图像,含义是跳过nx每层的字节数,图像数据毕竟还是内存数据都符合内存数据操作。
*IMAGE_HEIGHT是一个像素存储参数,定义了三维纹理其中一层的高度, 如果*IMAGE_HEIGHT的值为0,那么每个二维纹理图像的行数都是height。

纹理数组

纹理对象需要在OGL GPU中更新,每次都调用glBindTexture,可能会影响到应用程序的性能(因此,这可能是纹理存储资源的一个短处)。纹理数组允许把一维或二维纹理的一个集合组织起来,所有的纹理都具有相同的大小,都位于更高维度的一个纹理之中(二维是三维纹理的一部分)。直接用三维纹理会导致定位索引纹理太消耗性能,且在三维纹理中的两片纹理之间进行过滤会得到异常的过滤效果, 纹理数组允许在通过索引访问的纹理中进行合适的mipmap过滤,得到比较好的效果所以用纹理数组定位会比较快,在纹理数组中进行二维纹理之间进行合适的过滤mipmap,是有优势的。
具体使用:类似手动创建mipmap一样使用就可以了。

压缩纹理图像

纹理图像在GPU 内存中可以用一种压缩格式存储,以减少它所使用的内存数量。纹理图像可以在加载时进行压缩,也可以直接以一种压缩格式加载, 例如pvr格式( 但pvr更加优秀,体积小,加载速度快,不需要转换为RGBA格式,不使用CPU内存,ios应该对pvr格式图片在硬件层做了优化)。

在加载时候进行压缩

当创建纹理,或进行像素存储模式或像素传输模式设置时候把internalFormat参数设置为其中一种GL_COMPRESSED_*枚举值,加载到GPU纹理内存中就是以压缩形式存储的。
查询纹理是否被压缩或压缩成的形式:
GLboolean compressed;
GLenum textureFormat;
GLsizei imageSize;
glGetTexLevelParameteriv(GL_TEXTURE_2D, GL_TEXTURE_COMPRESSED, &compressed);
if (compressed)
{
glGetTexLevelParameteriv(GL_TEXTURE_2D, GL_TEXTURE_INTERNAL_FORMAT, &textureFormat);
glGetTexLevelParameteriv(GL_TEXTURE_2D, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &imageSize);
}

加载经过压缩的纹理图像

每种OGL实现,都可以指定一组OGL拓展,实现一种特定的纹理压缩格式。因此需要验证下当前使用的OGL实现支持的纹理压缩格式。
为了加载一个压缩格式存储的纹理,可以使用glCompressedTexImage*()函数。
void glCompressedTexImage1D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const void *data);
void glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data);
void glCompressedTexImage3D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void *data);
internalformat必须是OGL实现支持的压缩格式之一,否则会报GL_INVALID_ENUM错误。
width,height,depth是和未压缩的参数大小一样。
OGL3.1中,glCompressedTexImage2D target是GL_TEXTURE_RECTANGLE或GL_PROXY_TEXTURE_RECTANGLE会产生GL_INVALID_ENUM错误。
另外就像未压缩的纹理一样,可以替换一个已经加载的纹理的全部或一部分。
void glCompressedTexSubImage1D(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const void *data);
void glCompressedTexSubImage2 (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data);
void glCompressedTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const void *data);

图像draw call优化

小图合并为大图,或使用纹理数组,都可以有效的在图像像素层减少draw call。顶点层,应该也是进行mesh的合并,将所有静态的物体合并为一个大的mesh,经过一次相同的顶点运算MVP,对于不同顶点运算的尽量合并为相同的顶点运算来减少draw call。


0 0