OpenGL ES纹理贴图

转载自:http://seya.iteye.com/blog/532525

OpenGL可以把纹理映射到指定的图形的表面上。简单一点的,就是给平面映射纹理,比如一个四边形,一个长方体的6个面,都可以指定位图作为纹理映射到各个面上。 

关于将一个位图作为纹理映射到某个或者多个面上,可以学习Jeff Molofee的OpenGL系列教程。 

对于指定的多个纹理,要根据自己的需要映射到不同的面上,需要对位图创建一个数组,用来存储位图的名称,然后在初始化OpenGL的时候,可以读取这些位图,然后生成多个纹理存储到一个纹理数组中,接着就可以指定绘制的某个面,对该指定的面进行纹理映射。 

下面,在的Jeff Molofee教程的第六课的基础上,实现对6个面分别进行不同的纹理映射。 

准备工作就是制作6幅不同的位图,如图所示: 


关键代码及其说明如下。 

创建全局纹理数组 

GLuint texture[6];   // 创建一个全局的纹理数组,用来存储将位图转换之后得到的纹理,对应于立方体的6个面 


加载位图文件 

加载位图,也就是把位图读取到内存空间,实现纹理的创建,加载位图的函数说明一下: 

AUX_RGBImageRec *LoadBMP(char *Filename)    // 根据位图文件的名称进行加载 

FILE *File=NULL;         // 文件指针 

if (!Filename)          // 如果没有指定位图文件名称就返回NULL 

   return NULL;         


File=fopen(Filename,"r");       // 根据指定的位图文件名称,打开该位图文件 

if (File)           // 如果位图文件存在 

   fclose(File);         // 因为只是需要判断问题是否存在,而不需要对位图文件进行写操作,所以关闭位图文件 
   return auxDIBImageLoad(Filename);    // 其实,只需要一个真正存在的位图文件的名称,实现加载位图文件,并返回一个指针 


return NULL;          // 位图文件加载失败就返回NULL 


上面实现加载位图的函数中,AUX_RGBImageRec是glaux.h中定义的类型,该类型的定义如下所示: 

/* 
** RGB Image Structure 
*/ 

typedef struct _AUX_RGBImageRec { 
    GLint sizeX, sizeY; 
    unsigned char *data; 
} AUX_RGBImageRec; 

首先,AUX_RGBImageRec类型是一个RGB图像结构类型。该结构定义了三个成员: 

sizeX —— 图像的宽度; 
sizeY —— 图像的高度; 
data; —— 图形所包含的数据,其实也就是该图形在内存中的像素数据的一个指针。 

AUX_RGBImageRec类型的变量描述了一幅图像的特征。 

上述函数中,调用了glaux.h库文件中的auxDIBImageLoad函数,其实它是一个宏,函数原型为auxRGBImageLoadW(LPCWSTR)或者auxRGBImageLoadA(LPCSTR),可以在该库文件中找到它的定义,如下所示: 

/* AUX_RGBImageRec * APIENTRY auxRGBImageLoad(LPCTSTR); */ 
#ifdef UNICODE 
#define auxRGBImageLoad auxRGBImageLoadW 
#else 
#define auxRGBImageLoad auxRGBImageLoadA 
#endif 
AUX_RGBImageRec * APIENTRY auxRGBImageLoadA(LPCSTR); 
AUX_RGBImageRec * APIENTRY auxRGBImageLoadW(LPCWSTR); 

#ifdef UNICODE 
#define auxDIBImageLoad auxDIBImageLoadW 
#else 
#define auxDIBImageLoad auxDIBImageLoadA 
#endif 
AUX_RGBImageRec * APIENTRY auxDIBImageLoadA(LPCSTR); 
AUX_RGBImageRec * APIENTRY auxDIBImageLoadW(LPCWSTR); 

宏auxDIBImageLoad实现的功能就是:根据指定的位图名称,将该位图的信息加载到内存中,以便用来创建成为纹理。 

