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_2D 和 GL_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...

相关推荐
扶尔魔ocy3 天前
【qt openGL入门笔记】windows编译MinGW版本的osgearth(2.10.1)
qt·opengl·osg
扶尔魔ocy4 天前
【QT OpenGL】知识点编目及传送门
qt·opengl·osg
扶尔魔ocy17 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行并进行态势标绘(四)
qt·opengl·osg
:mnong18 天前
Web 三维可视化技术对比:ParaView Web vs 本项目 wasm + Three.js
前端·webgl·opengl·opencl·科学可视化
扶尔魔ocy19 天前
【qt openGL入门笔记】OSG实现显示飞机模型飞行的点迹(二)
qt·opengl·osg
扶尔魔ocy19 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行(三)
opengl·osg
扶尔魔ocy20 天前
【qt openGL入门笔记】OSG实现飞机模型在网格图上飞行
qt·opengl·osg
扶尔魔ocy24 天前
【qt openGL入门笔记】FPS相机
qt·opengl
扶尔魔ocy24 天前
qt openGL入门笔记】FPS相机和轨道相机
qt·opengl
扶尔魔ocy1 个月前
【qt openGL入门笔记】基础光照
qt·opengl