1. 程式人生 > >【OpenGL ES】紋理

【OpenGL ES】紋理

1、2D紋理

2D紋理是OpenGL ES中最基本和最常用的紋理形式,它是一個影象資料的二維陣列,紋理座標(s, t)或(u, v)用作紋理影象中的索引,代表用於查詢一個紋理貼圖的規範化座標。紋理影象的左下角由st座標(0.0, 0.0)指定,右上角由座標(1.0, 1.0)指定,在[0.0, 1.0]區間之外的座標是允許的,在該區間外的紋理讀取行為由紋理包裝模式定義。一個紋理的單獨資料元素稱作紋素Texel,即紋理元素Texture Pixels,由基本格式和資料型別而定,可以用許多不同的基本格式表現,如GL_RGB。

2、立方圖紋理

立方圖是一個由6個單獨的2D紋理面組成的紋理,形狀為立方體,最常用的是環境貼圖特效。環境貼圖特效,就是環境在物體上的倒影通過一個表示環境的立方圖進行渲染,這個立方圖通過在場景中央放置一個攝像機,從6個軸的方向(+X, -X, +Y, -Y, +Z, -Z)捕捉場景影象並將結果儲存在立方體的每個面來生成。立方圖紋素的讀取通過一個3D向量(s, t, r)作為紋理座標,在立方圖中查詢。用於紋理座標的3D向量與2D紋理的不同,通常不直接逐頂點地儲存在網格上,相反,立方圖通常使用法向量作為計算立方圖紋理座標的基礎來讀取。一般來說,法向量和一個來自眼睛的向量一起使用,計算出一個反射向量,然後用這個向量在立方圖中查詢。

3、3D紋理

3D紋理或稱為體紋理,可以看作2D紋理多個切片的一個數組,用座標(s, t, r)訪問,r座標選擇3D紋理中需要取樣的切片,(s, t)座標讀取每個切片中的2D貼圖。3D紋理中的每個mip貼圖級別包含上一個級別的紋理中的半數切片。

4、2D紋理陣列

2D紋理陣列與3D紋理很相似,使用紋理座標(s, t, r),但是也有差別,用途不同,例如,2D紋理陣列常常用於儲存2D影象的一個動畫,陣列的每個切片表示紋理動畫的一幀。對於過濾,3D紋理過濾發生在切片之間,而從2D紋理陣列中讀取只從一個單獨的切片取樣。對於mip貼圖,2D紋理陣列的每個2D切片完全獨立於其它切片,每個mip貼圖級別包含與上一級別相同的切片數量,而3D紋理的每個mip貼圖級別卻是上一個級別切片數量的一半。

5、紋理載入

void glGenTextures(GLsizei n,
    GLuint * textures);
void glDeleteTextures(GLsizei n,
    const GLuint * textures);
void glBindTexture(GLenum target,
    GLuint texture);
void glTexImage2D(GLenum target,
    GLint level,
    GLint internalFormat,
    GLsizei width,
    GLsizei height,
    GLint border,
    GLenum format,
    GLenum type,
    const
GLvoid * data); void glPixelStorei(GLenum pname, GLint param); void glTexParameteri(GLenum target, GLenum pname, GLint param);

紋理物件載入之前,首先要使用glGenTextures建立紋理物件,紋理物件是一個容器物件,儲存渲染所需的紋理資料,例如影象資料、過濾模式和包裝模式。紋理物件不再使用時,使用glDeleteTextures進行刪除。在使用紋理物件之前,還要通過glBindTexture進行繫結,繫結之後,就可以對紋理物件進行操作。用於載入2D和立方圖紋理的基本函式是glTexImage2D,引數data包含影象的實際畫素資料,資料必須包含一定的畫素個數,每個畫素根據格式和型別規範有相應的位元組數,畫素行必須對齊到用glPixelStorei設定的GL_UNPACK_ALIGNMENT。為了得到最佳的效能,建議使用不可變紋理。下面是一個簡單的例子,完整程式碼請參考https://github.com/geminy/aidear/tree/master/graphics/mu/examples/opengles3/Simple_Texture2D

   // Texture object handle
   GLuint textureId;

   // 2x2 Image, 3 bytes per pixel (R, G, B)
   GLubyte pixels[4 * 3] =
   {
      255,   0,   0, // Red
        0, 255,   0, // Green
        0,   0, 255, // Blue
      255, 255,   0  // Yellow
   };

   // Use tightly packed data
   glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 );

   // Generate a texture object
   glGenTextures ( 1, &textureId );

   // Bind the texture object
   glBindTexture ( GL_TEXTURE_2D, textureId );

   // Load the texture
   glTexImage2D ( GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels );

   // Set the filtering mode
   glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
   glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

