OGL纹理对象(Texture)和优化纹理贴图

来源:互联网 发布:mac文稿无端占内存 编辑:程序博客网 时间:2024/06/06 05:50

OGL纹理贴图Texture

OGL纹理是图像位图像素数据的中间存储形式。
CPU需要解析图像格式文件,得到二维的图像数据imageData,在GPU中可以在缓存区中用CPU传递过来的图像数据imageData生成纹理对象,纹理对象不是编程中的对象其实是图像数据的结构体,GPU中的纹理可以是一维,二维,三维的。
texture is an OpenGL Object that contains one or more images that all have the same image format. A texture can be used in two ways. It can be the source of a texture access from a Shader, or it can be used as a render target.

关于mipmap,锁定视角2.5D的游戏不需要mipMap,全视角游戏需要mipMap。


纹理对象在GPU中的结构。
纹理就是矩形的数据数组,例如颜色数据,亮度数据,颜色和alpha数据,纹理数组中的单个元素就叫做纹理单元(texel),textureelement.纹理贴图可以直接应用于多边形表面,可以用于曲面上,也可以重复应用同一个纹理;最终颜色结果可以直接用纹理颜色,也可以和表面颜色进行混合。

纹理贴图之所以复杂,是因为纹理贴图可以不是颜色数据,例如高度图,法线贴图等;可以是一维,二维,三维;且矩形纹理 可以映射到非矩形区域,且必须以合理的方式实现,可以用Fragment操作复杂的组合(不同形式不同数量上)表现丰富的图像效果。

一个纹理可以应用于多个片断,纹理的插值模式。一个片断可以有多个纹理单元着色,叫多重纹理映射。
纹理应用到顶点上,可能要进行裁剪,拉伸,偏移,旋转。因为纹理存在比较多的计算,所以有很多的专用图像系统(包括硬件)对纹理贴图提供支持的原因

应用程序可以创建纹理对象,每个纹理对象都表示一个单独的纹理(并可能与mipmap相关联)。有些OGL实现可能支持一种特别的纹理对象工作集(working set),这些高性能的纹理对象称为常驻纹理对象,可以用OGL手动设置工作集和需要加入到其中的纹理对象,可以有效的提高性能。

在纹理着色处理之后,可以计算镜面亮点(根据光照的辅助颜色)。
纹理坐标必须在RGBA模式下才能使用。

更多纹理资料见:https://www.opengl.org/wiki/Texture

纹理对象和创建的流程

使用纹理对象目的是管理纹理在GPU中的存放格式,第二是在GPU中存储纹理(如果支持有限的高性能纹理工作集)重用纹理,避免每次drawcall都要从CPU端glTexImage*()加载巨大的纹理数据到GPU端。

关于创建纹理对象,需要先在GPU中获得一个纹理标识符。
然后绑定纹理加载纹理数据和纹理状态设置。
再绑定纹理,draw call绘制多边形应用纹理对象。

初次绑定时候会创建一个新的纹理对象,并把纹理图像和纹理属性设置为默认值后面的glTexImage*(),glTexSubImage*(), glCopyImage*(), glCopyTexSubImage*(), glTexParameter*()和glPrioritizeTextures函数调用,将真正的分配内存到纹理对象中,并把纹理数据存储到纹理内存中,纹理对象可以包含一副纹理图像以及相关的mipmap图像如果有,并包含相关的数据,例如宽度,高度,边框宽度,内部格式,成分的分辨率和纹理属性,被保存的纹理属性包括缩小和放大过滤器,环绕模式,边框颜色和纹理优先级。当一个纹理对象以后再次绑定时,它的数据就成为当前的纹理状态(以前绑定到当前的纹理对象的状态被替换,当前的纹理对象是一个全局的当前纹理指针)。
因为只是一个纹理,就没有必要设置纹理优先级,也没必要管理纹理对象工作集。也没有包括纹理边框、mipmap或立方图纹理等高级技巧。

1.创建纹理对象标识符

