OpenGL教程(3):加载贴图

发表于2017-11-25
评论0 2.8k浏览

有了模型还需要贴图,加载贴图的流程大体分为两部分,首先是图片的解码,其次是使用UV坐标与模型对应,下面就从底层原理和第三方库两个方面来给大家介绍加载贴图 。


解码

下面分别介绍硬编码实现和SOIL库两种方式。


硬编码实现

因为加载不同的类型图片偏移值不一样,加载图片之前要确定图片类型。另一方面,对于DXT这种压缩图片,也需要在压缩图的基础上进行采样,而不是将其还原回未压缩的图元。

static unsigned char* DecodeBMPData(unsigned char* imageData,int&width,int& heigh)  
{  
    //decode bmp  
    int pixelDataOffset =*((int*)(imageData+10));  
    width = *((int*)(imageData +18));  
    heigh = *((int*)(imageData +22));  
    unsigned char* pixelData = (imageData+pixelDataOffset);  
    for(int i = 0;i<width*heigh*3;i+=3)  
    {  
        //bgr->rgb  
        unsigned char temp = pixelData[i+2];  
        pixelData[i+2] = pixelData[i];  
        pixelData[i] = temp;  
    }  
    return pixelData;  
}  
const unsigned long  FORMATE_DXT1 = 0x31545844l; //DXT1-> 1 T X D  
static unsigned char* DecodeDXT1Data(unsigned char* imageData,int&width,int& height,int& pixelSize)  
{  
    height = *(unsigned long*)(imageData+sizeof(unsigned long)*3);  
    width = *(unsigned long*)(imageData+sizeof(unsigned long)*4);  
    pixelSize = *(unsigned long*)(imageData+sizeof(unsigned long)*5);  
    unsigned long compressFormate;  
    compressFormate = *(unsigned long*)(imageData+sizeof(unsigned long)*21);  
    switch (compressFormate)  
    {  
    case FORMATE_DXT1:  
        printf("DXT1\n");  
        break;  
    default:  
        break;  
    }  
    unsigned char* pixelData = new unsigned char[pixelSize];  
    memcpy(pixelData,(imageData+sizeof(unsigned long)*32),pixelSize);  
    return pixelData;  
}  
GLuint CreateTextureFromFile(const char* imagePath)  
{  
    unsigned char* imageData =(unsigned char*) LoadFileContent(imagePath);  
    int width = 0;  
    int heigh = 0;  
    //decode bmp  
    unsigned char* pixelData =nullptr;  
    int pixelDataSize = 0;  
    GLenum srcForamte = GL_RGB;  
    if ((*(unsigned short*)imageData) == 0x4D42)  
    {  
        pixelData = DecodeBMPData(imageData,width,heigh);  
    }   
    else if (memcmp(imageData,"DDS ",4)==0)  
    {  
        pixelData = DecodeDXT1Data(imageData,width,heigh,pixelDataSize);  
        srcForamte = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;  
    }  
    if (pixelData == nullptr)  
    {  
        printf("cannot decode %s \n",imagePath);  
        delete imageData;  
        return 0;  
    }  
    GLuint texture;  
    glGenTextures(1,&texture);  
    glBindTexture(GL_TEXTURE_2D,texture);  
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);  
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);  
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);  
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);  
    if(srcForamte == GL_RGB){  
        glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,width,heigh,0,GL_RGB,GL_UNSIGNED_BYTE,pixelData);  
    }  
    else if (srcForamte == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT)  
    {  
        glCompressedTexImage2D(GL_TEXTURE_2D,0,srcForamte,width,heigh,0,pixelDataSize,pixelData);  
    }  
    glBindBuffer(GL_TEXTURE_2D,0);  
    delete imageData;  
    return texture;  


可以注意到,通过glGenTextures生成buffer,通过glTexParameteri设置图片的显示参数,采样参数等。最后通过glTexImage2D生成图元。


第三方库实现

加载贴图 还可以使用SOIL库,SOIL是简易OpenGL图像库(Simple OpenGL Image Library)的缩写,它支持大多数流行的图像格式,使用起来也很简单。如果使用SOIL库加载,代码封装如下:

GLuint CreateTextureFromFile(const char* imagePath)  
{  
    GLuint texture;  
    glGenTextures(1, &texture);  
    glBindTexture(GL_TEXTURE_2D, texture);  
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE);  
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE);  
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);  
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);  
    int width, height;  
    unsigned char* image = SOIL_load_image(imagePath, &width, &height, 0, SOIL_LOAD_RGB);  
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);  
    glGenerateMipmap(GL_TEXTURE_2D);  
    SOIL_free_image_data(image);  
    glBindTexture(GL_TEXTURE_2D, 0);   
    return texture;  
}  

使用贴图

通过glActiveTexture可以开启图元位置。

TextureLocation = glGetUniformLocation(s_program,"U_MainTexture");  
glActiveTexture(GL_TEXTURE0);  
glBindTexture(GL_TEXTURE_2D,mainTexture);  
glUniform1i(TextureLocation,0);  


在OpenGL中图元可以直接被指定,而无需glUniform为其赋值,它可以从0到16,分别传入显卡中,默认0对应第一张图元的位置。当然也可以通过使用glUniform1i,给纹理采样器分配一个位置值,这样可以实现在一个片段着色器中设置多个加载贴图 。

默认情况下GL_TEXTURE0是被激活的。因此在单图元的情况下,可以只写如下代码进行渲染。

glBindTexture(GL_TEXTURE_2D,mainTexture);  


shader绘制

在编写shader时,需要在vs中加入texcoord,并传递给fs。fs方面要加入sampler2D来接收纹理。

//vs  
attribute vec3 pos;  
attribute vec2 texcoord;  
attribute vec3 normal;  
uniform mat4 M;  
uniform mat4 V;  
uniform mat4 P;  
varying vec2 V_Texcoord;  
void main()  
{  
    V_Texcoord = texcoord;  
    gl_Position=P*V*M*vec4(pos,1.0);  
}  
//fs  
uniform sampler2D U_MainTexture;  
varying vec2 V_Texcoord;  
void main()  
{  
   gl_FragColor= texture2D(U_MainTexture,V_Texcoord);  
}  


总结

通过以上代码,可以 加载贴图 并将其绘制出来。建议将其封装成接口或类,因为这部分代码很底层,通常不会更改。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引