OpenGL ES 2.0 笔记 #6:Texture Unit

OpenGL 的 texture unit 概念,让我懵逼良久...对照原教程的代码,反复阅读揣摩 ES 2.0 spec 字句,才得以拼凑出完整的逻辑。只想说:太坑爹了!多的不说了。

首先,ES spec 中,texture image unit, texture unit, image unit 说的都是同一个东西。

其次,texture object 与 texture unit,是 2 个并列的存在,两...哦不,二者互不隶属。Texture object 和 texture unit 都有(或可以有)多个,例如:程序可以创建多个 texture object,同时,OpenGL 状态机包含若干固定数量的 texture unit,这个数量取决于具体的 OpenGL 实现。OpenGL 用 GL_TEXTURE0, GL_TEXTURE1, GL_TEXTURE2,... 来指代各个 unit。

再次,可能是嫌概念的迷惑性还不够,ES 2.0 又规定每个 texture unit 内有 2 个 target:GL_TEXTURE_2DGL_TEXTURE_CUBE_MAP。Texture unit 与 target 之间是隶属关系:target 自古以来就是 unit 的不可分割的一部分。Texture object 须通过 glBindTexture() 绑定到某一 unit 的某一 target 上。

最后,在 fragment shader 中,uniform texture 变量的值选择其对应的 unit,例如:若值为 0,则会选择 GL_TEXTURE0 unit;若值为 0 则选择 GL_TEXTURE1。此外,变量的类型则选择 unit 中的 target,例如:sampler2D 类型选择 GL_TEXTURE_2D

注意到,fragment shader 中的 uniform 与 texture object 之间没有直接关系,而是通过 texture unit 作为中间纽带建立起关联。一图以蔽之:

要点,

  • uniform 变量的值选择 texture unit
  • uniform 的类型选择 texture unit 的 target,如:sampler2D 选择 GL_TEXTURE_2D target
  • 调用 glBindTexture() 绑定 texture object 到 texture unit 的指定 target

OpenGL 提供 glActiveTexture() 函数,用来选择"当前 unit"。大多 texture 相关的函数,都隐含地针对当前 unit 进行操作,例如,

  • glBindTexture() 将 texture object 绑定到当前 unit 的指定 target
  • glTexImage2D() 将像素数据复制到当前 unit 的指定 target 所绑定的 texture object

在 OpenGL 初始状态下,当前 unit 为 GL_TEXTURE0,同时,shader 中 uniform 变量的默认值为 0。前一课的示例程序中,没有调用过 glActiveTexture() 设定当前 unit,也没有给 fragment shader 的 uniform sampler2D 变量进行赋值,其原因在于,OpenGL 初始状态恰巧形成了 uniform 变量、texture unit 与 texture object 之间的正确关系。

这一课的例程加载了 2 个 texture,并叠加应用到 3D 模型上:

C++ 复制代码
...

uniform sampler2D s_tex;
uniform sampler2D s_tex1;

void main()
{
    gl_FragColor = mix(texture2D(s_tex, v_tex), texture2D(s_tex1, v_tex), 0.2);
}

这里调用了 GLSL 内建函数 mix() 将 2 个颜色值进行混合。运行效果:

程序要设置两个 uniform 的值,建立与 texture unit 之间的关联:

C 复制代码
GLint uni_tex = glGetUniformLocation(prog, "s_tex");
GLint uni_tex1 = glGetUniformLocation(prog, "s_tex1");

// Use program before setting uniform/sampler value
glUseProgram(prog);

glUniform1i(uni_tex, 0);
glUniform1i(uni_tex1, 1);

接下来将 texture object 绑定到 texture unit:

C 复制代码
// Bind the 1st texture object to unit 0
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex0);
// Bind the 2nd texture object to unit 1
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, tex1);

特别注意在调用 glBindTexture() 前都须调用 glActiveTexture() 选择当前 unit。

上一课的例子所用的图片(木头)本身是 RGB24 格式,因此没有进行像素类型的转换,直接提交给 OpenGL。这次两张图片格式不同,没法偷懒了,程序中必须进行转换。其实也不难,因为 SDL2 已提供了转换函数:

C 复制代码
SDL_Surface *surf = ...

int w, h;
void *pixels;
{
    w = surf->w;
    h = surf->h;

    pixels = malloc(4 * w * h);

    int err = SDL_ConvertPixels(w, h, surf->format->format, surf->pixels, surf->pitch, SDL_PIXELFORMAT_RGBA32, pixels, w * 4);
    if (err)
    {
        ...
    }
}

SDL_FreeSurface(surf);

不论原始图片格式为何,这里统一转换为 RGBA32。RGBA32 格式有 alpha 通道,支持透明度。

创建 texture object 并复制数据的过程与之前一样,但这里有一个隐藏的细节,与前述当前 texture unit 的概念有关,需要多啰嗦两句。先看代码:

C 复制代码
typedef struct
{
    GLint format;
    int width;
    int height;
    void *pixels;
} texture_image_t;

static GLuint load_texture(const char *path)
{
    ...
    
    texture_image_t img = ...

    GLuint tex;
    glGenTextures(1, &tex);

    // Active unit does not matter here. We just need to copy texture data to texture object
    glBindTexture(GL_TEXTURE_2D, tex);
    glTexImage2D(GL_TEXTURE_2D, 0, img.format, img.width, img.height, 0, img.format, GL_UNSIGNED_BYTE, img.pixels);

    ...
    
    return tex;
}

注意到这里并没有调用 glActiveTexture() 来选择当前 unit。理由是:在创建 texture object 时,当前 unit 无所谓。不管当前 texture unit 具体是哪个,把 texture object 绑定到它,都可以完成复制 texture object 数据的任务。等到程序流程的后面,在设置 sampler2D 变量值以与 texture unit 建立对应关系时,才需要确保当前 texture unit 的正确。

完整程序代码在这里:gitlab.com/sihokk/lear...

相关推荐
起司锅仔3 天前
OpenGL ES 着色器(5)
android·安卓·opengl·着色器
起司锅仔4 天前
OpenGL ES MVP/变换投影矩阵(8)
android·安卓·opengl
起司锅仔5 天前
OpenGL ES 之EGL(6)
android·安卓·opengl
起司锅仔5 天前
OpenGL ES 纹理(7)
android·安卓·opengl
rainInSunny8 天前
Learn OpenGL In Qt之炫酷进度条
c++·qt·opengl
起司锅仔8 天前
OpenGL ES 绘制一个三角形(2)
android·安卓·opengl
我啥都会11 天前
顶点缓存对象(VBO)与顶点数组对象(VAO)
opengl
刘好念15 天前
[OpenGL]使用OpenGL绘制带纹理三角形
c++·计算机图形学·opengl
战术摸鱼大师19 天前
OpenGL(四) 纹理贴图
贴图·opengl
ansondroider21 天前
Android MediaPlayer + GLSurfaceView 播放视频
android·opengl·mediaplayer·glsurfaceview