天天看點

OpenGL學習筆記4:紋理

原始圖像資料

像素包裝

圖像資料在記憶體中很少以緊密包裝的形式存在。在許多硬體平台上,處于性能上的考慮,一幅圖像的每一行都應該從一種特定位元組對齊位址開始。絕大多數編譯器會自動把變量和緩沖區放置在一個針對該架構對齊優化的位址上。

例如一個包含3個分量的rgb圖像,每個分量存儲在一個位元組中,如果圖像有199個像素,那麼一行需要597個像素。如果硬體本身的體系結構是4位元組排列,那麼圖像每一行的末尾将由額外的3個空字元進行填充達到600位元組。

我們可以使用下列函數改變或者回複像素的存儲方式。

void glPixelStorei(GLenum pname,Glint param);
void glPixelStoref(GLenum pname,GLfloat param);
           

例如,改成緊密包裝(檔案格式以1個位元組排列,沒有補齊):

glPixelStorei(GL_UNPACK_ALLGNMENT,);
           

其中GL_UNPACK_ALIGNMENT指定OpenGL如何從資料緩沖區中解包圖像資料。

glPixelStore參數

參數名 類型 初始值
GL_PACK_SWAP_BYTES GLboolean GL_FALSE
GL_UNPACK_SWAP_BYTES GLboolean GL_FALSE
GL_PACK_LSB_FIRST GLboolean GL_FALSE
GL_UNPACK_LSB_FIRST GLboolean GL_FALSE
GL_PACK_ROW_LENGTH GLint
GL_UNPACK_ROW_LENGTH GLint
GL_PACK_SKIP_ROWS GLint
GL_UNPACK_SKIP_ROWS GLint
GL_PACK_SKIP_PIXELS GLint
GL_UNPACK_SKIP_PIXELS GLint
GL_PACK_ALIGNMENT GLint 4
GL_UNPACK_ALIGNMENT GLint 4
GL_PACK_IMAGE_HEIGHT GLint
GL_UNPACK_IMAGE_HEIGHT GLint
GL_PACK_SKIP_IMAGES GLint
GL_UNPACK_SKIP_IMAGES GLint

像素圖

每個像素需要一個以上的存儲位來表示,附加位允許存儲強度(亮度)。在OpenGL核心版本中,我們無法直接将一個像素圖繪制到顔色緩沖區中,但是可以使用下面的函數将顔色緩沖區的内容作為像素圖直接讀取。

void glReadPixels(Glint x,Glint y,GLSizei width,GLSizei height,GLenum format,GLenum type,const void *pixels);
           

我們将x和y值指定為矩形左下角的視窗坐标,然後再指定矩形的width和height值(像素形式)。如果顔色緩沖區存儲的資料與我們要求的不同,OpenGL将負責進行必要的轉換。

第4個變量format指定pixels指向的資料元素的顔色布局,如下表:

OpenGL像素格式

常量 描述
GL_RGB 按照紅、綠、藍順序排列的顔色
GL_RGBA 按照紅、綠、藍、Alpha順序排列的顔色
GL_BGR 按照藍、綠、紅順序排列的顔色
GL_BGRA 按照藍、綠、紅、Alpha順序排列的顔色
GL_RED 每個像素值包含一個紅色分量
GL_GREEN 每個像素值包含一個綠色分量
GL_BLUE 每個像素值包含一個藍色分量
GL_RG 每個像素依次包含一個紅色和一個綠色分量
GL_RED_INTEGER 每個像素包含一個整數形式的紅色分量
GL_GREEN_INTEGER 每個像素包含一個整數形式的綠色分量
GL_BLUE_INTETER 每個像素包含一個整數形式的藍色分量
GL_RG_INTEGER 每個玄素依次包含一個整數形式的紅色和一個整數形式的綠色分量
GL_RGB_INTEGER 每個像素依次包含整數形式的紅色、綠色和藍色分量
GL_RGBA_INTEGER 每個像素一次包含整數形式的紅色、綠色、藍色和Alpha分量
GL_BGR_INTEGER 每個像素依次包含整數形式的藍色、綠色和紅色分量
GL_BGRA_INTEGER 每個像素依次包含整數形式的藍色、綠色、紅色和Alpha分量
GL_STENCIL_INDEX 每個像素隻包含一個模闆值
GL_DEPTH_COMPONENT 每個像素隻包含一個深度值
GL_DEPTH_STENCIL 每個像素包含一個深度值和一個模闆值

參數type解釋參數*pixels指向的詩句,它告訴OpenGL使用緩沖區中的什麼資料類型來存儲顔色分量。如下表:

像素資料的資料類型

