Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to access automatic mipmap level in GLSL fragment shader texture?

How do I determine what mipmap level was used when sampling a texture in a GLSL fragment shader?

I understand that I can manually sample a particular mipmap level of a texture using the textureLod(...) method:

uniform sampler2D myTexture;

void main()
{
    float mipmapLevel = 1;
    vec2 textureCoord = vec2(0.5, 0.5);
    gl_FragColor = textureLod(myTexture, textureCoord, mipmapLevel);
}

Or I could allow the mipmap level to be selected automatically using texture(...) like

uniform sampler2D myTexture;

void main()
{
    vec2 textureCoord = vec2(0.5, 0.5);
    gl_FragColor = texture(myTexture, textureCoord);
}

I prefer the latter, because I trust the driver's judgment about appropriate mipmap level more than I do my own.

But I'd like to know what mipmap level was used in the automatic sampling process, to help me rationally sample nearby pixels. Is there a way in GLSL to access the information about what mipmap level was used for an automatic texture sample?

like image 677
Christopher Bruns Avatar asked Jun 24 '14 13:06

Christopher Bruns


People also ask

How is the mipmap level chosen?

If the sampler is not using anisotropic filtering, the mipmap level is chosen by considering the gradient vector with the highest magnitude and finding a mipmap level where the texel size best fits that vector.

What is mipmap texture?

A mipmap is a sequence of textures, each of which is a progressively lower resolution representation of the same image. The height and width of each image, or level, in the mipmap is a power of two smaller than the previous level.

How do you show textures in OpenGL?

in display() function : GLuint texture; texture = LoadTexture("bubble. png"); glBindTexture(GL_TEXTURE_2D, texture);

Is mipmap good for performance?

Make sure that you use mipmapping. This is because mipmapping improves both performance and quality. Mipmapping improves the performance of the GPU. This is because the GPU does not need to render the full resolution texture on an object that is far away from the camera.


2 Answers

Below are three distinct approaches to this problem, depending on which OpenGL features are available to you:

  1. As pointed out by Andon M. Coleman in the comments, the solution in OpenGL version 4.00 and above is simple; just use the textureQueryLod function:

    #version 400
    
    uniform sampler2D myTexture;
    in vec2 textureCoord; // in normalized units
    out vec4 fragColor;
    
    void main()
    {
        float mipmapLevel = textureQueryLod(myTexture, textureCoord).x;
        fragColor = textureLod(myTexture, textureCoord, mipmapLevel);
    }
    
  2. In earlier versions of OpenGL (2.0+?), you might be able to load an extension, to similar effect. This approach worked for my case. NOTE: the method call is capitalized differently in the extension, vs. the built-in (queryTextureLod vs queryTextureLOD).

    #version 330
    
    #extension GL_ARB_texture_query_lod : enable
    
    uniform sampler2D myTexture;
    in vec2 textureCoord; // in normalized units
    out vec4 fragColor;
    
    void main()
    {
        float mipmapLevel = 3; // default in case extension is unavailable...
    #ifdef GL_ARB_texture_query_lod
        mipmapLevel = textureQueryLOD(myTexture, textureCoord).x; // NOTE CAPITALIZATION
    #endif
        fragColor = textureLod(myTexture, textureCoord, mipmapLevel);
    }
    
  3. If loading the extension does not work, you could estimate the automatic level of detail using the approach contributed by genpfault:

    #version 330
    
    uniform sampler2D myTexture;
    in vec2 textureCoord; // in normalized units
    out vec4 fragColor;
    
    // Does not take into account GL_TEXTURE_MIN_LOD/GL_TEXTURE_MAX_LOD/GL_TEXTURE_LOD_BIAS,
    // nor implementation-specific flexibility allowed by OpenGL spec
    float mip_map_level(in vec2 texture_coordinate) // in texel units
    {
        vec2  dx_vtc        = dFdx(texture_coordinate);
        vec2  dy_vtc        = dFdy(texture_coordinate);
        float delta_max_sqr = max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc));
        float mml = 0.5 * log2(delta_max_sqr);
        return max( 0, mml ); // Thanks @Nims
    }
    
    void main()
    {
        // convert normalized texture coordinates to texel units before calling mip_map_level
        float mipmapLevel = mip_map_level(textureCoord * textureSize(myTexture, 0));
        fragColor = textureLod(myTexture, textureCoord, mipmapLevel);
    }
    

In any case, for my particular application, I ended up just computing the mipmap level on the host side, and passing it to the shader, because the automatic level-of-detail turned out to be not exactly what I needed.

like image 168
Christopher Bruns Avatar answered Sep 23 '22 01:09

Christopher Bruns


From here:

take a look at the OpenGL 4.2 spec chapter 3.9.11 equation 3.21. The mip map level is calculated based on the lengths of the derivative vectors:

float mip_map_level(in vec2 texture_coordinate)
{
    vec2  dx_vtc        = dFdx(texture_coordinate);
    vec2  dy_vtc        = dFdy(texture_coordinate);
    float delta_max_sqr = max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc));
    return 0.5 * log2(delta_max_sqr);
}
like image 32
genpfault Avatar answered Sep 24 '22 01:09

genpfault