Opengl 如何在GLSL片段着色器纹理中访问自动mipmap级别?

Opengl 如何在GLSL片段着色器纹理中访问自动mipmap级别?,opengl,textures,glsl,fragment-shader,mipmaps,Opengl,Textures,Glsl,Fragment Shader,Mipmaps,如何确定在GLSL片段着色器中采样纹理时使用的mipmap级别 我知道我可以使用textureLod(…)方法手动采样纹理的特定mipmap级别: uniform sampler2D myTexture; void main() { float mipmapLevel = 1; vec2 textureCoord = vec2(0.5, 0.5); gl_FragColor = textureLod(myTexture, textureCoord, mipmapLeve

如何确定在GLSL片段着色器中采样纹理时使用的mipmap级别

我知道我可以使用
textureLod(…)
方法手动采样纹理的特定mipmap级别:

uniform sampler2D myTexture;

void main()
{
    float mipmapLevel = 1;
    vec2 textureCoord = vec2(0.5, 0.5);
    gl_FragColor = textureLod(myTexture, textureCoord, mipmapLevel);
}
或者我可以允许使用
texture(…)
like自动选择mipmap级别

uniform sampler2D myTexture;

void main()
{
    vec2 textureCoord = vec2(0.5, 0.5);
    gl_FragColor = texture(myTexture, textureCoord);
}
我更喜欢后者,因为我更相信驾驶员对适当mipmap级别的判断,而不是我自己的判断

但我想知道自动采样过程中使用了什么mipmap级别,以帮助我合理地采样附近的像素。GLSL中是否有方法访问有关自动纹理采样使用的mipmap级别的信息?

来自:

看看第3.9.11章等式3.21。mip贴图级别根据导数向量的长度计算:

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);
}

以下是解决此问题的三种不同方法,具体取决于您可以使用哪些OpenGL功能:

  • 正如在评论中所指出的,OpenGL 4.00及以上版本的解决方案非常简单;只需使用
    textureQueryLod
    功能:

    #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);
    }
    
  • 在早期版本的OpenGL(2.0+?)中,您可能可以加载扩展,效果类似。这种方法适用于我的案例注意:方法调用在扩展中的大写形式与内置的不同(
    querytextrelod
    vs
    querytextrelod

  • 如果加载扩展不起作用,可以使用以下方法估计自动详细程度:


  • 在任何情况下,对于我的特定应用程序,我最终只是在主机端计算mipmap级别,并将其传递给着色器,因为自动细节级别并不是我所需要的。

    感谢您找到这一点。我看了一下规范,遗憾的是,它似乎在实际实现中留下了一些回旋余地,因此这种方法可能并不确切地被使用。此外,它不考虑GL_纹理\u最小\u LOD/GL_纹理\u最大\u LOD设置。请注意,代码段中的纹理坐标应该是非标准化的,即以像素为单位,而不仅仅是(0,1)。这是我自己手动指定LOD的一个良好开端。但我希望我有办法访问内部自动LOD值。@ChristopherBruns:你可以做一些非常简单的事情:为每个mip级别填充不同纯色/值的虚拟纹理,对其进行采样,然后将颜色/值转换回mip级别:)实际上,如果我们突然谈论GL 4.x,正是出于这个原因。与大多数隐式LOD纹理函数一样,它只在片段着色器中工作,因为它需要屏幕空间梯度(偏导数)来计算适当的mipmap LOD。@AndonM.Coleman哦,耶,textureQueryLod()看起来正是我想要的。我一直使用GLSL 3.30,但这可能会让我考虑GLSL 4。或者至少让我尝试加载一个扩展。有人应该将此纳入适当的答案中…您的目标是什么版本的GLSL?GLSL 4.00支持,这正是您想要的。如果可以的话,我想问几个问题:是否可以仅使用规范化UV纹理坐标中的dFdx和dFdy值来实现这一点?为什么我需要dFdx和dFdy的结果是二维变量?(如果我所需要的只是沿U或V坐标的导数/变化-常规浮点难道不够吗?)这些会是0,1,1/2,1/4,1/8……1/4096吗?@Nims标准化的UV坐标是不够的,因为缺少关于纹理采样精细程度的信息;选择最佳mipmap级别的关键信息。dFdx和dFdy是二维的,因为纹理坐标是二维的。就像你说的,你所需要的就是沿“U或V”坐标的导数。所以它给了你们两个。这回答了你的问题吗?谢谢你的回答,但我仍然不明白为什么导数是二维的,比如说,ddx只是测量沿U坐标的变化,沿V坐标没有变化,所以对于dx_vtc,第二个值是零。否则,同时拥有ddx和ddy有什么意义呢?这里有4个不同的偏导数:du/dx、dv/dx、du/dy和dv/dy。这四个值都不需要为零,除非纹理图像坐标恰好与显示屏轴完全对齐。我已经实现了建议的方法(在unity3d中-使用Cg)-当非常接近曲面时,计算mipmap级别的方法返回负数,这意味着在这种情况下,delta_max_sqr小于1。
    #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);
    }
    
    #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);
    }