常量 描述
GL_UNSIGNED_BYTE 每種顔色分量都是一個8位無符号整數
GL_BYTE 8位有符号整數
GL_UNSIGNED_SHORT 16位無符号整數
GL_SHORT 16位有符号整數
GL_UNSIGNED_INT 32位無符号整數
GL_INT 32位有符号整數
GL_FLOAT 單精度浮點數
GL_HALF_FLOAT 半精度浮點數
GL_UNSIGNED_BYTE_3_2_2 包裝的RGB值
GL_UNSIGNED_BYTE_2_3_3_REV 包裝的RGB值
GL_UNSIGNED_SHORT_5_6_5 包裝的RGB值
GL_UNSIGNED_SHORT_5_6_5_REV 包裝的RGB值
GL_UNSIGNED_SHORT_4_4_4_4 包裝的RGB值
GL_UNSIGNED_SHORT_4_4_4_4_REV 包裝的RGB值
GL_UNSIGNED_SHORT_5_5_5_1 包裝的RGB值
GL_UNSIGNED_SHORT_1_5_5_5_REV 包裝的RGB值
GL_UNSIGNED_INT_8_8_8_8 包裝的RGB值
GL_UNSIGNED_INT_8_8_8_8_REV 包裝的RGB值
GL_UNSIGNED_INT_10_10_10_2 包裝的RGB值
GL_UNSIGNED_INT_2_10_10_10_REV 包裝的RGB值
GL_UNSIGNED_INT_24_8 包裝的RGB值
GL_UNSIGNED_INT_10F_11F_REV 包裝的RGB值
GL_FLOAT_24_UNSIGNED_INT_24_8_REV 包裝的RGB值

glReadPixels從圖形硬體中複制資料,通常通過總線傳輸到系統記憶體。在這種情況下,應用程式将被阻塞,直到記憶體傳輸完成。此外,如果我們制定一個與圖形硬體的本地排列不同的像素不具,那麼在資料進行重定格式時将産生額外的性能開銷。

包裝的像素格式

預設情況下,對于glReadPixels函數來說,讀取操作在雙緩沖區渲染環境下将在背景緩沖區進行,而在單緩沖區渲染環境下則在前台緩沖區進行。我們可以用下面的函數改變這些像素操作的源。

void glReadBuffer(GLenum mode);

模式參數可以取GL_FRONT、GL_BACK、GL_LEFT、GL_RIGHT、GL_FRONT_LEFT、GL_FRONT_RIGHT、GL_BACK_LEFT、GL_BACK_RIGHT或者甚至是GL_NONE中的任意一個。

儲存像素

GLTools庫中的gltWriteTGA函數從前台顔色緩沖區中讀取顔色資料,并将這些資料存儲到一個Targa檔案格式的圖像檔案中。

GLint gltWriteTGA(const char *szFileName)
{
    FILE *pFile;                // 檔案指針
    TGAHEADER tgaHeader;        // tga檔案頭
    unsigned long lImageSize;   // 圖像的大小
    GLbyte    *pBits = NULL;      // 圖像資料
    GLint iViewport[];         // 視口
    GLenum lastBuffer;          // 儲存目前讀取緩沖區的設定

    // 取得目前視口大小
    glGetIntegerv(GL_VIEWPORT, iViewport);

    // 獲得圖像大小,因為tga的圖像資料是緊密包裝的,是以用視口的寬高乘以3個位元組
    lImageSize = iViewport[] *  * iViewport[];    

    // 配置設定記憶體用于存儲讀取出來的圖像資料
    pBits = (GLbyte *)malloc(lImageSize);
    if(pBits == NULL)
        return ;

    // 設定為逐個像素的方式讀取
    glPixelStorei(GL_PACK_ALIGNMENT, );
    glPixelStorei(GL_PACK_ROW_LENGTH, );
    glPixelStorei(GL_PACK_SKIP_ROWS, );
    glPixelStorei(GL_PACK_SKIP_PIXELS, );

    // 儲存目前的設定,後面再恢複它
    glGetIntegerv(GL_READ_BUFFER, (GLint *)&lastBuffer);
    glReadBuffer(GL_FRONT);
    glReadPixels(, , iViewport[], iViewport[], GL_BGR_EXT, GL_UNSIGNED_BYTE, pBits);
    glReadBuffer(lastBuffer);

    // 初始化tag檔案頭的格式
    tgaHeader.identsize = ;
    tgaHeader.colorMapType = ;
    tgaHeader.imageType = ;
    tgaHeader.colorMapStart = ;
    tgaHeader.colorMapLength = ;
    tgaHeader.colorMapBits = ;
    tgaHeader.xstart = ;
    tgaHeader.ystart = ;
    tgaHeader.width = iViewport[];
    tgaHeader.height = iViewport[];
    tgaHeader.bits = ;
    tgaHeader.descriptor = ;

    // 蘋果操作需要,進行大小端的轉換
#ifdef __APPLE__
    LITTLE_ENDIAN_WORD(&tgaHeader.colorMapStart);
    LITTLE_ENDIAN_WORD(&tgaHeader.colorMapLength);
    LITTLE_ENDIAN_WORD(&tgaHeader.xstart);
    LITTLE_ENDIAN_WORD(&tgaHeader.ystart);
    LITTLE_ENDIAN_WORD(&tgaHeader.width);
    LITTLE_ENDIAN_WORD(&tgaHeader.height);
#endif

    // 打開檔案
    pFile = fopen(szFileName, "wb");
    if(pFile == NULL)
    {
        free(pBits);    
        return ;
    }

    // 寫檔案頭
    fwrite(&tgaHeader, sizeof(TGAHEADER), , pFile);

    // 寫圖像資料
    fwrite(pBits, lImageSize, , pFile);

    // 釋放臨時配置設定的記憶體空間
    free(pBits);    
    fclose(pFile);

    // 成功了
    return ;
}
           

