Opengl ES之纹理数组(2D纹理数组的使用)

今天的内容比较简单,给大家介绍一下纹理数组,它是OpenGL ES 3.0引入的一个新特性,它能让我们以数组的方式往shader中传递纹理。

2D纹理数组是OpenGL ES 3.0开始支持的纹理类型。通过使用2D纹理数组,在同一个着色器中使用多个2D纹理的情况下可以简化开发。 试想一下,如果没有2D纹理数组技术,当一个着色器中需要使用多个2D纹理时就需要声明多个采样器变量,而如果使用2D纹理数组则同样的功能只需声明一个sampler2DArray类型的变量即可,方便高效, 而且扩展性更强。

如果是单单使用2D纹理的话,一旦着色器程序确定了,那么内部可使用的纹理个数就确定了,而如果使用2D纹理数组的话,即使着色器程序确定了,我们依然可以按照不同的需求使用不同个数的纹理, 这就大大提高了我们编程的灵活性。

2D纹理数组的使用

2D纹理数组的使用和普通的纹理贴图步骤差不多,只是普通纹理使用绑定的是GL_TEXTURE_2D,而2D纹理数组绑定的类型是GL_TEXTURE_2D_ARRAY,同时2D纹理数组分配纹理空间时需要使用函数glTexImage3D而不是glTexImage2D

另外在使用2D纹理数组进行采样时,需要提供的纹理坐标有3个分量也就是vec3,前两个与普通2D纹理坐标含义相同,为S、T分量,第三个分量则为2D纹理数组中的索引。

下面是一个简单的使用2D纹理数组的片元着色器:

#version 300 es
precision mediump float;
precision mediump sampler2DArray;
out vec4 FragColor;
in vec3 TexCoord;
uniform sampler2DArray ourTexture;
void main()
{
    FragColor = texture(ourTexture, TexCoord);
}

可以看到纹理坐标使用的是三个分量变量,而且需要声明sampler2DArray精度类型。

以下是使用纹理数组绘制两张不同的图片的一个demo源码实例:


TextureArrayOpengl.cpp

// 顶点着色器
static const char *ver = "#version 300 esn"
                         "in vec4 aPosition;n"
                         "in vec3 aTexCoord;n"
                         "out vec3 TexCoord;n"
                         "void main() {n"
                         "  TexCoord = aTexCoord;n"
                         "  gl_Position = aPosition;n"
                         "}";

// 片元着色器
static const char *fragment = "#version 300 esn"
                              "precision mediump float;n"
                              "precision mediump sampler2DArray;n"
                              "out vec4 FragColor;n"
                              "in vec3 TexCoord;n"
                              "uniform sampler2DArray ourTexture; n"
                              "void main()n"
                              "{n"
                              "    FragColor = texture(ourTexture, TexCoord);n"
                              "}";


// 使用绘制两个三角形组成一个矩形的形式(三角形带)
// 第一第二第三个点组成一个三角形,第二第三第四个点组成一个三角形
const static GLfloat VERTICES[] = {
        0.5f,0.0f, // 右下
        0.5f,0.5f, // 右上
        0.0f,0.0f, // 左下
        0.0f,0.5f, // 左上

        0.0f,-0.5f, // 右下
        0.0f,0.0f, // 右上
        -0.5f,-0.5f, // 左下
        -0.5f,0.0f // 左上
};


// 贴图纹理坐标(参考手机屏幕坐标系统,原点在左上角)
//由于对一个OpenGL纹理来说,它没有内在的方向性,因此我们可以使用不同的坐标把它定向到任何我们喜欢的方向上,然而大多数计算机图像都有一个默认的方向,它们通常被规定为y轴向下,X轴向右
const static GLfloat TEXTURE_COORD[] = {
        1.0f,1.0f,0, // 右下
        1.0f,0.0f,0, // 右上
        0.0f,1.0f,0, // 左下
        0.0f,0.0f,0, // 左上

        1.0f,1.0f,1, // 右下
        1.0f,0.0f,1, // 右上
        0.0f,1.0f,1, // 左下
        0.0f,0.0f,1 // 左上

};

