天天看點

Android OpenGL_ES glActiveTexture和glBindTexture

Refrence:

  1. https://blog.csdn.net/n5/article/details/90045648
  2. https://blog.csdn.net/hankern/article/details/112058229
  3. https://blog.csdn.net/huazi5dgan/article/details/76160334
  4. https://blog.csdn.net/zhuyingqingfen/article/details/19968341
  5. https://blog.csdn.net/candycat1992/article/details/8974719

簡單的了解:GPU(顯示卡)中有N個

texture unit

紋理單元(具體數量依賴你的GPU能力),每個 texture unit 紋理單元(GL_TEXTURE0、GL_TEXTURE1…)都有GL_TEXTURE_1D、GL_TEXTURE_2D等

textureTarget

紋理目标接口。

Android系統預設有從

GL_TEXTURE0

GL_TEXTURE31

共32個紋理單元,不過不同的平台或不同的系統,可用的紋理單元數量是不同的。

比如,在Pixel3XL+Android11上,可用的TextureUnit共有96個:

// [GL_TEXTURE0 ~ GL_TEXTURE31 + 64]
// "(不在這個區間就會列印警告“bindTextureImage: clearing GL error: 0x500”)"
GLES20.glActiveTexture(GL_TEXTURE0); 
           

glActiveTexture函數

預設情況下目前活躍的紋理單元為0,即如果不調用

glActiveTexture

函數,預設使用紋理單元0。
struct TextureUnit
{
    GLuint targetTexture1D;
    GLuint targetTexture2D;
    GLuint targetTexture3D;
    GLuint targetTextureCube;
    ...
};
 
TextureUnit textureUnits[GL_MAX_TEXTURE_IMAGE_UNITS]
GLuint currentTextureUnit = 0;
           

從下面代碼中可以看到:當調用

glBindTexture

綁定紋理對象到紋理目标時,所作用的是目前活躍的紋理單元:

void glActiveTexture(GLenum textureUnit)
{
    currentTextureUnit = textureUnit  - GL_TEXTURE0 ;
}

void glBindTexture(GLenum textureTarget, GLuint textureObject)
{
    TextureUnit *texUnit = &textureUnits[currentTextureUnit];
    switch(textureTarget)
    {
    case GL_TEXTURE_1D: texUnit->targetTexture1D = textureObject; break;
    case GL_TEXTURE_2D: texUnit->targetTexture2D = textureObject; break;
    case GL_TEXTURE_3D: texUnit->targetTexture3D = textureObject; break;
    case GL_TEXTURE_CUBEMAP: texUnit->targetTextureCube = textureObject; break;
    }
}
           

glBindTexture函數

将一個命名的紋理綁定到一個紋理目标上

//這個更好了解 void glBindTexture(GLenum textureTarget, GLuint textureObject)
void glBindTexture(GLenum  target, GLuint  texture); 
           
  • target

    紋理對象要綁定到的目标。必須是下面中的一個:GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_1D_ARRAY, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_RECTANGLE, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_BUFFER, GL_TEXTURE_2D_MULTISAMPLE 或者 GL_TEXTURE_2D_MULTISAMPLE_ARRAY。

  • texture

    紋理對象(紋理對象配置設定号),取值範圍為從

    Integer.MIN_VALUE

    Integer.MAX_VALUE

    ,可通過

    glGenTextures

    得到。
int[] textures = new int[3];
GLES20.glGenTextures(textures.length, textures, 0);
// --------
// SurfaceTexture texture = new SurfaceTexture(textures[0]);
// texture.detachFromGLContext(); // 不調用會抛異常
// texture.attachToGLContext(textures[1]);
// --------
// 将surfaceTexture紋理對象附加到目前的GLContext上下文中
// surfaceTexture紋理對象在目前GLContext上下文中的配置設定号是textures[0]
surfaceTexure.attachToGLContext(textures[0]);
...
// 選擇目前活動的紋理單元,不調用此代碼預設使用紋理單元0
GLES20.glActiveTexture(GL_TEXTURE0);
// 将配置設定号為textures[0]的紋理對象綁定到目前活動紋理單元的紋理目标GLES11Ext.GL_TEXTURE_EXTERNAL_OES上
// 紋理對象配置設定号的取值為Integer.MIN_VALUE~Integer.MAX_VALUE
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[0]);
...
           