讀取像素

gltReadTGABits從磁盤中載入Targa檔案。
GLbyte *gltReadTGABits(const char *szFileName, GLint *iWidth, GLint *iHeight, GLint *iComponents, GLenum *eFormat)
{
  FILE *pFile;            // 檔案指針
  TGAHEADER tgaHeader;        // TGA 檔案頭
  unsigned long lImageSize;        // 圖像的大小,用位元組表示
  short sDepth;            // 像素深度
  GLbyte    *pBits = NULL;          // 指向位的指針

  // 預設/失敗值
  *iWidth = ;
  *iHeight = ;
  *eFormat = GL_BGR_EXT;
  *iComponents = GL_RGB;

  // 嘗試打開檔案
  pFile = fopen(szFileName, "rb");
  if(pFile == NULL)
    return NULL;

  // 讀入檔案(二進制)
  fread(&tgaHeader, /* sizeof(TGAHEADER)*/, , pFile);

  // 為大小位元組存儲順序問題而進行位元組交換
#ifdef __APPLE__
  LITTLE_ENDIAN_WORD(&tgaHeader.colorMapStart);
  LITTLE_ENDIAN_WORD(&tgaHeader.colorMapLength);
  LITTLE_ENDIAN_WORD(&tgaHeader.xstart);
  LITTLE_ENDIAN_WORD(&tgaHeader.ystart);
  LITTLE_ENDIAN_WORD(&tgaHeader.width);
  LITTLE_ENDIAN_WORD(&tgaHeader.height);
#endif


  // 擷取紋理的寬度、高度和深度
  *iWidth = tgaHeader.width;
  *iHeight = tgaHeader.height;
  sDepth = tgaHeader.bits / ;

  // 這裡進行一些有效性檢驗,非常簡單,我們隻要懂得或者說關心8位、24位或32位
  //的targa
  if(tgaHeader.bits !=  && tgaHeader.bits !=  && tgaHeader.bits != )
    return NULL;

  // 計算圖像緩沖區的大小
  lImageSize = tgaHeader.width * tgaHeader.height * sDepth;

  // 進行記憶體定位并進行成功檢驗
  pBits = (GLbyte*)malloc(lImageSize * sizeof(GLbyte));
  if(pBits == NULL)
    return NULL;

  // 讀入位
  // 檢查讀取錯誤。這項操作應該發現RLE或者其他我們不想識别的奇怪格式
  if(fread(pBits, lImageSize, , pFile) != )
  {
    free(pBits);
    return NULL;
  }

  // 設定希望的OpenGL格式
  switch(sDepth)
  {
  case :     // 最可能的情況
    *eFormat = GL_BGR;
    *iComponents = GL_RGB;
    break;
  case :
    *eFormat = GL_BGRA_EXT;
    *iComponents = GL_RGBA8;
    break;
  case :
    *eFormat = GL_LUMINANCE;
    *iComponents = GL_LUMINANCE8;
break;
default: // RGB
// 如果是在iPhone上TGA為BGR,并且iPhone不支援沒有Alpha的BGR,
//但是它支援RGB,是以隻要将紅色和藍色調整一下就能滿足要求。
//但是為了加快iPhone的載入速度,請儲存帶有Alpha的TGA。
#ifdef OPENGL_ES
    for(int i=;i<lImageSize;i+=){
        GLbyte temp=pBits[i];
        pBits[i]=pBits[i+];
        pBits[i+]=temp;
}
#endif
  };

  // 檔案操作完成
  fclose(pFile);

  // 傳回指向圖像資料的指針
  return pBits;
}
           

載入紋理

有3個OpenGL函數最經常用來從存儲器緩沖區中載入(比如說,從一個磁盤檔案中讀取)紋理資料。

void glTexImage1D(GLenum target,Glint level,Glint internalformat,GLsizei width,Glint border,GLenum format,GLenum type,void *data);
void glTexImage2D(GLenum target,Glint level,Glint internalformat,GLsizei width,GLsizei height,Glint border,GLenum format,GLenum type,void *data);
void glTexImage3D(GLenum target,Glint level,Glint internalformat,GLsizei width,GLsizei height,GLsizei depth,Glint border,GLenum format,GLenum type,void *data);
           