例子中,pixels陣列用簡單的2x2紋理資料初始化,即width為2,height為2,每個pixel有3個位元組,共4x4x3等於12個位元組。當著色器從一個ubyte紋理分量讀取資料時,該值從[0, 255]區間被對映到浮點區間(0.0, 1.0)。一般來說,應用程式不會以這種簡單的方式建立紋理資料,而從一個影象檔案中載入資料。glPixelStorei設定了畫素儲存模式GL_UNPACK_ALIGNMENT為1,即解包對齊方式設定為1,表示位元組對齊。glPixelStorei設定的打包和解包選項是全域性狀態,不由紋理物件儲存,也不與之關聯。隨後,使用glGenTextures建立了一個紋理物件,使用glBindTexture將這個紋理物件繫結到了GL_TEXTURE_2D目標,使用glTexImage2D將影象資料載入到紋理物件。最後,使用glTexParameteri將縮小和放大過濾模式設定為GL_NEAREST,這是必需的,因為還沒有為紋理載入完整的mip貼圖鏈,所以選擇非mip貼圖縮小過濾器。另外,glTexImage2D還可以用於載入立方體紋理,使用glTexImage3D載入3D紋理和2D紋理陣列。例子的效果圖如下。
這裡寫圖片描述

6、mip貼圖

當使用glPixelStorex設定縮小和放大過濾器為GL_NEAREST時,一個紋素將在提供的紋理座標上讀取,這稱作點取樣或者最近取樣,最近取樣可能產生嚴重的視覺偽像,這是因為三角形在螢幕空間中變得較小,在不同畫素間的插值中,紋理座標有很大的跳躍,導致從一個大得紋理貼圖中取得少量樣本,造成鋸齒偽像,而且可能造成巨大的效能損失。解決這種偽像問題的方法是使用mip貼圖,基本思想是構建一個貼圖鏈,貼圖鏈始於原來指定的影象,後續的每個影象在每個維度上是前一個影象的一半,一直持續到最後達到鏈底部的1x1紋理。mip貼圖級別可以程式設計生成,一個mip級別中的每個畫素通常根據上一級別中相同位置的4個畫素的平均值計算,也就是盒式過濾。下面是一個使用盒式過濾技術生成mip貼圖鏈的例子,完整程式碼請參考https://github.com/geminy/aidear/tree/master/graphics/mu/examples/opengles3/MipMap2D

// Texture object handle
   GLuint textureId;
   int    width = 256,
          height = 256;
   int    level;
   GLubyte *pixels;
   GLubyte *prevImage;
   GLubyte *newImage;

   pixels = GenCheckImage ( width, height, 8 );

   if ( pixels == NULL )
   {
      return 0;
   }

   // Generate a texture object
   glGenTextures ( 1, &textureId );

   // Bind the texture object
   glBindTexture ( GL_TEXTURE_2D, textureId );

   // Load mipmap level 0
   glTexImage2D ( GL_TEXTURE_2D, 0, GL_RGB, width, height,
                  0, GL_RGB, GL_UNSIGNED_BYTE, pixels );

   level = 1;
   prevImage = &pixels[0];

   while ( width > 1 && height > 1 )
   {
      int newWidth,
          newHeight;

      // Generate the next mipmap level
      GenMipMap2D ( prevImage, &newImage, width, height,
                    &newWidth, &newHeight );

      // Load the mipmap level
      glTexImage2D ( GL_TEXTURE_2D, level, GL_RGB,
                     newWidth, newHeight, 0, GL_RGB,
                     GL_UNSIGNED_BYTE, newImage );

      // Free the previous image
      free ( prevImage );

      // Set the previous image for the next iteration
      prevImage = newImage;
      level++;

      // Half the width and height
      width = newWidth;
      height = newHeight;
   }

   free ( newImage );

   // Set the filtering mode
   glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST );
   glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

