今天的内容比较简单,给大家介绍一下纹理数组,它是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