如何使用textureLod在glsl中对mip级别进行采样?

Posted

技术标签:

【中文标题】如何使用textureLod在glsl中对mip级别进行采样?【英文标题】:How to sample a mip level in glsl using textureLod? 【发布时间】:2018-06-09 12:52:34 【问题描述】:

如何在 glsl 中使用 textureLod() 对 mip 级别进行采样?

据我所知,mipmap LOD只能通过顶点着色器“显式”访问(虽然不确定它是否在版本 420 中受支持,因为大多数文档已过时)。其次,您需要通过设置纹理参数来定义mipmap的细节层次,例如GL_TEXTURE_MAX_LEVELGL_TEXTURE_BASE_LEVEL

在我的代码中,我在调用 glCompressedTexImage2D 后定义了这些纹理参数:

glTexParameteri(texture_type, GL_TEXTURE_MIN_FILTER, min_filter);
glTexParameteri(texture_type, GL_TEXTURE_MAG_FILTER, mag_filter);
glTexParameteri(texture_type, GL_TEXTURE_MAX_LEVEL, 9);
glTexParameteri(texture_type, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(texture_type, GL_TEXTURE_WRAP_S, wrap_s);
glTexParameteri(texture_type, GL_TEXTURE_WRAP_T, wrap_t);

接下来,我使用这段代码为每个绑定每个纹理样本(例如反照率贴图等类型):

glActiveTexture(GL_TEXTURE0 + unit);    // Set active texture type
glBindTexture(GL_TEXTURE_2D, id);   // Bind the texture object

最后,这是我的着色器代码:

顶点:

#version 420 core

out vec3 _texcoord;
out vec4 _albedo_lod;

uniform sampler2D albedo;   // Albedo and specular map

void main()

    _texcoord = texcoord;
    _albedo_lod = textureLod(albedo, vec2(_texcoord.st), 2.0);

带有附加片段:

#version 420 core

layout(location = 0) out vec4 gAlbedo;  // Albedo texel colour

in vec3 _texcoord;
in vec4 _albedo_lod;

void main()

    gAlbedo = _albedo_lod;  // Assign albedo

现在由于某种原因,无论我输入什么 LOD 值,结果总是求助于这个:

这似乎是最后一个 mip 级别(尽管我输入了什么值)。请记住,我将 10 个 mip 级别打包为 .dds 文件。但是,当我通过纹理参数 GL_TEXTURE_BASE_LEVEL 手动设置基本 mip 级别时,它可以工作。

总而言之,为什么不使用textureLod 在glsl 中采样正确的mip 级别?这在 420 版中是否已被弃用?

编辑:这是加载dds文件的代码:

// This function imports a dds file and returns the dds data as a struct
inline GLuint LoadDds(std::vector<std::string> file, size_t &img_width, size_t &img_height, size_t &num_mips, GLvoid* data, GLint wrap_s, GLint wrap_t, GLint min_filter, GLint mag_filter, size_t texture_type, bool anistropic_filtering)


    // Create one OpenGL texture
    GLuint textureID;
    glGenTextures(1, &textureID);

    // "Bind" the newly created texture : all future texture functions will modify this texture
    glBindTexture(texture_type, textureID);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    for (unsigned int i = 0; i < file.size(); i++)  // For each image...
    
        FILE *fp;

        unsigned char header[124];
        unsigned int height;
        unsigned int width;
        unsigned int linearSize;
        unsigned int mipMapCount;
        unsigned int fourCC;
        unsigned int components;
        unsigned int format;
        unsigned int bufsize;
        unsigned char* buffer;


        /* try to open the file */
        errno_t err;
        err = fopen_s(&fp, file[i].c_str(), "rb");
        if (fp == NULL)
            return 0;

        /* verify the type of file */
        char filecode[4];
        fread(filecode, 1, 4, fp);
        if (strncmp(filecode, "DDS ", 4) != 0)
        
            fclose(fp);
            return 0;
        

        /* get the surface desc */
        fread(&header, 124, 1, fp);

        height = *(unsigned int*)&(header[8]);
        width = *(unsigned int*)&(header[12]);
        linearSize = *(unsigned int*)&(header[16]);
        mipMapCount = *(unsigned int*)&(header[24]);
        fourCC = *(unsigned int*)&(header[80]);
        bufsize = mipMapCount > 1 ? linearSize * 2 : linearSize;
        buffer = (unsigned char*)malloc(bufsize * sizeof(unsigned char));

        fread(buffer, 1, bufsize, fp);

        /* close the file pointer */
        fclose(fp);

        components = (fourCC == FOURCC_DXT1) ? 3 : 4;
        switch (fourCC)
        
        case FOURCC_DXT1:
            format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
            break;
        case FOURCC_DXT3:
            format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
            break;
        case FOURCC_DXT5:
            format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
            break;
        default:
            free(buffer);
            return 0;
        

        unsigned int blockSize = (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16;

        unsigned int offset = 0;
        for (unsigned int level = 0; level < mipMapCount && (width || height); ++level)
        
            unsigned int size = ((width + 3) / 4) * ((height + 3) / 4) * blockSize;
            glCompressedTexImage2D(texture_type != GL_TEXTURE_CUBE_MAP ? GL_TEXTURE_2D : GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, level, format, width, height,
                0, size, buffer + offset);

            if ((level < 1) && (i < 1))     // Only assign input variable values from first image
            
                img_width = width;  // Assign texture width
                img_height = height;    // Assign texture height
                data = buffer;  // Assign buffer data
                num_mips = mipMapCount;     // Assign number of mips
            

            offset += size;
            width /= 2;
            height /= 2;
        

        if (anistropic_filtering)   // If anistropic_filtering is true...
        
            GLfloat f_largest;  // A contianer for storing the amount of texels in view for anistropic filtering
            glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &f_largest);     // Query the amount of texels for calculation
            glTexParameterf(texture_type, GL_TEXTURE_MAX_ANISOTROPY_EXT, f_largest);    // Apply filter to texture
        

        if (!mipMapCount)
            glGenerateMipmap(texture_type); // Generate mipmap

        free(buffer);   // Free buffers from memory
    

    // Parameters
    glTexParameteri(texture_type, GL_TEXTURE_MIN_FILTER, min_filter);
    glTexParameteri(texture_type, GL_TEXTURE_MAG_FILTER, mag_filter);
    glTexParameteri(texture_type, GL_GENERATE_MIPMAP, GL_TRUE);
    glTexParameteri(texture_type, GL_TEXTURE_MAX_LEVEL, 9);
    glTexParameteri(texture_type, GL_TEXTURE_BASE_LEVEL, 0);
    glTexParameteri(texture_type, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(texture_type, GL_TEXTURE_WRAP_S, wrap_s);
    glTexParameteri(texture_type, GL_TEXTURE_WRAP_T, wrap_t);

    // Set additional cubemap parameters
    if (texture_type == GL_TEXTURE_CUBE_MAP)
        glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, wrap_s);

    return textureID;   // Return texture id

这是使用 NVIDIA 的 dds 插件生成的每个 mipmap 级别的图像:

【问题讨论】:

textureLod 不被 OpenGL 4 弃用。你也不需要设置 GL_TEXTURE_MAX_LEVELGL_TEXTURE_BASE_LEVEL 除非你想强制 OpenGL 使用现有 mipmap 的子集。 10000 的默认值应该没问题。是什么让您相信您的代码始终对 LOD 9 进行采样,而不管您为 textureLod 提供什么值? 请显示您上传或生成每个 mip 地图级别的代码。 抱歉回复晚了!我已经上传了整个 dds 加载器代码 ^ :) 谢谢。 Fibbles - 感谢您对这个主题有所了解。基本上,目标是使用每个 LOD 值作为立方体贴图卷积的粗糙度步骤,因此具有 10 个级别将允许每 0.1 R 1 个。TextureLod 是实时访问 mips 的唯一方法 - 尽管你可以看,由于它的低级三线性质量,结果被视为使用了最后一个级别。谢谢。 (我很想知道将纹理采样器换成插值器的实际好处,你试过分析它吗?:)) 【参考方案1】:

由于您对每个顶点进行采样,这似乎正是预期的行为。

您说 mip 级别参数没有影响,但据我所知,只有当像素密度低于顶点密度并且值开始平均时,差异才会明显。但是,如果您不存储整个 mipchain,这可能永远不会发生,因为最低分辨率可能仍然具有足够的定义(我无法从屏幕截图中真正看出,我只能猜测模型的镶嵌)。

由于您是手动生成 mipchain,尽管您可以轻松地为每个级别使用不同的平面颜色进行测试,并查看它们是否确实正确获取(实际上,如果您不确定导入器,它可能是值得的首先在像素着色器中尝试一下)。

【讨论】:

以上是关于如何使用textureLod在glsl中对mip级别进行采样?的主要内容,如果未能解决你的问题,请参考以下文章

对 glDrawElements 的多次调用是不是比在 GLSL 中对每个片段执行相同的计算更有效?

chunked lod pros and cons

如何在目标 c 中对目标的项目级 API 进行单元测试?

实验报告实验一 MIPS指令系统和MIPS体系结构

GLSL:如何使用投影矩阵计算光线方向?

如何使用 OpenGL 和 GLSL 改变颜色(C++)?