Why don’t linear filtering and mipmaps work for cubemap texture?

I used the same method for mipmap generation that works for a 2D texture, but it doesn’t seem to work for a cubemap texture. The cubemap appears pixelated. How to fix it?

glGenTextures(1, &texture1); // texture1 = 1
{
  glBindTexture(GL_TEXTURE_CUBE_MAP, texture1);
  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA8, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
  glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA8, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA8, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
  glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA8, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA8, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
  glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA8, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
}

// [copyBufferToImage]
glBindTexture(GL_TEXTURE_CUBE_MAP, texture1);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, 5);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
{
  // region = 0: (bufferOffset = 0, copySize = 65536)
  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA8, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, <ptr>);
  // region = 1: (bufferOffset = 65536, copySize = 65536)
  glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA8, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, <ptr>);
  // region = 2: (bufferOffset = 131072, copySize = 65536)
  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA8, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, <ptr>);
  // region = 3: (bufferOffset = 196608, copySize = 65536)
  glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA8, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, <ptr>);
  // region = 4: (bufferOffset = 262144, copySize = 65536)
  glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA8, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, <ptr>);
  // region = 5: (bufferOffset = 327680, copySize = 65536)
  glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA8, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, <ptr>);
}

// [generateMipmap]
glBindTexture(GL_TEXTURE_CUBE_MAP, texture1);
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);

Shader:

vec3 color = texture(samplerEnv, cubemapCoord).rgb; // And the result is in the image below, it's pixelated but why?

The cube map texture is being magnified, so mipmaps won’t have any effect (mipmaps are only used for minification). The magnification filter appears to be set to GL_NEAREST.

128×128 isn’t a particularly high resolution, so it’s bound to look either pixelated or blurred (depending upon the filter) when magnified.

I double-checked the code, and with target = GL_TEXTURE_CUBE_MAP (0x8513), the GL_TEXTURE_MAG_FILTER parameter is indeed set to GL_LINEAR. So, why does it look like it’s not set to that? Maybe it failed to be set correctly. What could cause this failure?

My goal is to create smooth reflection using textureLod.

The cubemap filter works with a very simple OpenGL code based on this tutorial: LearnOpenGL - Cubemaps

Now, I’m very confused because I copied the exact code into my actual project, and the filter doesn’t work there. Is it an OpenGL bug, or is there a parameter somewhere in my project that is disabling the cubemap filter? It’s difficult to determine. Does anyone know?

glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); // Global

glGenTextures(1, &g_cubemapTexture);
glBindTexture(GL_TEXTURE_CUBE_MAP, g_cubemapTexture);

uint32_t width  = 8;
uint32_t height = 8;
uint8_t data[] = {
    0x00,0x00,0x00, 0x00,0x00,0x00, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,
    0x00,0x00,0x00, 0x00,0x00,0x00, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0x00,0x00,0x00, 0x00,0x00,0x00,
    0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0x00,0x00,0x00, 0x00,0x00,0x00,
    0x00,0x00,0x00, 0x00,0x00,0x00, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,
    0x00,0x00,0x00, 0x00,0x00,0x00, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,
    0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0x00,0x00,0x00, 0x00,0x00,0x00,
    0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0x00,0x00,0x00, 0x00,0x00,0x00, 0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF, 0x00,0x00,0x00, 0x00,0x00,0x00,
};

for (int i = 0; i < 6; i++) {
    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

//---------- Per frame ----------

...
glBindTexture(GL_TEXTURE_CUBE_MAP, g_cubemapTexture);
...
glDrawElements(GL_TRIANGLES, 3, ...);
...

Fragment Shader (Triangle) and the filter works:

layout (location = 0) in vec2 _uv;
layout (location = 1) in vec3 _color;
uniform samplerCube skybox;

layout (location = 0) out vec4 outColor;

void main() {
    outColor.rgb = vec4(_color, 1.0).rgb * texture(skybox, vec3(_uv * 4.0 - vec2(2.0), 1)).rgb;
}

Fragment Shader (Actual project) and the filter doesn’t work:

...
layout (binding = 6) uniform samplerCube samplerEnv;
...
layout (location = 0) out vec4 outColor;

void main() {
    ...
    vec3 color;
    ...
    color = texture(samplerEnv, cubemapCoord).rgb;
    ...
    outColor = vec4(color, 1);
}

I solved the problem. I believe that the cube map parameters are not saved in the bound texture during creation; they must be set per frame before drawing the primitive. It took me a while to realize this.

// Per frame
glActiveTexture(GL_TEXTURE...);
glBindTexture(GL_TEXTURE_CUBE_MAP, g_cubemapTexture);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);

This is incorrect; if this solved your problem, it’s only by accident. Texture parameters are part of the texture object. So its likely that you did something wrong when you first tried to set them.

1 Like

@Alfonse_Reinheart
Okay, you’re right. However, I notice that in Vulkan, the sampler is independent of the image (or texture). There is something called CombinedImageSampler, as the same image can be used multiple times with different samplers. Therefore, I prefer to set the texture parameters just before rendering the primitive, as the bound image might have a different sampler (or parameters). I don’t mind a slight performance impact because OpenGL is used for educational purposes as I build my own rendering API with various backends (Vulkan, OpenGL, etc.), with Vulkan being the final backend.

Separate sampler objects exist in OpenGL as well, see wiki.

1 Like

Thanks, I didn’t know that changing the texture parameters every time I draw a primitive has a performance problem. I’ll consider using samplers with the OpenGL backend in the future.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.