例子中,原始畫素資料由GenCheckImage函式生成。生成mip貼圖鏈的程式碼由GenMipMap2D函式提供,這個函式以一個RGB8影象作為輸入,在前面的影象上執行盒式過濾,生成下一個mip貼圖級別。mip貼圖鏈用glTexImage2D載入。載入mip貼圖鏈之後,便可以使用過濾模式,以使用mip貼圖,結果是實現了螢幕畫素和紋理畫素間的更好比率,從而減少了鋸齒偽像,影象的鋸齒也減少了,這是因為mip貼圖鏈中的每個影象連續進行過濾,使得高頻元素隨著貼圖鏈的下移而越來越少。例子中的貼圖鏈是手動建立的,OpenGL ES 3.0提供了自動生成貼圖鏈的函式glGenerateMipMap。例子的效果圖入如下,左邊為GL_NEARST,右邊為GL_LIEAR_MAPMAP_LINEAR即三線性過濾,產生所有模式中最佳的質量。
這裡寫圖片描述
渲染髮生時,發生兩種過濾,縮小和放大。縮小發生在螢幕上投影的多邊形小於紋理尺寸的時候,放大發生在螢幕上投影的多邊形大於紋理尺寸的時候。過濾器型別的確定由硬體自動處理,但是API提供了對每種情況下使用的過濾型別的控制。對於放大,mip貼圖不起作用,因為我們總是從最大的可用級別進行取樣。對於縮小,可以使用不同的取樣模式,所用模式的選擇基於需要實現的顯示質量水平以及為了紋理過濾願意損失縮少效能。在紋理縮小模式中,只有GL_NEARST和GL_LIEAR不需要為紋理指定完整的貼圖鏈,其它所有模式都要求紋理存在完整的貼圖鏈。在實際的使用中,需要平衡不同的過濾模式帶來的質量效果以及由此而帶來的效能代價。

在OpenGL ES 2.0中,當線性過濾核心落到立方圖邊緣時,過濾將只發生在立方圖的一個面上,這將在立方圖各面之間的邊上造成偽像。在OpenGL ES 3.0中,支援無縫立方圖過濾,預設支援,也就是說,過濾核心跨越立方圖不只一個面時,核心將會從其覆蓋的每個面中獲得樣本,從而在立方圖各面的邊緣形成了更平滑的過濾。

7、紋理包裝

紋理包裝模式用於指定紋理座標超出[0.0, 1.0]範圍時所發生的行為,用glTexParameterx設定,可以為s、t、r座標單獨設定,分別對應於GL_TEXTURE_WRAP_S、GL_TEXTURE_WRAP_T、GL_TEXTURE_WRAP_R,模式包括GL_REPEAT重複紋理、GL_CLAMP_TO_EDGE限定讀取紋理的邊緣、GL_MIRRORED_REPEAT重複紋理和映象,其中r座標包裝僅用於3D紋理和2D紋理陣列。紋理包裝模式影響過濾行為。下面的例子使用了紋理包裝,完整示例程式碼請參照https://github.com/geminy/aidear/tree/master/graphics/mu/examples/opengles3/TextureWrap

   // Draw quad with repeat wrap mode
   glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
   glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
   glUniform1f ( userData->offsetLoc, -0.7f );
   glDrawElements ( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices );

   // Draw quad with clamp to edge wrap mode
   glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
   glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
   glUniform1f ( userData->offsetLoc, 0.0f );
   glDrawElements ( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices );

   // Draw quad with mirrored repeat
   glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT );
   glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT );
   glUniform1f ( userData->offsetLoc, 0.7f );
   glDrawElements ( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices );

例子中,用3種不同的包裝模式繪製正方形,紋理座標範圍為[-1.0, 2.0],左邊的為GL_REPEAT,紋理只是在[0, 1]區間之外重複,造成傾斜的圖案,中間的為GL_CLAMP_TO_EDGE,當紋理左邊超出[0, 1]的範圍時,紋理座標限定於來自紋理邊緣的樣本,右邊的為GL_MIRRORED_REPEAT,當紋理左邊超出[0, 1]的範圍時,影象被映象並重復,效果圖如下。
這裡寫圖片描述

8、紋理調配

紋理調配Swizzle控制輸入的R、RG、RGB或RGBA紋理中的顏色分量在著色器中讀取時如何對映到分量。例如,應用程式可能希望一個GL_RED紋理對映為(0, 0, 0, R)或者(R, R, R, 1)而不是預設的R, 0, 0, 1)。紋理調配通過glTexParameterx設定,對應的分量為GL_TEXTURE_SWIZZLE_R、GL_TEXTURE_SWIZZLE_G、GL_TEXTURE_SWIZZLE_B、GL_TEXTURE_SWIZZLE_A,紋理值來源可能分別是從R、G、B、A分量讀取的GL_RED、GL_GREEN、GL_BLUE、GL_ALPHA,或者設定為常數GL_ZERO、GL_ONE。

9、紋理細節級別

在某些應用中,在所有紋理mip貼圖級別可用之前就能夠開始顯示場景是很有用的。例如,通過資料連線下載紋理影象的GPS應用可以從最低級別的mip貼圖開始,在更高級別可用時再顯示它們。在OpenGL ES 3.0中,這可以通過使用glTexParameterx的多個引數來實現。GL_TEXTURE_BASE_LEVEL設定用於紋理的最大mip貼圖級別,預設情況下,該值為0,但是如果mip貼圖級別還不可用,則它可以設定為更高的值。同樣,GL_TEXTURE_MAX_LEVEL設定使用的最小mip貼圖級別,預設情況下,它的值為1000,超過了任何紋理可能具備的最大級別,但是可以將其設定為較小的值,以控制用以紋理的最小mip級別。為了選擇要用於渲染的mip貼圖級別,OpenGL ES自動計算一個細節級別LOD值,以確定從哪一個mip貼圖級別過濾,在三線性過濾中,控制每個mip貼圖使用的多少。應用程式還可以使用GL_TEXTURE_MIN_LOD和GL_TEXTURE_MAX_LOD控制最小和最大的LOD值。可以從基本和最大mip貼圖級別單獨控制LOD限制的一個原因是在新的mip貼圖級別可用時提供平滑過渡,僅僅設定紋理的基本和最大級別可能在新mip貼圖級別可用時造成間歇偽像,而插入LOG可以使這一過渡看起來更平滑。

10、深度紋理對比

glTexParameterx的紋理引數GL_TEXTURE_COMPARE_FUNC和GL_TEXTURE_COMPARE_MODE,提供了百分比漸進過濾PCF功能。在執行被稱作陰影貼圖的陰影技術時,片段著色器需要比較一個片段的當前深度值和深度紋理中的深度值,以確定陰影在片段之內還是之外。為了實現平滑的陰影邊緣效果,對深度紋理進行雙線性過濾是很有用的。但是,在過濾深度值時,我們希望過濾在取樣深度值與當前深度或者參考值比較之後發生。如果過濾在比較之前發生,我們將平均計算深度紋理中的值,這不能提供正確的結果。PCF提供了正確的過濾,將取樣的深度值與每個參考值比較,然後將這些比較的結果0或者1一起進行平均。GL_TEXTURE_COMPARE_MODE預設為GL_NONE,但是當它被設定為GL_COMPARE_REF_TO_TEXTURE時,(s, t, r)紋理座標中的r座標將於深度紋理的值進行比較,然後,比較的結果將成為陰影紋理讀取的結果,可能是0或者1,如果啟用紋理過濾,則為這些值的平均。GL_TEXTURE_COMPARE_FUNC設定比較函式,可以設定為GL_LEQUAL、GL_GEQUAL、GL_LESS、GL_GREATER、GL_EQUAL、GL_NOTEQUAL、GL_ALWAYS、GL_NEVER。