创建纹理并加载纹理 

用于创建并加载纹理的函数为LoadGLTextures,实现如下所示: 

int LoadGLTextures()         // 根据加载的位图创建纹理 

int Status=FALSE;         // 指示纹理创建是否成功的标志 

AUX_RGBImageRec *TextureImage[6];     // 创建一个纹理图像数组,这里指定数组大小为6 

memset(TextureImage,0,sizeof(void *)*6);           // 初始化纹理图像数组,为其分配内存 

char *pictures[] = { // 创建一个位图名称数组,对应6幅位图 
   "Data/No1.bmp", 
   "Data/No2.bmp", 
   "Data/No3.bmp", 
   "Data/No4.bmp", 
   "Data/No5.bmp", 
   "Data/No6.bmp" 
}; 
for(int i=0; i<6; i++) // 遍历位图名称数组,根据位图名称分别生成 

   if (TextureImage[i]=LoadBMP(pictures[i])) // 加载位图i成功,修改状态标志变量Status为TRUE 
   { 
    Status=TRUE;        

   glGenTextures(1, &texture[i]);     // 为第i个位图创建纹理 
   glBindTexture(GL_TEXTURE_2D, texture[i]); // 将生成的纹理的名称绑定到指定的纹理上 
   glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[i]->sizeX, TextureImage[i]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[i]->data); 
   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); 
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); 
   } 

   if (TextureImage[i])         // 释放位图数组占用的内存空间 
   { 
    if (TextureImage[i]->data)       
    { 
     free(TextureImage[i]->data);    
    } 

    free(TextureImage[i]);        
   } 

return Status;          // 创建纹理并加载,返回成功或者失败的标志Status 


上述函数是创建和加载纹理的核心实现。 

1、glGenTextures函数 

其中,调用了glGenTextures函数,查看MSDN可以看到,声明如下所示: 

void glGenTextures( 
GLsizei n,          
GLuint * textures   
); 

函数参数的含义: 

n —— 生成的纹理的名称的个数; 

textures —— 生成的纹理名称所存储位置的指针,也就是一个纹理数组的内存地址,或者说是数组首元素的内存地址。 

函数被调用,会生成一系列纹理的名字,并存储到指定的数组中。 

2、glBindTexture函数 

glBindTexture函数实现了将调用glGenTextures函数生成的纹理的名字绑定到对应的目标纹理上。该函数的声明如下所示: 

void glBindTexture( 
GLenum target,   
GLuint texture   
); 

函数参数的含义: 

target —— 纹理被绑定的目标,它只能取值GL_TEXTURE_1D或者GL_TEXTURE_2D; 

texture —— 纹理的名称,并且,该纹理的名称在当前的应用中不能被再次使用。 

3、glTexImage2D函数 

调用glTexImage2D函数,用来指定二维纹理图像。该函数的声明如下所示: 

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

函数参数的含义: 

target —— 指定目标纹理,必须为GL_TEXTURE_2D; 

level —— 指定图像级别的编号,0表示基本图像,其它可以参考MSDN; 

components —— 纹理中颜色组件的编号,可是是1或2或3或4; 

width —— 纹理图像的宽度; 

height —— 纹理图像的高度; 

border —— 纹理图像的边框宽度,必须是0或1; 

format —— 指定像素数据的格式,一共有9个取值:GL_COLOR_INDEX、GL_RED、GL_GREEN、GL_BLUE、GL_ALPHA、GL_RGB、GL_RGBA、GL_BGR_EXT、GL_BGRA_EXT、GL_LUMINANCE、GL_LUMINANCE_ALPHA ,具体含义可以参考MSDN; 

type —— 像素数据的数据类型,取值可以为GL_UNSIGNED_BYTE, GL_BYTE, GL_BITMAP, GL_UNSIGNED_SHORT, GL_SHORT, GL_UNSIGNED_INT, GL_INT, and GL_FLOAT; 

