1. 程式人生 > >OpenGL(三) 載入貼圖

OpenGL(三) 載入貼圖

有了模型還需要貼圖。 載入貼圖 的流程大體分為兩部分,首先是圖片的解碼,其次是使用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);
}

總結

通過以上程式碼,可以 載入貼圖 並將其繪製出來。建議將其封裝成介面或類,因為這部分程式碼很底層,通常不會更改。

松陽論道

關注我的微信公眾號,獲取更多優質內容