11、紋理格式

紋理格式包括規範化紋理、浮點紋理、整數紋理、共享指數紋理、sRGB紋理和深度紋理,有些格式支援渲染,有些格式支援過濾。

規範化紋理——
規範化指的是從片段著色器中讀取紋理時,結果將對映到[0.0, 1,0]或[-1.0, 1.0]範圍內。規範化紋理都可以過濾,有符號時不能渲染。

浮點紋理——
大部分浮點格式由16位半浮點資料或者32位浮點資料支援。OpenGL ES 3.0還引入了11/11/10 GL_R11F_G11F_B10F浮點格式,只能用於代表正值,目的是提供更高精度的三通道紋理,同時保持每個紋素的儲存量為32位,這種格式的使用可能達到比16/16/16 GL_RGB16F或32/32/32 GL_RGB32F更高的效能。浮點紋理不強制用作渲染目標,只強制16位半浮點資料可以過濾。

整數紋理——
整數紋理中的值在片段著色器讀取時仍為整數。整數紋理不可過濾,但是R、RG和RGBA變種可以用作幀緩衝區物件中渲染的顏色附著。使用整數紋理作為顏色附著的時候,整數渲染目標不可能進行混合,忽略Alhpa混合狀態。用於從整數紋理讀取並輸出到整數渲染目標的片段著色器應該使用對應該格式的有符號或者無符號整數型別。

共享指數紋理——
共享指數紋理為不需要浮點紋理使用的那麼多深度位數的大範圍RGB紋理提供了一種儲存方式,通常用於高動態範圍HDR影象,不需要半浮點或者全浮點資料。OpenGL ES 3.0中的共享指數紋理格式是GL_RGB9_E5,3個RGB分量共享一個5位的指數,5位的指數隱含地由數值15調整。

sRGB紋理——
sRGB是一個非線性顏色空間,大約遵循一個冪函式,大部分影象實際上都儲存為sRGB顏色空間,這種非線性解釋了人類能夠在不同的亮度級別上更好地區分顏色這一事實。如果用於紋理的影象是以sRGB顏色空間創作的,但是沒有使用sRGB紋理讀取,那麼所有發生在著色器中的照明計算都會在非線性顏色空間中進行。為了正確地處理sRGB影象,應用程式應該使用一個sRGB紋理格式,這種格式在著色器中讀取時將從sRGB轉換為線性顏色空間,然後,著色器中的所有計算都將線上性顏色空間中完成。最後,通過渲染到一個sRGB渲染目標時,影象將會自動地轉換回sRGB。可以使用著色器命令pow(value, 2.2)進行近似的sRGB到線性的轉換,然後用pow(value, 1/2.2)進行近似的線性到sRGB的轉換。然後,儘可能使用sRGB紋理是最好的做法,因為這樣減少了著色器指令數量,並且提供更準確的sRGB轉換。

深度紋理——
深度紋理允許應用程式從幀緩衝區物件的深度附著中讀取深度值和可選的模板值,這在各種高階渲染演算法中很有用,包括陰影貼圖。

12、著色器與紋理

下面是在著色器中使用紋理的一個例子。

// vertex
#version 300 es
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec2 a_texCoord;
out vec2 v_texCoord;
void main()
{
    gl_Position = a_position;
    v_texCoord = a_texCoord;
}
// fragment
#version 300 es
precision mediump float;
in vec2 v_texCoord;
layout(location = 0) out vec4 outColor;
uniform sampler2D s_texture;
void main()
{
    outColor = texture( s_texture, v_texCoord );
}

例子中,頂點著色器以一個二分量vec2紋理座標a_texCoord作為頂點輸入,並將其作為輸出v_texCoord傳遞給片段著色器,片段著色器消費該紋理座標,並將其用於紋理讀取。片段著色器宣告一個型別為sampler2D的統一變數s_texture,取樣器是用於從紋理貼圖中讀取的特殊統一變數,將載入一個指定紋理繫結的紋理單元的數值。例如,通過glUniformi指定取樣器s_texture為數值0,表示用glActiveTexture啟用紋理時引數為GL_TEXTURE0,從0開始,最大為31。glActiveTexture設定當前紋理單元,以便後續的glBindTexture將紋理繫結到當前活動單元。texture內建函式返回一個代表從紋理貼圖中指定位置讀取顏色的vec4。