pixels —— 内存中像素数据的指针。 

4、glTexParameteri函数 

glTexParameteri函数或者glTexParameterf函数用来设置纹理参数,声明如下所示: 

void glTexParameterf( 
GLenum target, 
GLenum pname, 
GLfloat param 
); 

void glTexParameteri( 
GLenum target, 
GLenum pname, 
GLint param    
); 

函数参数的含义: 

target —— 目标纹理,必须为GL_TEXTURE_1D或GL_TEXTURE_2D; 

pname —— 用来设置纹理映射过程中像素映射的问题等,取值可以为:GL_TEXTURE_MIN_FILTER、GL_TEXTURE_MAG_FILTER、GL_TEXTURE_WRAP_S、GL_TEXTURE_WRAP_T,详细含义可以查看MSDN; 

param —— 实际上就是pname的值,可以参考MSDN。 

另外,该类函数还有两个: 

void glTexParameterfv( 
GLenum target,         
GLenum pname,          
const GLfloat *params 
); 

void glTexParameteriv( 
GLenum target,       
GLenum pname,        
const GLint *params 
); 

上述程序中调用如下: 

    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); 
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); 

功能就是实现线形滤波的功能,当纹理映射到图形表面以后,如果因为其它条件的设置导致纹理不能更好地显示的时候,进行过滤,按照指定的方式进行显示,可能会过滤掉显示不正常的纹理像素。 

纹理映射过程 

纹理映射的过程是在DrawGLScene函数中实现的,也就是在绘制图形的过程中,直接进行我呢里映射,或者称为,为指定的平面贴纹理,DrawGLScene函数实现如下所示: 

int DrawGLScene(GLvoid)         

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
glLoadIdentity();         
glTranslatef(0.0f,0.0f,-5.0f); 

glRotatef(xrot,1.0f,0.0f,0.0f); 
glRotatef(yrot,0.0f,1.0f,0.0f); 
glRotatef(zrot,0.0f,0.0f,1.0f); 


// Front Face 
glBindTexture(GL_TEXTURE_2D, texture[0]); //   选择第一个纹理texture[0],进行贴纹理 
glBegin(GL_QUADS); 
   glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); 
   glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); 
   glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); 
   glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); 
glEnd(); 

// Back Face 
glBindTexture(GL_TEXTURE_2D, texture[1]); //   选择第二个纹理texture[1],进行贴纹理 
glBegin(GL_QUADS); 
   glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); 
   glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); 
   glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); 
   glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); 
glEnd(); 

// Top Face 
glBindTexture(GL_TEXTURE_2D, texture[2]); //   选择第三个纹理texture[2],进行贴纹理 
glBegin(GL_QUADS); 
   glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); 
   glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); 
   glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); 
   glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); 
glEnd(); 

// Bottom Face 
glBindTexture(GL_TEXTURE_2D, texture[3]); //   选择第四个纹理texture[3],进行贴纹理 
glBegin(GL_QUADS); 
   glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); 
   glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f); 
   glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); 
   glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); 
glEnd(); 

// Right face 
glBindTexture(GL_TEXTURE_2D, texture[4]); //   选择第五个纹理texture[4],进行贴纹理 
glBegin(GL_QUADS); 
   glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); 
   glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); 
   glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); 
   glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); 
glEnd(); 

// Left Face 
glBindTexture(GL_TEXTURE_2D, texture[5]); //   选择第六个纹理texture[5],进行贴纹理 
glBegin(GL_QUADS); 
   glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); 
   glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); 
   glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); 
   glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); 
glEnd(); 

xrot+=0.3f; 
yrot+=0.2f; 
zrot+=0.4f; 
return TRUE;          


因为,通过前面的过程,已经将位图加载并创建和加载纹理成功,纹理数组已经存在于内存之中,调用上述函数实现纹理映射,即,从内存中取出指定的纹理,将其映射到立方体的指定的面上。 