這些函數中的target變量應分别為GL_TEXTURE_1D、GL_TEXTURE_2D或GL_TEXTURE_3D。我們也可以指定代理紋理(Proxy texture),方法是指定GL_PROXY_TEXTURE_1D、GL_PROXY_TEXTURE_2D或GL_PROXY_TEXTURE_3D,并使用glGetTexPatameter函數提取代理查詢的結果。

Level參數指定了這些函數所加在的mip貼圖層次。

internalformat參數告訴OpenGL我們希望在每個紋理單元中存儲多少顔色成分,并在可能的情況下說明這些成分的存儲大小,以及是否希望對紋理進行壓縮。

最常用的紋理内部格式

常量 含義
GL_ALPHA 按照alpha值存儲紋理單元
GL_LUMINANCE 按照亮度值存儲紋理單元
GL_LUMINANCE_ALPHA 按照亮度值和alpha值存儲紋理單元
GL_RGB 按照紅、綠、藍成分存儲紋理單元
GL_RGBA 按照紅、綠、藍和alpha成分存儲紋理單元

使用顔色緩沖區

一維和二維的紋理也可以從顔色緩沖區加在資料。我們可以從顔色緩沖區讀取一幅圖像,并通過下面這兩個函數将它化為一個新的紋理使用。

void glCopyTexImage1D(GLenum target,Glint level,GLenum internalformat,Glint x,Glint y,GLsizei width,Glint border);
void glCopyTexImage2D(GLenum target,Glint level,GLenum internalformat,Glint x,Glint y,GLsizei width,GLsizei height,Glint border);
           

這兩個函數的操作類似于glTexImage,但在這裡x和y在顔色緩沖區中指定了開始讀取紋理資料的位置。源緩沖區是通過glReadBuffer函數設定的。并不存在glCopyTexImage3D。

更新紋理

替換一個紋理圖像常常要比直接使用glTexImage重新加載一個新紋理快得多:

void glTexSubImage1D(GLenum target,Glint level,Glint xOffset,GLsizei width,GLenum format,GLenum type,const GLvoid *data);
void glTexSubImage2D(GLenum target,Glint level,Glint xOffset,Glint yOffset,GLsizei width,GLsizei height,GLenum format,GLenum type,const GLvoid *data);
void glTexSubImage3D(GLenum target,Glint level,Glint xOffset,Glint yOffset,Glint zOffset,GLsizei width,GLsizei height,GLsizei depth,GLenum format,GLenum type,const GLvoid *data);
           

下面函數允許沃恩從顔色緩沖區讀取紋理,并插入或替換原來紋理的一部分。

void glCopyTexSubImage1D(GLenum target,Glint level,Glint xoffset,Glint x,Glint y,GLsizei width);
void glCopyTexSubImage2D(GLenum target,Glint level,Glint xoffset,Glint yoffset,Glint x,Glint y,GLsizei width,GLsizei height);
void glCopyTexSubImage3D(GLenum target,Glint level,Glint xoffset,Glint yoffset,Glint zoffset,Glint x,Glint y,GLsizei width,GLsizei height);
           

紋理對象

紋理對象允許我們一次加載一個以上的紋理狀态(包括紋理圖像),以及在它們之間快速切換。紋理狀态是由目前綁定的紋理對象維護的,而紋理對象是由一個無符号整數辨別的:

按所需加載紋理數量初始化紋理對象:

void glGenTextures(GLsizei n,GLuint *textures);
           

為了綁定紋理狀态,我們可以調用下面這個函數:

void glBindTexture(GLenum target,GLuint texture);
           

為了删除紋理對象,可以調用下面這個函數:

void glDeleteTextures(GLsizei n,GLuint *textures);
           

我們可以使用下面這個函數對紋理對象名(或句柄)進行測試,判斷它們是否有效:

GLboolean glIsTexture(GLuint texture);
           

如果這個證書是一個以前已經配置設定的紋理對象名,則傳回GL_TRUE。

紋理應用

紋理坐标

典型情況下,紋理坐标是作為0.0到1.0範圍内的浮點值指定的。紋理坐标命名為s、t、r和q,支援從一維到三維紋理坐标。

紋理參數

很多參數的應用都會影響渲染的規則和紋理貼圖的行為,這些參數通過下列函數的變量來進行設定:

void glTexParameterf(GLenum target,GLenum pname,GLfloat param);
void glTexParamereri(GLenum target,GLenum pname,Glint param);
void glTexParameterfv(GLenum target,GLenum pname,GLfloat *params);
void glTexParameteriv(GLenum target,GLenum pname,Glint *params);
           

第一個參數target指定這些參數将要應用到哪個紋理模式上,它可以是GL_TEXTURE_1D、GL_TEXTURE_2D和GL_TEXTURE_3D.第二個參數pname指定了需要設定哪個紋理參數,而最後一個參數param或params用于設定特定的紋理參數的值。

基本過濾