glGenTextures(1, &texName);
// target参数可以是GL_TEXTURE_1D,GL_TEXTURE_2D,GL_TEXTURE_3D或GL_TEXTURE_CUBE_MAP立方体纹理(6个面每个面一张二维纹理)
glBindTexture(GL_TEXTURE_2D, texName);

2.设置纹理采样插值参数,传输数据到GPU中且定义纹理对象

//#define GL_CLAMP 0x2900
//#define GL_REPEAT 0x2901
// 参数设置
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

// 传输数据到纹理贴图中,定义创建一个纹理对象
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, checkImageWidth, checkImageHeight,
0, GL_RGBA, GL_UNSIGNED_BYTE, checkImage);
// GL_TEXTURE_1D_ARRAY; GL_PROXY_TEXTURE_1D_ARRAY;在 OGL 3.0以上才可用

也可以设置片断取纹理元的方式,其实就是有mipmap时候对当前使用的层级纹理的选择的一个加权值了。
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);//GL_DECAL
//构建mipmap纹理栈,可以用glGenerateMipmap, OGL 3.0以前的版本,使用glTexParameter*()把GL_GENERATE_MIPMAP设置为GL_TRUE来创建mipmap栈

3.存储纹理对象在GPU纹理工作集中(常驻纹理)

如果OGL实现支持高性能纹理工作集,检查下是否有足够的空间容纳纹理对象,如果空间不足,可以为各个纹理对象建立优先级,把最常用的纹理对象保存在这个工作集中。
判断是否是常驻纹理
想判断一个当前纹理是否为常驻纹理可以用以下函数。一般情况下这些OGL实现具有专门的硬件来执行纹理操作,并提供有限的硬件缓存来存储纹理对象。这种情况下应该使用纹理对象,因为可以把许多纹理加载到这个工作集中,并对他们加以控制。实际效果显示Intel OGL3.1返回GL_TRUE , AMD OGL 4.2 返回GL_TRUE,应该是AMDOGL 4.2对纹理进行的更加多的优化放置在了GPU某个位置中。
  GLint resident;
   glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_RESIDENT, &resident);
   GLboolean texState[2];
   glAreTexturesResident(2, texName, texState);
调整纹理常驻策略
如果是视频游戏或视觉模拟程序,那么性能要求很高,应该所有纹理都使用常驻纹理,如果并不拥有足够的纹理内存,可能需要减少所使用的纹理图像的大小,分辨率以及mipmap层数量。或者可以使用glTexSubImg*()函数反复的复用同一块纹理内存(之前的会不再使用情况下),这个技巧比从头创建新纹理要快得多。
对于无法避免需要随时创建纹理图像的应用程序,无法避免使用非常驻纹理,那么需要调整纹理的常驻优先级。一般OGL实现是基于LRU最近最少使用原则来移出纹理工作集的,这种情况下为所有纹理分配相同的优先级即可。如果OGL不是LRU策略,或者不清楚,那么自己使用一次该纹理增加一下该纹理的优先级即可,一定时间后全部减少优先级避免整数溢出。
需要绑定的,设置单个纹理优先级的函数:
glTexParameteriv(target, GL_TEXTURE_PRIORITY, value);
不需要绑定的,设置多个纹理优先级的函数:
glPrioritizeTextures (GLsizei n, const GLuint *textures, const GLclampf *priorities);

4.绘制物体时候指定纹理坐标,激活绑定纹理贴图,绘制物体

glTexCoord2f(0.0, 0.0); glVertex3f(1.0, -1.0, 0.0);
glTexCoord2f(0.0, 1.0); glVertex3f(1.0, 1.0, 0.0);
glTexCoord2f(1.0, 1.0); glVertex3f(2.41421, 1.0, -1.41421);
glTexCoord2f(1.0, 0.0); glVertex3f(2.41421, -1.0, -1.41421);

glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texName);
// draw call
glFlush();
glDisable(GL_TEXTURE_2D);// 绘制完关闭

