opengltexturesglslfragment-shadermipmaps

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?


Solution

  • 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.