OpenGL ES 高級進階:紋理數組

你們好,這是個人OpenGL ES 高級進階系列文章,在個人github上有一個與本系列文章對應的項目,歡迎關注,連接:github.com/kenneycode/…git

今天給你們介紹一下紋理數組,它是OpenGL ES 3.0引入的一個新特性,它能讓咱們以數組的方式往shader中傳遞紋理,咱們先來看看一個普通的fragment shadergithub

#version 300 es
precision mediump float;
layout(location = 0) out vec4 fragColor;
layout(location = 0) uniform sampler2D u_texture;
in vec2 v_textureCoordinate;
void main() {
    fragColor = texture(u_texture, v_textureCoordinate);
}
複製代碼

在這個shader中咱們聲明瞭一個uniform sampler2D變量,即咱們只能傳遞一個紋理,若是要傳遞多個紋理,就要聲明多個uniform sampler2D數組

#version 300 es
precision mediump float;
...
layout(location = 0) uniform sampler2D u_texture0;
layout(location = 1) uniform sampler2D u_texture1;
layout(location = 2) uniform sampler2D u_texture2;
...

複製代碼

這樣一方面會佔用多個紋理單元,另外一方面一旦shader定了,裏面支持的紋理數量也就定了,不利於各類數量的紋理,除非本身去生成shader。 紋理數組的出現讓咱們能夠像傳遞一個數組同樣將紋理傳給shader,咱們來看一下使用紋理數組時的shaderpost

// vertex shader
#version 300 es
precision mediump float;
layout(location = 0) in vec4 a_Position;
layout(location = 1) in vec3 a_textureCoordinate;
out vec3 v_textureCoordinate;
void main() {
    v_textureCoordinate = a_textureCoordinate;
    gl_Position = a_Position;
}

// fragment shader
#version 300 es
precision mediump float;
precision mediump sampler2DArray;
layout(location = 0) out vec4 fragColor;
in vec3 v_textureCoordinate;
layout(location = 0) uniform sampler2DArray u_texture;
void main() {
    fragColor = texture(u_texture, v_textureCoordinate);
}
複製代碼

咱們先來看fragment shader,能夠看到,sampler2D變成了sampler2DArray,表示它是一個數組,而後使用的時候,也是texture(u_texture, v_textureCoordinate),彷佛看越來和不使用紋理數組時同樣?其實不同,也許細心的朋友發現了差異,v_textureCoordinate此時是vec3而不是vec2了,咱們知道紋理座標是二維的,這裏vec3的第三維就是取對應的紋理,能夠理解成是數組的下標。 咱們還能看到,紋理數組不須要在shader先聲明數組的大小,它是在代碼裏控制的,這樣就很靈活,咱們來看看代碼是怎樣寫的,大部分的操做和使用普通紋理時同樣(可參考個人另外一篇文章:《Android OpenGL ES 2.0 手把手教學(6)- 紋理》),不同的地方是首先綁定紋理時的類型不同:spa

// 建立圖片紋理數組
// Create texture for image
val textures = IntArray(1)
GLES30.glGenTextures(textures.size, textures, 0)
imageTexture = textures[0]

// 注意這裏是GL_TEXTURE_2D_ARRAY
// Note that the type is GL_TEXTURE_2D_ARRAY
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D_ARRAY, textures[0])
複製代碼

普通紋理是按GL_TEXTURE_2D的類型綁定的,這裏是GL_TEXTURE_2D_ARRAY,另外,還要像給數組分配空間同樣給紋理數組分配大小:code

GLES30.glTexStorage3D(GLES30.GL_TEXTURE_2D_ARRAY, 1, GLES30.GL_RGBA8, 390, 270, 2)
複製代碼

好比在咱們的例子中,咱們建立了一個大小爲2的紋理數組,每一個紋理的大小是390*270,這裏用的APIglTexStorage3D,實際上還有一種紋理叫3D紋理,它和紋理數組有些相似,咱們使用紋理數組的時候,有些API就是使用了操做3D紋理時的APIorm

接下來咱們就把2張圖片加載到咱們的紋理數組中:cdn

// 經過glTexSubImage3D指定每層的紋理
// Specify the texture of each layer via glTexSubImage3D
for (i in 0 until 2) {
    val bitmap = Util.decodeBitmapFromAssets("image_$i.jpg")
    val b = ByteBuffer.allocate(bitmap.width * bitmap.height * 4)
    bitmap.copyPixelsToBuffer(b)
    b.position(0)
    GLES30.glTexSubImage3D(GLES30.GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, bitmap.width, bitmap.height, 1, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, b)
    bitmap.recycle()
}
複製代碼

這步操做和咱們使用單個紋理時很相似,單個紋理時是用glTexImage2D,這裏是用glTexSubImage3D而且須要指定當前是給紋理數組的哪層紋理載入數據。blog

咱們前面提到,v_textureCoordinate此時是vec3而不是vec2了,所以咱們傳的紋理座標也要相應的變化:圖片

// 紋理座標
// The texture coordinate
private val textureCoordinateData = floatArrayOf(
                                        0f, 1f, 0f, 
                                        0f, 0f, 0f, 
                                        1f, 0f, 0f, 
                                        0f, 1f, 0f, 
                                        1f, 0f, 0f, 
                                        1f, 1f, 0f,
                                        0f, 1f, 1f, 
                                        0f, 0f, 1f, 
                                        1f, 0f, 1f, 
                                        0f, 1f, 1f, 
                                        1f, 0f, 1f, 
                                        1f, 1f, 1f
                                    )
複製代碼

另外,在這裏我將頂點座標設置爲左下角和右上角,所以會在左下角和右上角分配渲染紋理數組中的第0個和第1個紋理,效果以下:

代碼在我githubOpenGLESPro項目中,本文對應的是SampleTextureArray,項目連接:github.com/kenneycode/…

感謝閱讀!

相關文章
相關標籤/搜索