TextureArrayOpengl::TextureArrayOpengl():BaseOpengl() {
    initGlProgram(ver,fragment);
    positionHandle = glGetAttribLocation(program,"aPosition");
    textureHandle = glGetAttribLocation(program,"aTexCoord");
    textureSampler = glGetUniformLocation(program,"ourTexture");
    LOGD("program:%d",program);
    LOGD("positionHandle:%d",positionHandle);
    LOGD("textureHandle:%d",textureHandle);
    LOGD("textureSample:%d",textureSampler);
}

void TextureArrayOpengl::setPixel(void *data, int width, int height, int length) {
    LOGD("texture setPixel");
    glGenTextures(1, &textureId);

    // 绑定纹理
    glBindTexture(GL_TEXTURE_2D_ARRAY, textureId);
    // 为当前绑定的纹理对象设置环绕、过滤方式
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    // 使用glTexStorage3D搭配glTexSubImage3D也是可以的
    // 注意这里是GL_RGBA8而不是GL_RGBA
//    glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, width, height,2);
    glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, width, height,2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                 nullptr);
    glTexSubImage3D(GL_TEXTURE_2D_ARRAY,0,0,0,0,width,height,1,GL_RGBA,GL_UNSIGNED_BYTE,data);
    // 解绑定
    glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
}

// 主要先调用setPixel设置第一张纹理,在setPixel中声明了一个容量为2的纹理数组,先调用setPixel2的话是不行的哦

void TextureArrayOpengl::setPixel2(void *data, int width, int height, int length) {

    LOGD("texture setPixel2");

    // 绑定纹理
    glBindTexture(GL_TEXTURE_2D_ARRAY, textureId);
    // 为当前绑定的纹理对象设置环绕、过滤方式
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glTexSubImage3D(GL_TEXTURE_2D_ARRAY,0,0,0,1,width,height,1,GL_RGBA,GL_UNSIGNED_BYTE,data);
    // 解绑定
    glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
}

void TextureArrayOpengl::onDraw() {
    glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glUseProgram(program);

    // 激活纹理
    glActiveTexture(GL_TEXTURE2);
    glUniform1i(textureSampler, 2);

    // 绑定纹理
    glBindTexture(GL_TEXTURE_2D_ARRAY, textureId);

    /**
     * size 几个数字表示一个点,显示是两个数字表示一个点
     * normalized 是否需要归一化,不用,这里已经归一化了
     * stride 步长,连续顶点之间的间隔,如果顶点直接是连续的,也可填0
     */
    // 启用顶点数据
    glEnableVertexAttribArray(positionHandle);
    glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,0,VERTICES);

    // 纹理坐标
    glEnableVertexAttribArray(textureHandle);
    glVertexAttribPointer(textureHandle,3,GL_FLOAT,GL_FALSE,0,TEXTURE_COORD);

    // 4个顶点绘制两个三角形组成矩形
    glDrawArrays(GL_TRIANGLE_STRIP,0,8);

    glUseProgram(0);

    // 禁用顶点
    glDisableVertexAttribArray(positionHandle);
    if(nullptr != eglHelper){
        eglHelper->swapBuffers();
    }

    glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
}

TextureArrayOpengl::~TextureArrayOpengl() {
    LOGD("TextureArrayOpengl析构函数");
}

运行结果显示:

图片

注意事项

查阅相关资料解说使用2D纹理数组时,一般会使数组中的纹理保持相同的尺寸。

但是笔者在某些机型上实测发现只要加载的纹理大小不要超过glTexImage3D中声明的宽高也能正常加载使用,不知道如果尺寸不一只是否有兼容性问题, 实际开发中还是建议大家尽量保存纹理数组的尺寸大小都一致。

系列教程源码

https://github.com/feiflyer/NDK_OpenglES_Tutorial

关注我,一起进步,人生不止coding!!!

思想觉悟

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/19240.html

(0)

相关推荐

发表回复

登录后才能评论