上述函数中调用了glTexCoord2f函数,设置纹理坐标,该函数的声明如下所示: 

void glTexCoord2f( 
GLfloat s, 
GLfloat t 
); 

glTexCoord2f 的第一个参数是X坐标,当s=0.0f 时是纹理的左侧,s=0.5f 时是纹理的中点,s=1.0f 时是纹理的右侧。 glTexCoord2f 的第二个参数是Y坐标,t=0.0f 是纹理的底部,t=0.5f 是纹理的中点, t=1.0f 是纹理的顶部。 

上述函数在为前面那个面映射纹理的时候调用如下: 

   // Front Face 
glBindTexture(GL_TEXTURE_2D, texture[0]); 
glBegin(GL_QUADS); 
   glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); 
   glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); 
   glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); 
   glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); 
glEnd(); 

其中: 

glTexCoord2f(0.0f, 0.0f);表示将纹理texture[0]的左下角坐标(0.0f, 0.0f)映射到立方体前面那个面的顶点(-1.0f, -1.0f, 1.0)上; 

glTexCoord2f(1.0f, 0.0f);表示将纹理texture[0]的右下角坐标(1.0f, 0.0f)映射到立方体前面那个面的顶点(1.0f, -1.0f, 1.0f)上; 

glTexCoord2f(1.0f, 1.0f);表示将纹理texture[0]的右上角坐标(1.0f, 1.0f)映射到立方体前面那个面的顶点(1.0f, 1.0f, 1.0f)上; 

glTexCoord2f(0.0f, 1.0f);表示将纹理texture[0]的左上角坐标(0.0f, 1.0f)映射到立方体前面那个面的顶点(-1.0f, 1.0f, 1.0f)上。 

这样,纹理texture[0]就被映射到了立方体前面那个面上。 

纹理映射结果 

为了使立方体能够运动起来,对立方体进行累的旋转变换,而且,定义了三个全局变量: 

GLfloat xrot;    // 沿着x旋转的变量 
GLfloat yrot;    // 沿着y旋转的变量 
GLfloat zrot;    // 沿着z旋转的变量 

初始化都为0,然后,在每次调用DrawGLScene函数的时候,改变x、y、z的分量值,在DrawGLScene函数中如下所示: 

xrot+=0.3f; 
yrot+=0.2f; 
zrot+=0.4f; 

在DrawGLScene函数中还要执行旋转变换: 

glRotatef(xrot,1.0f,0.0f,0.0f); 
glRotatef(yrot,0.0f,1.0f,0.0f); 
glRotatef(zrot,0.0f,0.0f,1.0f); 

// 
今天纠结于怎么在一个图上画多个图形,分别贴上纹理,看完了上面的文章,恍然大悟。改改代码,总算成功了。 
在android中的实现代码如下, 
首先在onSurfaceCreate()的时候,要先把这些图像载入进来,读取到内存中,给它们分配textureID. 
public void onSurfaceCreated(GL10 gl, EGLConfig config) { 
        /* 
         * By default, OpenGL enables features that improve quality 
         * but reduce performance. One might want to tweak that 
         * especially on software renderer. 
         */ 
        glDisable(GL_DITHER); 

        /* 
         * Some one-time OpenGL initialization can be made here 
         * probably based on features of this particular context 
         */ 
        glHint(GL_PERSPECTIVE_CORRECTION_HINT, 
                GL_FASTEST); 

        glClearColor(.5f, .5f, .5f, 1); 
        glShadeModel(GL_SMOOTH); 
        glEnable(GL_DEPTH_TEST); 
        glEnable(GL_TEXTURE_2D); 

        /* 
         * Create our texture. This has to be done each time the 
         * surface is created. 
         */ 

        int[] textures = new int[2]; 
//这里是给textures这个数组中的每一个id,创建一个对应的纹理。第一个参数是1. 
        glGenTextures(1, textures, 0); 


        mTextureID = textures[0]; 
        textureID2 = textures[1]; 
        for(int i=0;i<textures.length;i++){ 
//之前已经创建好了纹理,这一步可以想象为有一个隐藏的局部变量a,我们把这个变量a指向之前在内存中分配好的一个纹理textures[i]. 
        glBindTexture(GL_TEXTURE_2D, textures[i]); 
            
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 
                    GL_NEAREST); 
            glTexParameterf(GL_TEXTURE_2D, 
                    GL_TEXTURE_MAG_FILTER, 
                    GL_LINEAR); 

            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 
                    GL_CLAMP_TO_EDGE); 
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 
                    GL_CLAMP_TO_EDGE); 

            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, 
                    GL_REPLACE); 