當一張紋理被綁定後,GL對于這個紋理目标的操作都會影響到這個被綁定的紋理。也就是說,這個紋理目标成為了被綁定到它上面的紋理的别名,而紋理名稱為0則會引用到它的預設紋理。之後可能還會調用如下方法:

glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,512,512,0,GL_RGBA, GL_UNSIGNED_BYTE,NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
           

這些函數裡面的GL_TEXTURE_2D就等價與我們之前綁定的紋理,是以我們對GL_TEXTURE_2D的操作就會影響到之前的紋理,這和C++中的引用有點類似。

glBindTexture的使用方法實際上展現在兩個地方:

  • 首先是第一次載入紋理對象時,将載入的紋理對象(int型的紋理配置設定号)與紋理單元綁定在一起。也就是給目前的紋理配置設定相應的紋理号。
  • 其次是載入多個紋理之後,需要在多個紋理之間進行切換,此時也需要使用glBindTexture函數進行切換。(這裡是有正确? 應該是可通過需要glActiveTexture切換)
  • 在openGL中,存在一系列的

    texture unit

    ,通過 glActiveTexture 選擇目前的

    texture unit

    ,預設的unit是0。而目前的

    texture unit

    中存在多個

    texture target

    ,例如GL_TEXTURE_2D, GL_TEXTURE_CUBEMAP…。
  • 通過

    glCreateTexture

    建立

    texture object

    後,第一次調用glBindTexture時,決定了

    texture object

    的類型,比如調用的是

    glBindTexture(GL_TEXTURE_2D,0)

    ,那麼這個

    texture object

    就是一個2D texture,其内部狀态被初始化為2D texture的狀态,它不能再被bind到其他類型的texture target上,否則會産生運作時錯誤。
  • 通過glBindTexture将一個texture object綁定到目前激活的texture unit的texture target上。然後通過glTexImage2D, glTexParameteri等函數改變texture object的狀态。
  • 建立texture object的時候需要指定texture unit嗎?

    并不需要,無論目前是哪個texture unit,不影響建立texture object。建立好的texture object可以綁定到其他texture unit的texture target上使用。

  • 什麼時候需要關心texture unit?

    當使用多重紋理的時候,也就是說在shader裡面要同時使用多于一個sampler的時候。通過glUniform1i将texture unit傳給sampler,讓sampler知道應該去哪個texture unit中擷取texture object,那麼應該擷取哪個texture target指向的texture object呢?這就要看sampler的類型了。比如sampler2D,就會擷取sampler被指向的texture unit中的GL_TEXTURE_2D texture targert。

總結:

openGL中紋理的狀态分為texture unit和texture object包含的狀态。texture unit的狀态包括目前激活的unit,每個unit下面的各個target分别指向哪些texture object。texture object的狀态包含type, texParam, format等等。

什麼時候需要調用glActiveTexture以及glBindTexture就要看狀态是否會改變。

對于shader來說,他可以通路所有的texture unit中指定的texture object。隻要你告訴他某個sampler使用哪個unit就行。如果每個unit的内容指定後不需要改變,則即便shader使用了多個sampler也不需要來回切換unit的狀态。當然更常見的是渲染完一個pass後,需要改變目前texture unit中某target中的texture object,也就是需要換貼圖了。那麼标準的操作就是先glActiveTexture,然後glBindTexture。當然如果你隻使用unit0,則不需要調用glActiveTexture。

  • glActiveTexture負責選擇目前活躍的紋理單元
  • glBindTexture負責使用紋理單元中的某一種紋理類型

一個紋理單元可以有多種紋理類型GL_TEXTURE_1D、GL_TEXTURE_2D等,但是隻能使用一種,不可以多種同時使用。或者可以了解為紋理單元有多種綁定點,但一次隻能綁定到一種類型上。給着色器上傳的是紋理單元号。

調用順序:先調用glActiveTexture,再調用glBindTexture

如果使用了幀緩沖區,則調用順序為glBindFramebuffer、glBindTexture,不需要glActiveTexture

glBindFramebuffer後調用glActiveTexture會有什麼效果,應該沒有直接影響,但是glActiveTexture會影響紋理貼圖,進而會影響渲染到幀緩沖區中的内容。

也就是glBindFramebuffer是确定畫布,glActiveTexture是确定繪畫的顔料。

Android OpenGL_ES glActiveTexture和glBindTexture

繼續閱讀