5.清楚纹理对象

绑定或解除绑定纹理对象时,它们保存的数据存放在纹理资源的某个位置中。如果纹理资源有限,删除纹理显然是释放纹理资源的有效方法。
glDeleteTextures(GLsizei n, const GLuint *textureNames);参数对应于glGenTextures。

实例

#include <GL/glut.h>#include <stdlib.h>#include <stdio.h>/*Create checkerboard texture*/#definecheckImageWidth 64#definecheckImageHeight 64static GLubyte checkImage[checkImageHeight][checkImageWidth][4];#ifdef GL_VERSION_1_1static GLuint texName;#endifvoid makeCheckImage(void){   int i, j, c;       for (i = 0; i < checkImageHeight; i++) {      for (j = 0; j < checkImageWidth; j++) {         c = ((((i&0x8)==0)^((j&0x8))==0))*255;         checkImage[i][j][0] = (GLubyte) c;         checkImage[i][j][1] = (GLubyte) c;         checkImage[i][j][2] = (GLubyte) c;         checkImage[i][j][3] = (GLubyte) 255;      }   }}void init(void){       glClearColor (0.0, 0.0, 0.0, 0.0);   glShadeModel(GL_FLAT);   glEnable(GL_DEPTH_TEST);   makeCheckImage();   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);#ifdef GL_VERSION_1_1   glGenTextures(1, &texName);   glBindTexture(GL_TEXTURE_2D, texName);#endif   // 设置纹理参数,着色,过滤插值方式   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);#ifdef GL_VERSION_1_1   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, checkImageWidth, checkImageHeight,                 0, GL_RGBA, GL_UNSIGNED_BYTE, checkImage);#else   //根据指定的参数,生成一个2D纹理,纹理对象标示符是texName   glTexImage2D(GL_TEXTURE_2D, 0, 4, checkImageWidth, checkImageHeight,                 0, GL_RGBA, GL_UNSIGNED_BYTE, checkImage);#endif}void display(void){   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);   glEnable(GL_TEXTURE_2D);   // 片断取纹理颜色的方式   glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);//GL_DECAL#ifdef GL_VERSION_1_1   glBindTexture(GL_TEXTURE_2D, texName);#endif   glBegin(GL_QUADS);   glTexCoord2f(0.0, 0.0); glVertex3f(-2.0, -1.0, 0.0);   glTexCoord2f(0.0, 1.0); glVertex3f(-2.0, 1.0, 0.0);   glTexCoord2f(1.0, 1.0); glVertex3f(0.0, 1.0, 0.0);   glTexCoord2f(1.0, 0.0); glVertex3f(0.0, -1.0, 0.0);   glTexCoord2f(0.0, 0.0); glVertex3f(1.0, -1.0, 0.0);   glTexCoord2f(0.0, 1.0); glVertex3f(1.0, 1.0, 0.0);   glTexCoord2f(1.0, 1.0); glVertex3f(2.41421, 1.0, -1.41421);   glTexCoord2f(1.0, 0.0); glVertex3f(2.41421, -1.0, -1.41421);   glEnd();   glFlush();   glDisable(GL_TEXTURE_2D);}void reshape(int w, int h){   glViewport(0, 0, (GLsizei) w, (GLsizei) h);   glMatrixMode(GL_PROJECTION);   glLoadIdentity();   gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0, 30.0);   glMatrixMode(GL_MODELVIEW);   glLoadIdentity();   glTranslatef(0.0, 0.0, -3.6);}void keyboard (unsigned char key, int x, int y){   switch (key) {      case 27:         exit(0);         break;      default:         break;   }}int main(int argc, char** argv){   glutInit(&argc, argv);   glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);   glutInitWindowSize(250, 250);   glutInitWindowPosition(100, 100);   glutCreateWindow(argv[0]);   init();   glutDisplayFunc(display);   glutReshapeFunc(reshape);   glutKeyboardFunc(keyboard);   glutMainLoop();   return 0; }

0 0