13、紋理壓縮

我們可以載入壓縮過的紋理影象資料,通過glCompressedTexXxx完成。紋理壓縮,可以減少紋理在裝置上的記憶體佔用,節約著色器中讀取紋理時消耗的記憶體頻寬,減少應用程式載入紋理的大小。在OpenGL ES 2.0中,核心規範不定義任何壓縮的紋理影象格式,因此,許多供應商提供了許多特定於硬體的紋理壓縮擴充套件,它們並不相容。OpenGL ES 3.0引入了所有供應商必須支援的標準紋理壓縮格式,ETC2和EAC,不支援3D紋理,但是glCompressedTexImage3D可以用於載入供應商專用的3D紋理壓縮格式。獲取實現支援的紋理壓縮格式,可以用glGetIntergerv查詢GL_NUM_COMPRESSED_TEXTURE_FORMATX、GL_COMPRESSED_TEXTURE_FORMATS。

14、紋理更新

用glTexImage2D載入紋理圖象之後,可以更新影象的各個部分,更新影象的一個子區域使用glTexSubImage2D,還支援其它的3D紋理、壓縮紋理的子區域更新。

15、從顏色緩衝區複製紋理資料

紋理資料的來源可以從顏色緩衝區複製,使用渲染的結果作為紋理中的影象。從顏色緩衝區複製資料到紋理,對應的函式為glReadBuffer、glCopyTexImage2D等,glReadBuffer指定讀取的顏色緩衝區,可能為GL_BACK、GL_COLOR_ATTACHMENTi、GL_NONE。

16、取樣器物件

前面介紹了使用glTexParameteri設定紋理的過濾模式、座標包裝以及LOD,缺點是可能造成大量的不必要的API開銷,為了緩解這一問題,OpenGL ES 3.0引入了取樣器物件,將取樣器狀態與紋理狀態分離。所有可用glTexParameteri進行的設定,都可以對取樣器物件進行,可以在一次函式呼叫中與紋理單元繫結使用。取樣器物件可以用於許多紋理,從而降低API開銷。建立取樣器物件使用glGenSamplers,刪除取樣器物件使用glDeleteSamplers,繫結紋理與取樣器物件使用glBindSampler,設定取樣器物件屬性使用glSamplerParameterx。

緩衝區物件可以在伺服器端或者GPU記憶體中儲存資料,而不是在客戶端或者主機記憶體,這樣減少了從CPU到GPU的資料傳送,因而能夠改進效能,降低記憶體佔用率。OpenGL ES 3.0還引入了畫素解包緩衝區物件,它與GL_PIXEL_UNPACK_BUFFER目標繫結指定。

17、不可變紋理

OpenGL ES 3.0引入了另一種有助於改進應用程式效能的的功能是不可變紋理。應用程式glTexImage2D和glTexImage3D等函式獨立地指定紋理的每個mip貼圖級別,這對OpenGL ES驅動程式造成的問題是驅動程式在繪圖之前無法確定紋理是否已經完全指定,它必須檢查每個mip貼圖級別或者子影象的格式是否相符、每個級別的大小是否正確以及是否有足夠的記憶體,這種繪圖時檢查可能代價很高,而使用不可變紋理可以避免這種情況。不可變紋理,應用程式在載入資料之前指定紋理的格式和大小,之後,紋理格式變成不可變的,OpenGL ES驅動程式可以預先進行所有一致性和記憶體檢查。紋理不可變之後,應用程式只能通過glTexSubImage2D、glTexSubImage3D、glGenerateMipMap或者渲染到紋理載入影象資料。建立不可變紋理之前,先用glBindTexture繫結紋理,然後用glTexStorage2D或glTexStorage3D分配不可變儲存,之後可以使用glGetTexParameterx查詢GL_TEXTURE_IMMUTABLE_FORMAT為GL_TRUE。