OpenGL ES 3.1 - 无法使用 glTexImage2D 创建不可变纹理

OpenGL ES 3.1 - Unable to create IMMUTABLE Textures with glTexImage2D

我正在尝试使用 glTexImage2D() 创建一个不可变的纹理,然后我可以使用 glBindImageTexture()

进行绑定

这是我的 C++ 代码:

GLuint id;
glGenTextures(1, &id);

glBindTexture(GL_TEXTURE_2D, id);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);


GLint width = 2046;
GLint height = 1086;

// Not sure what to put here 1, 2, 4 or 8 ?
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

GLint levels;
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, &levels);

for (int i = 0; i < levels + 1; i++)
{

    glTexImage2D(GL_TEXTURE_2D, i, GL_R32UI, width, height, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, NULL);
    width = glm::max(1, (width / 2));
    height = glm::max(1, (height / 2));

    // check OpenGL error
    GLenum err;
    while ((err = glGetError()) != GL_NO_ERROR) {
        ALOGE("ERROR_GL_TEXTURE_INIT: %i", err);
    }
}

GLint status;
glGetTexParameteriv(GL_TEXTURE_2D,GL_TEXTURE_IMMUTABLE_FORMAT,&status);
ALOGE("IMMUTABLE_TEXTURE: %i", status);

glBindTexture(GL_TEXTURE_2D, 0);

glBindImageTexture(0, id, 0, GL_FALSE, 0, GL_READ_WRITE, GL_R32UI);

// check OpenGL error
GLenum err;
while ((err = glGetError()) != GL_NO_ERROR) {
    ALOGE("ERROR_GL_TEXTURE_IMG-USE: %i", err);
}

因为我不需要 Mipmap 级别,所以我将 GL_TEXTURE_MAX_LEVEL 设置为 0
然后我调用 glTexImage2D() 来设置所有纹理的级别(这里只有一个)并且没有出现错误。

不幸的是
glGetTexParameteriv(GL_TEXTURE_2D,GL_TEXTURE_IMMUTABLE_FORMAT,&status)的结果是0,这意味着纹理不是不可变的

现在,当我尝试调用 glBindImageTexture() 时,会出现错误 1282,因为正如 khronos documentation 中所写,此 opengl 函数需要一个不可变 纹理。

对我做错了什么有什么想法吗?

提前致谢;)

编辑:

感谢你们抽出宝贵时间和提供的所有信息,但有些事情我不明白。

glTexStorage2D()人(khronos documentation)的这段让我很困惑:

The behavior of glTexStorage2D depends on the target parameter. When target is GL_TEXTURE_2D, calling glTexStorage2D is equivalent, assuming no errors are generated, to executing the following pseudo-code:

for (i = 0; i < levels; i++)
{
    glTexImage2D(target, i, internalformat, width, height, 0, format, type, NULL);
    width = max(1, (width / 2));
    height = max(1, (height / 2));
}

Since no texture data is actually provided, the values used in the pseudo-code for format and type are irrelevant and may be considered to be any values that are legal for the chosen internalformat enumerant. internalformat must be one of the sized internal formats given in Table 1, or one of the compressed internal formats given in Table 2 below. Upon success, the value of GL_TEXTURE_IMMUTABLE_FORMAT becomes GL_TRUE. The value of GL_TEXTURE_IMMUTABLE_FORMAT may be discovered by calling glGetTexParameter with pname set to GL_TEXTURE_IMMUTABLE_FORMAT. No further changes to the dimensions or format of the texture object may be made. Using any command that might alter the dimensions or format of the texture object (such as glTexImage2D or another call to glTexStorage2D) will result in the generation of a GL_INVALID_OPERATION error, even if it would not, in fact, alter the dimensions or format of the object.

我不清楚上面的段落是关于 glTexImage2D() 还是 glTexStorage2D()

此外,我的基本问题是:我们可以使用 glBindImageTexture() 绑定使用 glTexImage2D() 创建的纹理吗? (我看到了这两个函数实际一起使用的代码示例,例如 here

要分配不可变纹理存储,请使用 glTexStorage2D() 而不是 glTexImage2D()。所以你分配纹理内存的调用变成:

glTexStorage2D(GL_TEXTURE_2D, levels, GL_R32UI, width, height);

在这种情况下,您可以通过一次调用分配所有 mipmap 级别,因此您不需要当前拥有的循环。

此外,我不确定您对此有何期望:

GLint levels;
glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, &levels);

在您的代码中,您只是在进行此调用时创建了纹理。所以查询层数是没有意义的。如果你想要一个 mipmap 纹理,你必须根据大小计算你需要的 mipmap 的数量。如果不需要 mipmapping,只需将 1 作为第二个参数传递给 glTexStorage2D().


因为有一些 follow-up 问题是关于是否有其他方法可以创建不可变纹理,例如glTexImage2D(),答案是否定的。 ES 3.1 规范第 8.17 节标题为 "Immutable-Format Texture Images",从第 190 页开始,解释了如何创建不可变纹理。该部分中列出的唯一调用是 glTexStorage2D()glTexStorage3D()。它还特别提到这些调用将 GL_TEXTURE_IMMUTABLE_FORMAT 属性 设置为 true.

唯一提到它创建不可变纹理的其他调用是第 171 页第 8.8 节中的 glTexStorage2DMultisample()

这意味着 glTexImage2D() 创建的纹理 不是 不可变的。确认这是第 194 页标题为 "Texture State" 的第 8.18 节,其中列出了各种纹理属性的默认值:

In the initial state, [..] The value of TEXTURE_IMMUTABLE_FORMAT is FALSE.

由于glTexImage2D()的规范中没有提及更改值,因此它将保持为false。