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/…

感谢阅读!

相关文章
相关标签/搜索