我使用 STB 映像加载映像时出错

I have an error loading an image with STB image

提问人:Nico Cano 提问时间:9/13/2023 最后编辑:Nico Cano 更新时间:9/14/2023 访问量:93

问:

我的问题是,在使用库 STB 图像加载任何类型的图像时,我收到下一个错误:BAD PNG SIG,即使它甚至不是 PNG,所有代码在我尝试制作 OBJECT 类之前都有效,因为我正在学习 OpenGL,现在我得到的结果是下一个:

enter image description here

首先,除了我应该看到两个纹理而不仅仅是板条箱纹理之外,我没有明显的错误,

如果我运行程序但具有不同的纹理,则发生的另一件事是我得到一个debugBreak:
enter image description here

纹理生成/加载的不同部分的代码如下:

纹理创建/添加:

TEXTURE tex1("res/textures/container.jpg", FILTER_LINEAR, GL_RGB, GL_TEXTURE_2D);

TEXTURE tex2("res/textures/dog.jpg", FILTER_NEAREST, GL_RGBA, GL_TEXTURE_2D);

box1.AddTextures(tex1);

box1.AddTextures(tex2);

这是纹理类:

class TEXTURE{

private:

    unsigned int m_TextureID;
    std::string m_filepath;
    int m_width, m_height, m_BPP;
    unsigned int m_TextureType;

public:

    TEXTURE(std::string filepath, FILTERING_OPTIONS opt, unsigned int RGB_CHANNEL, unsigned int TEXTURE_TYPE, bool FLIP_IMAGE = 0, bool MIPMAP = 1);
    ~TEXTURE();


    void Bind(unsigned int slot = 0);

    void Unbind();


    inline int GetWidth() const { return m_width; }
    inline int GetHeight() const { return m_height; }

};

它的构造函数是下一个:

