Thanks, knackered. Apparently, the MIN filter does matter (is mip map the default?), but my problem was that I was calling glTexImage2D() with the wrong parameters.
I was calling glTexImage2D() with GL_COMPRESSED_RGB_S3TC_DXT1_EXT instead of GL_RGB for the “format” parameter.
Here’s my testing code for anyone that might bump into similar problems:
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, texHandle);
unsigned char red_pixels[4][4][4];
for (int i=0; i < 4; ++i) {
for (int j=0; j < 4; ++j) {
red_pixels[i][j][0] = 0xff;
red_pixels[i][j][1] = 0x00;
red_pixels[i][j][2] = 0x00;
red_pixels[i][j][3] = 0xff;
}
}
unsigned char red_dxt1[4 * 4 / 2] =
{ 0x00, 0xf8, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00 };
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
static int chooser = 2;
switch (chooser) {
case 0:
// Load uncompressed image in one shot.
// Should work.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, (void *)red_pixels);
break;
case 1:
// Load compressed image in one shot.
// Should work.
glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0, 8, (void *)red_dxt1);
break;
case 2:
// Make space for uncompressed image, then load through subtexture region.
// Should work.
glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 8, (void *)red_dxt1);
break;
case 3:
// Just load subtexture region.
// Should NOT work.
glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, 8, (void *)red_dxt1);
break;
}
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(-1, -1);
glVertex3f(-1, -1, 0.5);
glTexCoord2f(1, -1);
glVertex3f(1, -1, 0.5);
glTexCoord2f(-1, 1);
glVertex3f(-1, 1, 0.5);
glTexCoord2f(1, 1);
glVertex3f(1, 1, 0.5);
glEnd();