//读取图像文件 
            InputStream is; 
            if(i == 0){ 
            is = mContext.getResources().openRawResource(R.drawable.robot); 
            }else{ 
            is = mContext.getResources() 
                .openRawResource(R.drawable.yellow); 
            } 
                    
            Bitmap bitmap; 
            try { 
                bitmap = BitmapFactory.decodeStream(is); 
            } finally { 
                try { 
                    is.close(); 
                } catch(IOException e) { 
                    // Ignore. 
                } 
            } 
            //这里就用到了之前所说的那个隐藏的变量a, 
            //把bitmap写到a所指向的那个texture里面。 
            GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0); 
            bitmap.recycle(); 
        } 
        
    } 

存好之后,要使用如下: 
    public void onDrawFrame(GL10 gl) { 
        /* 
         * By default, OpenGL enables features that improve quality 
         * but reduce performance. One might want to tweak that 
         * especially on software renderer. 
         */ 
        glDisable(GL_DITHER); 

        glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, 
                GL_MODULATE); 

        /* 
         * Usually, the first thing one might want to do is to clear 
         * the screen. The most efficient way of doing this is to use 
         * glClear(). 
         */ 

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

        /* 
         * Now we're ready to draw some 3D objects 
         */ 

        glMatrixMode(GL_MODELVIEW); 
        glLoadIdentity(); 

        GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f); 

        glEnableClientState(GL_VERTEX_ARRAY); 
        glEnableClientState(GL_TEXTURE_COORD_ARRAY); 

        glActiveTexture(GL_TEXTURE0); 
//隐藏局部变量a,指向一个texture.之前是指到那块内存,然后写进去 
//这里是拿出来用的。 
        glBindTexture(GL_TEXTURE_2D, mTextureID); 
        
        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, 
                GL_REPEAT); 
        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, 
                GL_REPEAT); 

        /*long time = SystemClock.uptimeMillis() % 4000L; 
        float angle = 0.090f * ((int) time); 

        glRotatef(angle, 0, 0, 1.0f); 
*/ 
        rectangle.draw(gl);//这里用texture1作为纹理。 
//绑定texture2,作为纹理。 
        glBindTexture(GL_TEXTURE_2D, textureID2); 
        tri.draw(gl); 
    } 

rectangle的draw()和trig的draw()方法是一样的: 
public void draw(GL10 gl) { 
            glFrontFace(GL_CCW); 
            glVertexPointer(3, GL_FLOAT, 0, mFVertexBuffer);//指定轮廓 
            glEnable(GL_TEXTURE_2D); 
            glTexCoordPointer(2, GL_FLOAT, 0, mTexBuffer);//指定纹理位置 
            glDrawElements(GL_TRIANGLE_STRIP, VERTS, 
                    GL_UNSIGNED_SHORT, mIndexBuffer);//画图 
        } 

在使用GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);的时候,可以不调用glBindTexture(),把图像载入到当前默认的一个texture里面,后面draw()的时候也不必去glBindTexture()。但这只能载入一个图像,多个的话就不行了,必须指定texture,每个图像绑定到一个texture上面。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值