根據一個拉伸或收縮的紋理貼圖計算顔色片段的過程稱為紋理過濾。使用OpenGL的紋理參數函數,可以同僚設定放大和縮小過濾器。這兩種過濾器的參數名分别是GL_TEXTURE_MAG_FILTER和GL_TEXTURE_MIN_FILTER。就目前來說,我們可以為他們從兩種基本的紋理過濾器GL_NEAREST和GL_LINEAR中進行選擇,它們分别對應于最鄰近過濾和線性過濾。

我們可以使用下面這兩個函數,為放大和縮小過濾器設定紋理過濾器:

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

我們可以使用下面這幾行代碼,簡單地設定線性過濾:

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

紋理環繞

我們可以調glTexParameteri函數(并分别使用GL_TEXTURE_WRAP_S、GL_TEXTURE_WRAP_T或GL_TEXTURE_WRAP_R作為參數)、為每個坐标分别設定環繞模式。然後,我們可以把環繞模式設定為下面幾個值之一:GL_REPEAT、GL_CLAMT、GL_CLAMP_TO_EDGE或GL_CLAMP_TO_BORDER。

綜合運用

加載紋理

GLuint textureID;
glGenTextures(,&textureID);
glBindTexture(GL_TEXTURE_2D,textureID);
loadTGATexture(“stone.tga”,GL_LINEAR,GL_LINEAR,GL_CLAMP_TO_EDGE);
           

其中,加載紋理的loadTGATexture定義如下:

bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
{
    GLbyte *pBits;
    int nWidth, nHeight, nComponents;
    GLenum eFormat;

    // 讀取材質比特
    pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
    if (pBits == NULL)
        return false;
    //設定放大和縮小過濾
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
    //設定線性過濾
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
    //改成緊密包裝
    glPixelStorei(GL_UNPACK_ALIGNMENT, );
    //載入紋理資料
    glTexImage2D(GL_TEXTURE_2D, , GL_COMPRESSED_RGB, nWidth, nHeight, ,
        eFormat, GL_UNSIGNED_BYTE, pBits);

    free(pBits);

    if (minFilter == GL_LINEAR_MIPMAP_LINEAR ||
        minFilter == GL_LINEAR_MIPMAP_NEAREST ||
        minFilter == GL_NEAREST_MIPMAP_LINEAR ||
        minFilter == GL_NEAREST_MIPMAP_NEAREST)
        //生成Mip層
        glGenerateMipmap(GL_TEXTURE_2D);

    return true;
}
           

指定紋理坐标

添加頂點:

pyramidBatch.Normal3f(, -, );
pyramidBatch.MultiTexCoord2f(, , );
pyramidBatch.Vertex3f(-, -, -);
           

為一個面計算表面法線,然後再在所有3個頂點使用它:

m3dFindNormal(n, vApex, vFrontLeft, vFrontRight);
pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(, f, f);
pyramidBatch.Vertex3fv(vApex);  

pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(, f, f);
pyramidBatch.Vertex3fv(vFrontLeft); 

pyramidBatch.Normal3fv(n);
pyramidBatch.MultiTexCoord2f(, f, f);
pyramidBatch.Vertex3fv(vFrontRight);
           

實際渲染:

glBindTexture(GL_TEXTURE_2D, textureID);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF, 
                                transformPipeline.GetModelViewMatrix(),
                                transformPipeline.GetProjectionMatrix(), 
                                vLightPos, vWhite, );
pyramidBatch.Draw();
           

Mip貼圖

Mip貼圖紋理由一些列紋理圖像組成,每個圖像大小在每個軸的方向上都縮小一半。

如果想指定隻加載0層至第4層,應如下操作:

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_BASE_LEVEL,);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAX_LEVEL,);
           

Mip貼圖過濾

經過Mip貼圖的紋理過濾

常量 描述
GL_NEAREST 在Mip基層上執行最鄰近過濾
GL_LINEAR 在Mip基層上執行線性過濾
GL_NEAREST_MIPMAP_NEAREST 選擇最鄰近Mip層,并執行最鄰近過濾
GL_NEAREST_MIPMAP_LINEAR 在Mip層之間執行線性插補,并執行最鄰近過濾
GL_LINEAR_MIPMAP_NEAREST 選擇最鄰近Mip層,并執行線性過濾
GL_LINEAR_MIPMAP_LINEAR 在Mip層之間執行線性插補,并執行線性過濾,又稱三線性Mip貼圖

生成Mip層

void glGenerateMipmap(GLenum target);
           

目标參數可以是GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D、GL_TEXTURE_CUBE_MAP、GL_TEXTURE_1D_ARRAY或GL_TEXTURE_2D_ARRAY

各項異性過濾

首先,必須确認這種擴充是得到支援的:

if(gltIsExtSupported(“GL_EXT_texture_filter_anisotropic”));
           

然後,查詢得到支援的各向異性過濾的最大數量:

GLfloat fLargest;
……
……
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT,&fLargest);
           

最後,設定想要應用的各向異性過濾的數量:

glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAX_ANISOTROPY_EXT,fLargest);

紋理壓縮

壓縮紋理

紋理壓縮是通過在glTexImage函數中把internalFormat參數設定為下表的任意值實作的:

通用壓縮紋理格式

壓縮格式 基本内部格式

GL_COMPRESSED_RGB GL_RGB

GL_COMPRESSED_RGBA GL_RGBA

GL_COMPRESSED_SRGB GL_RGB

GL_COMPRESSED_SRGB_ALPHA GL_RGBA

GL_COMPRESSED_RED GL_RED

GL_COMPRESSED_RG GL_RG(Red Green)

可以用如下方法來判斷這個紋理是否被成功壓縮:

Glint compFlag;
……
glGetTexLevelParameteriv(GL_TEXTURE_2D,0,GL_TEXTURE_COMPRESSED,&compFlag);
           

glGetTexLevelParameter函數提取的壓縮紋理格式

參數 傳回
GL_TEXTURE_COMPRESSED 如果紋理被壓縮,傳回1,否則傳回0
GL_TEXTURE_COMPRESSED_IMAGE_SIZE 壓縮後的紋理的大小(以位元組為機關)
GL_TEXTURE_INTERNAL_FORMAT 所使用的壓縮格式
GL_NUM_COMPRESSED_TEXTURE_FORMATS 受支援的研所為例格式的數量
GL_COMPRESSED_TEXTURE_FORMATS 一個包含了一些常量值的數組,每個常量值對應于一種受支援的壓縮紋理格式
GL_TEXTURE_COMPRESSION_HINT 紋理壓縮提示的值(GL/NICEST/GL_FASTEST)

我們可以使用glHint指定希望OpenGL根據最快速度還是最佳品質算法來選擇壓縮格式:

glHint(GL_TEXTURE_COMPRESSION_HINT,GL_FASTEST);
glHint(GL_TEXTURE_COMPRESSION_HINT,GL_NICEST);
glHint(GL_TEXTURE_COMPRESSION_HINT,GL_DONT_CARE);
           

GL_EXT_texture_conpression_s3tc的壓縮格式

格式 描述
GL_COMPRESSED_RGB_S3TC_DXT1 RGB資料被壓縮alpha值時鐘是1.0
GL_COMPRESSED_RGBA_S3TC_DXT1 RGB資料被壓縮,alpha值是1.0或0.0
GL_COMPRESSED_RGBA_S3TC_DXT2 RGB資料被壓縮,alpha值用4位存儲
GL_COMPRESSED_RGBA_S3TC_DXT3 RGB資料被壓縮,alpha值是一些8位置的權重平均值

加載壓縮紋理

為了加載預先經過壓縮的紋理資料,可以使用下列函數之一:

void glCompressedTexImage1D(GLenum target,Glint level,GLenum internalFormat,GLsizei width,Glint border,GLsizei imageSize,void *data);
void glCompressedTexImage2D(GLenum target,Glint level,GLenum internalFormat,GLsizei width,GLsizei height,Glint border,GLsizei imageSize,void *data);
void glCompressedTexImage3D(GLenum target,Glint level,GLenum internalFormat,GLsizei width,GLsizei height,GLsizei depth,Glint border,GLsizei imageSize,GLvoid *data);
           

轉載請注明出處:http://blog.csdn.net/ylbs110/article/details/51793970

示例