TEXTURE::TEXTURE(std::string filepath, FILTERING_OPTIONS opt, unsigned int RGB_CHANNEL, unsigned int TEXTURE_TYPE, bool FLIP_IMAGE, bool MIPMAP) : m_filepath(filepath), m_TextureType(TEXTURE_TYPE){

    //stbi_set_flip_vertically_on_load(FLIP_IMAGE);
    stbi_set_flip_vertically_on_load(1);
    glCall(glTexParameteri(TEXTURE_TYPE, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT));
    glCall(glTexParameteri(TEXTURE_TYPE, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT));

    float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
    glCall(glTexParameterfv(TEXTURE_TYPE, GL_TEXTURE_BORDER_COLOR, borderColor));


    if (MIPMAP) {

        switch (opt)
        {
        
        case FILTER_NEAREST:
            glCall(glTexParameteri(TEXTURE_TYPE, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR));
            glCall(glTexParameteri(TEXTURE_TYPE, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
            break;
        
        case FILTER_LINEAR:
            glCall(glTexParameteri(TEXTURE_TYPE, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR));
            glCall(glTexParameteri(TEXTURE_TYPE, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
            break;
        
        case BOTH:
            glCall(glTexParameteri(TEXTURE_TYPE, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR));
            glCall(glTexParameteri(TEXTURE_TYPE, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
            break;
        
        default:

            std::cerr << "[Error]: Wrong option parameter" << std::endl;

            return;
            break;
        }

    }
    else {

        switch (opt)
        {

        case FILTER_NEAREST:
            glCall(glTexParameteri(TEXTURE_TYPE, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
            glCall(glTexParameteri(TEXTURE_TYPE, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
            break;

        case FILTER_LINEAR:
            glCall(glTexParameteri(TEXTURE_TYPE, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
            glCall(glTexParameteri(TEXTURE_TYPE, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
            break;

        case BOTH:
            glCall(glTexParameteri(TEXTURE_TYPE, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
            glCall(glTexParameteri(TEXTURE_TYPE, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
            break;

        default:

            std::cerr << "[Error]: Wrong option parameter" << std::endl;

            return;
            break;
        }

    }

    
    unsigned char* data = stbi_load(filepath.c_str(), &m_width, &m_height, &m_BPP, 0);

    glCall(glGenTextures(1, &m_TextureID));

    glCall(glBindTexture(m_TextureType, m_TextureID));

    std::cerr << stbi_failure_reason() << std::endl;

    if (data)
    {
        glCall(glTexImage2D(TEXTURE_TYPE, 0, GL_RGB, m_width, m_height, 0, RGB_CHANNEL, GL_UNSIGNED_BYTE, data));
        glCall(if (MIPMAP) glGenerateMipmap(TEXTURE_TYPE));
    }
    else
    {
        std::cout << "Failed to load texture" << std::endl;
    };
    stbi_image_free(data);



}

这是纹理绑定函数:

void TEXTURE::Bind(unsigned int slot){

    glCall(glActiveTexture(GL_TEXTURE0 + slot));
    glCall(glBindTexture(m_TextureType, m_TextureID));


}

加载是由 OBJECT 类进行的:

这会将纹理添加到矢量中:

void OBJECT::AddTextures(TEXTURE texture){
    textures.push_back(texture);

}

这将加载 OBJECT(和纹理):

void OBJECT::Show(bool INDEXED, callbackFunction preRenderFunction){

    vb.Bind();
    va.Bind();
    shader.Bind();
    shader.Use();

    for (unsigned int i = 0; i < textures.size(); i++) {

        textures[i].Bind(i);

    }

    preRenderFunction(this);

    if (INDEXED) {
        ib.Bind();
        glCall(glDrawElements(GL_TRIANGLES, ib.GetLength() , GL_UNSIGNED_INT, 0));
        
    }else glDrawArrays(GL_TRIANGLES, 0, vb.GetLength());

}

这是着色器绑定和使用函数

void SHADER::Bind(){
    glCall(glUseProgram(m_ShaderProgramID));

}

void SHADER::Use(){

    glCall(glUseProgram(m_ShaderProgramID));

}

这是顶点着色器:

#version 330 core
layout (location = 0) in vec3 aPos; // the position variable has attribute position 0
layout (location = 1) in vec2 aTexCoord;

out vec3 vertexColor; // specify a color output to the fragment shader
out vec2 TexCoord;


uniform mat4 transform;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform mat4 MVP;

void main()
{
    gl_Position = projection * view * model * transform * vec4(aPos, 1.0); // see how we directly give a vec3 to vec4's constructor
    //gl_Position = MVP* vec4(aPos, 1.0);
    //gl_Position = projection * view * model * vec4(aPos, 1.0);
    TexCoord = aTexCoord;
}

这是片段着色器:

#version 330 core
out vec4 FragColor;
  
in vec3 vertexColor; // the input variable from the vertex shader (same name and same type)  
in vec2 TexCoord;

uniform sampler2D texture1;
uniform sampler2D texture2;
uniform float blend;

void main()
{
    //FragColor = texture(texture2, vec2(TexCoord.x, TexCoord.y * -1 ));
    FragColor = mix(texture(texture1, TexCoord), texture(texture2, vec2(TexCoord.x, TexCoord.y * -1 )) / 10, blend);
}

我认为它的所有代码都是相关的,这里是我项目的文件树(顺便说一句,我正在使用 Visual Studio):

C:.
├───res
│   ├───shaders
│   └───textures
├───src
│   ├───include
│   └───vendor
│       ├───glm
│       │   ├───detail
│       │   ├───ext
│       │   ├───gtc
│       │   ├───gtx
│       │   └───simd
│       ├───imgui
│       └───stb_image
└───x64
    ├───Debug
    │   └───OpenGL-PLSDFMTT.tlog
    └───Release
        └───OpenGL-PLSDFMTT.tlog

我尝试使用 VS 的调试器来观察正在发生的事情,在它没有崩溃的情况下,指向图像数据的数据指针是空的(数据是空的),当它有数据时它崩溃了,我尝试查看我的逻辑,但一切似乎都没问题,这就是我尝试的全部(所有这些都在 4 小时内完成)。

C++ opengl-3 stb-image

评论

0赞 Retired Ninja 9/13/2023
仅在实际失败时才调用。如果加载成功,您看到的消息没有任何意义。stbi_failure_reason()
0赞 Erdal Küçük 9/13/2023
你能展示一下代码吗?我猜您将纹理绑定到不同的纹理图像单元?TEXTURE::Bind()
0赞 Erdal Küçük 9/13/2023
并使用特定的纹理图像单位来设置制服?最好同时显示着色器代码。shader.Bind()sampler
0赞 Nico Cano 9/14/2023
我已经添加了您要求的内容@ErdalKüçük
0赞 Erdal Küçük 9/14/2023
纹理绑定源在哪里?

答:

1赞 Erdal Küçük 9/13/2023 #1

在将头文件包含在一个 C 或 C++ 源文件中以创建实现之前,请执行stbi_image

#define STB_IMAGE_IMPLEMENTATION

或者,添加

#define STBI_FAILURE_USERMSG //generate user friendly error messages

之后

#include "stb_image.h"

包含实现后,要加载映像,请执行stbi_image

unsigned char *pixels = stbi_load(filename, &width, &height, &channels, 0);

但在做任何其他事情之前,应该进行强制性的错误检查

if (!pixels) {
    std::cout
    << "unable to load image: "
    << stbi_failure_reason() 
    << "\n";
    //throw;
}

从现在开始,可以放心地假设图像已加载,并且字段中的值 , 可以安全使用。pixelswidthheightchannels

要获得成功(期望的)结果,需要进行其他检查,例如尺寸是否为 2 的幂(对于像素解包对齐很重要)、通道数(像素格式)等。


要在上传像素数据之前设置像素解包对齐方式,请执行

glPixelStorei(GL_UNPACK_ALIGNMENT, 1); //default is 4

此外

TEXTURE tex2("res/textures/dog.jpg", FILTER_NEAREST, GL_RGBA, GL_TEXTURE_2D);

如果图像文件没有 Alpha 通道(编码图像就是这种情况),则调用 将失败,因为您传递的格式参数无效(与像素数据不匹配)。因此,请检查您的图像有多少个通道,并根据该通道使用正确的格式参数。jpegglTexImage2D

#1 -> GL_RED
#2 -> GL_RG
#3 -> GL_RGB
#4 -> GL_RGBA

可以强制添加其他通道,因为您必须将最后一个参数设置为所需的通道数,这意味着要获取存储在图像文件中的通道数。stbistbi_load0

与其加载整个图像,不如做

stbi_info(filename, &width, &height, &channels);

它仅加载图像文件的标题(大多数图像格式确实包含包含所有相关信息的标题)。


一旦数据成功加载并上传到 GPU,就该将纹理绑定到特定的纹理图像单元了。

还必须通过设置相应的统一变量来通知着色器,例如

//bind tex to unit 0
glActiveTexture(GL_TEXTURE0);
glBindTexture(target, tex);

//set the value of the uniform located at sampler_loc to the
//unit where the texture is bound to, here 0
glProgramUniform1i(prog, sampler_loc, 0);

评论

0赞 Nico Cano 9/14/2023
嗨,当我添加和你给我的错误处理时,现在 stbi 没有给我错误,但图像仍然没有加载,当我在加载显示数据指针的图像时通过调试器,它有一个超长的字符串,但对于没有加载的图像,它除了指针本身之外没有其他数据, 顺便说一下,指针是 0,另外,应该有未加载的图像的第二个采样器有第一个采样器的图像#define STBI_FAILURE_USERMSG
0赞 Erdal Küçük 9/14/2023
提醒一下,如果返回空指针 (),则图像加载失败。原因可以通过 查询。宏(如果已定义)提供更用户友好的错误消息。如果可以加载图像(返回的指针不为 null),则(仅如此)继续将图像数据上传到 GPU。通过这样做,必须考虑一些规则(在我上面的回答中描述)。stbi_loadnullptrstbi_failure_reason()STBI_FAILURE_USERMSG
0赞 Erdal Küçük 9/14/2023
完成此操作后,您必须通过 和 ) 或一步通过(强烈建议阅读规格)将纹理绑定到特定的纹理图像单元。glActiveTexture(GLenum)glBindTexture(GLuint)glBindTextureUnit(GLuint, GLuint)
0赞 Erdal Küçük 9/14/2023
之后,使用特定单位的值设置着色器的采样器变量(简而言之,着色器中的采样器变量是纹理图像单元的值 - 索引)。如果着色器显示错误的纹理,则可能是将错误的纹理绑定到错误的单位,或者将错误的单位(通过)传递给着色器。这就是为什么我要求你提交你的纹理绑定函数。glProgramUniform
0赞 Nico Cano 9/14/2023
现在它返回一个指向任何内容的指针,“文本视图”显示 0 个字符