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...

相关推荐
byxdaz10 天前
Qt OpenGL 3D 编程入门
qt·opengl
byxdaz11 天前
Qt OpenGL 相机实现
opengl
二进制人工智能1 个月前
【OpenGL学习】(二)OpenGL渲染简单图形
c++·opengl
六bring个六1 个月前
qtcreater配置opencv
c++·qt·opencv·计算机视觉·图形渲染·opengl
爱看书的小沐1 个月前
【小沐学GIS】基于C++绘制二维瓦片地图2D Map(QT、OpenGL、GIS)
c++·qt·gis·opengl·glfw·glut·二维地图
六bring个六1 个月前
图形渲染+事件处理最终版
c++·qt·图形渲染·opengl
星火撩猿1 个月前
OpenGl实战笔记(3)基于qt5.15.2+mingw64+opengl实现光照变化效果
笔记·qt·opengl·光照效果
星火撩猿1 个月前
OpenGl实战笔记(2)基于qt5.15.2+mingw64+opengl实现纹理贴图
笔记·qt·opengl·纹理贴图
程序员爱德华1 个月前
计算机图形学中的深度学习
图形学·opengl
:mnong1 个月前
开放原子大赛石油软件赛道参赛经验分享
c++·qt·hdfs·开放原子·图形渲染·webgl·opengl