為了顯示紋理的使用,本示例在學習筆記3(http://blog.csdn.net/ylbs110/article/details/51760021)的示例上直接添加了紋理代碼,為了讓紋理顯示清晰,顔色做了少許改變。

注意:UseStockShader的第一個參數要改為GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF才能成功使用紋理

#include "stdafx.h"
#include <GLTools.h>
#include <GLShaderManager.h>
#include <GLFrustum.h>
#include <GLBatch.h>
#include <GLFrame.h>
#include <GLMatrixStack.h>
#include <GLGeometryTransform.h>
#include <StopWatch.h>

#include <math.h>
#include <stdio.h>

#include <math.h>
#define GLUT_DISABLE_ATEXIT_HACK
#include <GLUT.H>

/*
* 當libjpeg-turbo為vs2010編譯時,vs2015下靜态連結libjpeg-turbo會連結出錯:找不到__iob_func,
* 增加__iob_func到__acrt_iob_func的轉換函數解決此問題,
* 當libjpeg-turbo用vs2015編譯時,不需要此更新檔檔案
*/
#if _MSC_VER>=1900
#include "stdio.h" 
_ACRTIMP_ALT FILE* __cdecl __acrt_iob_func(unsigned);
#ifdef __cplusplus 
extern "C"
#endif 
FILE* __cdecl __iob_func(unsigned i) {
    return __acrt_iob_func(i);
}
#endif /* _MSC_VER>=1900 */

#define NUM_SPHERES 50
GLFrame disk[NUM_SPHERES / ];
GLFrame cylinder[NUM_SPHERES / ];


GLShaderManager     shaderManager;          // Shader Manager
GLMatrixStack       modelViewMatrix;        // Modelview Matrix
GLMatrixStack       projectionMatrix;       // Projection Matrix
GLFrustum           viewFrustum;            // View Frustum
GLGeometryTransform transformPipeline;      // Geometry Transform Pipeline

GLTriangleBatch     torusBatch;
GLBatch             floorBatch;
GLTriangleBatch     sphereBatch;
GLTriangleBatch     triangleBatch;
GLTriangleBatch     cylinderBatch;
GLTriangleBatch     diskBatch;

GLFrame             cameraFrame;

GLuint              uiTextures[];

bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
{
    GLbyte *pBits;
    int nWidth, nHeight, nComponents;
    GLenum eFormat;

    // 讀取材質比特
    pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
    if (pBits == NULL)
        return false;
    //設定放大和縮小過濾
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
    //設定線性過濾
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
    //改成緊密包裝
    glPixelStorei(GL_UNPACK_ALIGNMENT, );
    //載入紋理資料
    glTexImage2D(GL_TEXTURE_2D, , GL_COMPRESSED_RGB, nWidth, nHeight, ,
        eFormat, GL_UNSIGNED_BYTE, pBits);

    free(pBits);

    if (minFilter == GL_LINEAR_MIPMAP_LINEAR ||
        minFilter == GL_LINEAR_MIPMAP_NEAREST ||
        minFilter == GL_NEAREST_MIPMAP_LINEAR ||
        minFilter == GL_NEAREST_MIPMAP_NEAREST)
        //生成Mip層
        glGenerateMipmap(GL_TEXTURE_2D);

    return true;
}

//
// This function does any needed initialization on the rendering
// context. 
void SetupRC()
{

    // Make sure OpenGL entry points are set
    glewInit();
    // Initialze Shader Manager
    shaderManager.InitializeStockShaders();

    glEnable(GL_DEPTH_TEST);

    glClearColor(, , , );

    // 制造花圈
    gltMakeTorus(torusBatch, , , , );

    // 制造小球
    gltMakeSphere(sphereBatch, , , );

    //制造圓柱
    gltMakeCylinder(cylinderBatch, , , , , );

    //制造圓盤
    gltMakeDisk(diskBatch, , , , );

    floorBatch.Begin(GL_LINES, );
    for (GLfloat x = -; x <= ; x += ) {
        floorBatch.Vertex3f(x, -, );
        floorBatch.Vertex3f(x, -, -);

        floorBatch.Vertex3f(, -, x);
        floorBatch.Vertex3f(-, -, x);
    }
    floorBatch.End();

    // 設定3個材質物體
    glGenTextures(, uiTextures);

    // 加載材質 Marble并綁定
    glBindTexture(GL_TEXTURE_2D, uiTextures[]);
    LoadTGATexture("marble.tga", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_REPEAT);

    // 加載材質 Mars并綁定
    glBindTexture(GL_TEXTURE_2D, uiTextures[]);
    LoadTGATexture("marslike.tga", GL_LINEAR_MIPMAP_LINEAR,
        GL_LINEAR, GL_CLAMP_TO_EDGE);

    // 加載材質 Moon并綁定
    glBindTexture(GL_TEXTURE_2D, uiTextures[]);
    LoadTGATexture("moonlike.tga", GL_LINEAR_MIPMAP_LINEAR,
        GL_LINEAR, GL_CLAMP_TO_EDGE);

    // Randomly place the spheres
    for (int i = ; i < NUM_SPHERES; i++) {
        GLfloat x = ((GLfloat)((rand() % ) - ) * );
        GLfloat z = ((GLfloat)((rand() % ) - ) * );
        if (i %  == )
            disk[i / ].SetOrigin(x, , z);
        else
            cylinder[(i - ) / ].SetOrigin(x, , z);
    }

    GLfloat vetts[][];
    GLfloat vNorms[][];
    GLfloat vTexCoords[][];
    GLfloat angle = ;
    for (int i = ; i < ; i++) {
        angle += M3D_2PI / ;
        vetts[i][] = float(- + i*);
        vetts[i][] = float(sin(float(angle)));
        vetts[i][] = float(cos(float(angle)));

        vNorms[i][] = float(- + i*);
        vNorms[i][] = float(cos(float(angle)));
        vNorms[i][] = float(sin(float(angle)));

        vTexCoords[i][] = float(- + i*);
        vTexCoords[i][] = float(sin(float(angle)));
    }
    triangleBatch.BeginMesh();
    triangleBatch.AddTriangle(vetts, vNorms, vTexCoords);
    triangleBatch.End();

}

///
// Screen changes size or is initialized
void ChangeSize(int nWidth, int nHeight)
{
    glViewport(, , nWidth, nHeight);

    // 建立投影矩陣,并将它載入到投影矩陣堆棧中
    viewFrustum.SetPerspective(, float(nWidth) / float(nHeight), , );
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());

    // 設定變換管線以使用兩個矩陣堆棧 
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);

}
// 關閉渲染環境
void ShutdownRC(void)
{
    glDeleteTextures(, uiTextures);
}

