user1364743
user1364743

Reputation: 5661

What is texture downsampling (downscaling) using OpenGL?

I did not found any tutorial that explains how to downscale a texture using OpenGL.

For example if I have a 1024x720 texture and I want to generate downscale by the factor 1/4 how to do it?

Here's a tutorial talking about downsampling.

UPDATE

I tried the following filtering code in my fragment shader:

vec4 DownSampleFrame(sampler2D uniformSampler)
{
    vec2 pixelOffset = vec2(1.0f/1024.0f, 1.0f/720.0f);

    vec3 downScaleColor = vec3(0.0f);
    {
        downScaleColor += texture(uniformSampler, vec2(TexCoords.x - 4.0f * pixelOffset.x, TexCoords.y)).xyz;
        downScaleColor += texture(uniformSampler, vec2(TexCoords.x + 4.0f * pixelOffset.x, TexCoords.y)).xyz;
        downScaleColor += texture(uniformSampler, vec2(TexCoords.x, TexCoords.y - 4.0f * pixelOffset.y)).xyz;
        downScaleColor += texture(uniformSampler, vec2(TexCoords.x, TexCoords.y + 4.0f * pixelOffset.y)).xyz;
        downScaleColor *= 0.25f;
    }
    return (vec4(downScaleColor, 1.0f));
}

Is this kind of code refers to downsampling algorithm?

Upvotes: 4

Views: 11201

Answers (2)

Reto Koradi
Reto Koradi

Reputation: 54642

There's a few aspects here. If you already have the texture at the full size, I can't really think of a good reason to create a downscaled texture from it. If you really want to sample it at less than the full resolution, you can just use it in a downscaled form.

Mipmapping

The main approach to use lower resolution versions of a texture in OpenGL is mipmapping. You can generate mipmaps for a texture texId with:

glBindTexture(GL_TEXTURE_2D, texId);
glGenerateMipmap(GL_TEXTURE_2D);

For the mipmaps to be used, the sampling parameters have to be set accordingly:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

If you sample the texture now, it will use downscaled version depending on the size of the geometry you're drawing.

Controlling mipmap levels

If you just do the above, the full resolution will still be used depending on the scaling of the texture in the output. You can prevent that by restricting which mipmap levels can be used. For example, if you want to exclude the lowest 4 mipmap levels from sampling:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 4);

Since each mipmap level is half the linear resolution of the previous one, this will sample at 1/16th of the linear resolution of the full texture.

Downscaling

If for some reason, you really want a downscaled texture, there are a couple of options. Say you want to create a new texture from level 4 of texId, and you created mipmaps as shown above. You can create an FBO with level 4 of this texture as the attachment:

GLuint fboId = 0;
glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboId);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                       GL_TEXTURE_2D, texId, 4);

And then copy the content into a new texture:

GLuint smallTexId = 0;
glGenTextures(GL_TEXTURE_2D, &smallTexId);
glBindTexture(GL_TEXTURE_2D, smallTexId);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, smallWidth, smallHeight, 0);

An alternate solution is to use glBlitFramebuffer():

GLuint fboIds[2] = {0};
glGenFramebuffers(2, fboIds);

glBindFramebuffer(GL_READ_FRAMEBUFFER, fboIds[0]);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                       GL_TEXTURE_2D, texId, 0);

glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboIds[1]);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                       GL_TEXTURE_2D, smallTexId, 0);

glBlitFramebuffer(0, 0, width, height,
                  0, 0, smallWidth, smallHeight,
                  GL_COLOR_BUFFER_BIT, GL_LINEAR);

In this case, you don't need mipmaps on the original texture. Even though the quality of the downsampling might not be very good if it is by more than a factor of 2.

Upvotes: 10

rpress
rpress

Reputation: 462

Without knowing your reasons why you want to downsample a texture, here are are the options I can think of:

  • if you need smaller textures, just use smaller ones; you can preprocess your larger textures in an offline process
  • normally the hardware does texture filtering and you can use MIP mapping; the images for the MIP mapping levels can come from the offline process

The offline process can use the approach from the linked tutorial to generate the lower MIP map levels, convoluting with a Gaussian filter first to get rid of the high frequencies and then throwing away 3/4 of the pixels (for the 2:1 downsampling case in both directions). If you know the pixels to throw away beforehand (e.g. downsampling by 2:1 in both directions) the convolution calculation for these pixels is wasted and can be left out.

You can certainly do it in a fragment shader but you have to make sure that the sampler is set to not filter (nearest neighbor).

Upvotes: 0

Related Questions