// Called to draw scene
void RenderScene(void)
{
    // 顔色值
    static GLfloat vFloorColor[] = { , , ,  };
    static GLfloat vTorusColor[] = { , , ,  };
    static GLfloat vSphereColor[] = { , , ,  };
    static GLfloat vdiskColor[] = { , , ,  };
    static GLfloat vcylinderColor[] = { , , ,  };

    // 基于時間的動畫
    static CStopWatch   rotTimer;
    float yRot = rotTimer.GetElapsedSeconds() * ;

    // 清除顔色緩沖區和深度緩沖區
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


    // 儲存目前模型視圖矩陣 (機關矩陣)
    modelViewMatrix.PushMatrix();

    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    modelViewMatrix.PushMatrix(mCamera);

    // 将光源位置變換到視覺坐标系
    M3DVector4f vLightPos = { , , ,  };
    M3DVector4f vLightEyePos;
    m3dTransformVector4(vLightEyePos, vLightPos, mCamera);


    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    // 繪制背景
    shaderManager.UseStockShader(GLT_SHADER_FLAT,
        transformPipeline.GetModelViewProjectionMatrix(),
        vFloorColor);
    floorBatch.Draw();

    //綁定并使用紋理對象
    glBindTexture(GL_TEXTURE_2D, uiTextures[]);
    //繪制圓柱和圓盤
    for (int i = ; i < NUM_SPHERES; i++) {
        modelViewMatrix.PushMatrix();
        if (i %  == ) {
            modelViewMatrix.MultMatrix(disk[i / ]);
            shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(),
                transformPipeline.GetProjectionMatrix(), vLightEyePos, vdiskColor,);
            diskBatch.Draw();
        }
        else
        {
            modelViewMatrix.MultMatrix(cylinder[(i - ) / ]);
            shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(),
                transformPipeline.GetProjectionMatrix(), vLightEyePos, vcylinderColor, );
            cylinderBatch.Draw();

        }
        modelViewMatrix.PopMatrix();
    }


    // 繪制旋轉花托
    modelViewMatrix.Translate(, , -);

    // 儲存平移矩陣
    modelViewMatrix.PushMatrix();


    // 應用旋轉并繪制花托
    modelViewMatrix.Rotate(yRot, , , );
    //綁定并使用紋理對象
    glBindTexture(GL_TEXTURE_2D, uiTextures[]);
    shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(),
        transformPipeline.GetProjectionMatrix(), vLightEyePos, vTorusColor, );
    torusBatch.Draw();
    modelViewMatrix.PopMatrix(); // "清除" 以前的旋轉

                                 //繪制三角形
                                 //modelViewMatrix.PushMatrix();
    modelViewMatrix.Rotate(yRot, , , );
    shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(),
        transformPipeline.GetProjectionMatrix(), vLightEyePos, vTorusColor, );
    triangleBatch.Draw();
    //modelViewMatrix.PopMatrix(); 

    //綁定并使用紋理對象
    glBindTexture(GL_TEXTURE_2D, uiTextures[]);
    // 應用另一個旋轉,然後進行平移,然後再繪制球體
    modelViewMatrix.Rotate(yRot * -, , , );
    modelViewMatrix.Translate(, , );
    shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(),
        transformPipeline.GetProjectionMatrix(), vLightEyePos, vSphereColor, );
    sphereBatch.Draw();

    // 還原到以前的模型視圖矩陣 (機關矩陣)
    modelViewMatrix.PopMatrix();
    modelViewMatrix.PopMatrix();
    // 進行緩沖區交換
    glutSwapBuffers();

    // 通知GLUT在進行一次同樣操作
    glutPostRedisplay();
}


// Respond to arrow keys by moving the camera frame of reference
void SpecialKeys(int key, int x, int y)
{
    float linear = ;
    float angular = float(m3dDegToRad());

    if (key == GLUT_KEY_UP)
        cameraFrame.MoveForward(linear);

    if (key == GLUT_KEY_DOWN)
        cameraFrame.MoveForward(-linear);

    if (key == GLUT_KEY_LEFT)
        cameraFrame.RotateWorld(angular, , , );

    if (key == GLUT_KEY_RIGHT)
        cameraFrame.RotateWorld(-angular, , , );
}

int main(int argc, char* argv[])
{
    gltSetWorkingDirectory(argv[]);

    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(, );

    glutCreateWindow("OpenGL SphereWorld");

    glutSpecialFunc(SpecialKeys);
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);

    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return ;
    }

    SetupRC();
    glutMainLoop();
    ShutdownRC();
    return ;
}
           

運作結果:

除了地闆所有物體都加上了材質

OpenGL學習筆記4:紋理

繼續閱讀