c++ SOIL2不加载灰度纹理

2o7dmzc5  于 2023-06-07  发布在  其他
关注(0)|答案(1)|浏览(138)

我是OpenGL的新手,目前正在学习。
使用SOIL 2,我正在加载一个纹理,并试图在屏幕上渲染它。我像这样加载纹理:

GLuint texture;
texture = SOIL_load_OGL_texture(("resources/textures/" + fileName + ".png").c_str(),
   SOIL_LOAD_AUTO, //Problem
   SOIL_CREATE_NEW_ID,
   SOIL_FLAG_INVERT_Y | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_NTSC_SAFE_RGB);

glBindTexture(GL_TEXTURE_2D, texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, NULL);

问题是,当我尝试用SOIL_LOAD_AUTO或SOIL_LOAD_LA加载纹理时,我得到一个黑色的正方形。也就是说,如果我使用SOIL_LOAD_RGBA,则显示灰度纹理。
互联网搜索什么也没有找到,我倾向于相信我不明白“灰度”模式是如何工作的。
目前我的代码看起来像这样(是的,它有点错误,但现在我正在尝试理解OpenGL缓冲区,纹理加载和着色器是如何工作的,所以我没有完美地做每件事):
渲染:

void Render::runGame() {
    logger->info("Running content rendering...");

    glfwShowWindow(window);

    World world(width, height, &logger);

    GLfloat data[] = {
        0.0f, 0.0f,  1.0f, 0.0f, 0.0f, 1.0f,  0.0f, 1.0f, //Top left
        0.0f, 0.0f,  0.0f, 1.0f, 0.0f, 1.0f,  0.0f, 0.0f, //Bottom left
        0.0f, 0.0f,  0.0f, 0.0f, 1.0f, 1.0f,  1.0f, 1.0f, //Top right
        0.0f, 0.0f,  1.0f, 0.0f, 1.0f, 1.0f,  1.0f, 0.0f  //Bottom right
    };
    GLubyte edata[6] = { 0, 1, 2, 2, 1, 3 };

    world.getNormalizedCoord(0, height, 0, data);
    world.getNormalizedCoord(0, 0, 8, data);
    world.getNormalizedCoord(width, height, 16, data);
    world.getNormalizedCoord(width, 0, 24, data);

    GLuint VBO;
    glGenBuffers(1, &VBO);

    GLuint EBO;
    glGenBuffers(1, &EBO);

    GLuint VAO;
    glGenVertexArrays(1, &VAO);

    glBindVertexArray(VAO);

        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_STATIC_DRAW);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(edata), edata, GL_STATIC_DRAW);

        glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*) 0);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*) (2 * sizeof(GLfloat)));
        glEnableVertexAttribArray(1);
        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*) (6 * sizeof(GLfloat)));
        glEnableVertexAttribArray(2);

    glBindVertexArray(NULL);
    
    Shader shader = Shader("tile", &logger);
    shader.create();
    
    Texture texture = Texture("tile", &logger);
    texture.create();

    glEnable(GL_TEXTURE_2D);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    while (!glfwWindowShouldClose(window)) {
        glfwPollEvents();

        glClear(GL_COLOR_BUFFER_BIT);

        if (texture.use() && shader.use()) {
            glBindVertexArray(VAO);
            glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, NULL);
            glBindVertexArray(NULL);
        }

        shader.free();
        texture.free();

        glfwSwapBuffers(window);
    }

    destroy();
}

纹理类别:

//Constructor...

Texture::~Texture() noexcept { glDeleteTextures(1, &texture); }

void Texture::create() {
    if (hasLogger()) logger->info("Loading «" + fileName + "» texture...");

    if (isCreated()) {
        if (hasLogger()) logger->warn("Attempt to create texture «" + fileName + "» that is already created");
        return;
    }

    GLuint texture;
    texture = SOIL_load_OGL_texture(("resources/textures/" + fileName + ".png").c_str(),
        SOIL_LOAD_AUTO, //Problem
        SOIL_CREATE_NEW_ID,
        SOIL_FLAG_INVERT_Y | SOIL_FLAG_MULTIPLY_ALPHA | SOIL_FLAG_NTSC_SAFE_RGB);

    if (texture == 0) {
        if (hasLogger()) logger->fatal("Failed to create texture «" + fileName + "»: " + SOIL_last_result());
        return;
    }

    glBindTexture(GL_TEXTURE_2D, texture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glBindTexture(GL_TEXTURE_2D, 0);

    this->texture = texture;
    created = true;
}

GLuint Texture::getTexture() const noexcept { return texture; }

bool Texture::use() const {
    if (!isCreated()) {
        if (hasLogger()) logger->warn("Texture «" + fileName + "» using before creating");
        return false;
    }
    else {
        glBindTexture(GL_TEXTURE_2D, texture);
        return true;
    }
}

void Texture::free() const noexcept { glBindTexture(GL_TEXTURE_2D, NULL); }

bool Texture::isCreated() const noexcept { return created; }
bool Texture::hasLogger() const noexcept { return logger != nullptr; }

顶点着色器:

#version 330 core

layout (location = 0) in vec2 position;
layout (location = 1) in vec4 color;
layout (location = 2) in vec2 texCoord;

out vec4 fragColor;
out vec2 fragTexCoord;

void main() {
    gl_Position = vec4(position, 0.0f, 1.0f);
    fragColor = color;
    fragTexCoord = texCoord;
}

片段着色器:

#version 330 core

in vec4 fragColor;
in vec2 fragTexCoord;

out vec4 color;

uniform sampler2D fragTexture;

void main() {
    color = texture(fragTexture, fragTexCoord);
}

纹理:x1c 0d1x
使用SOIL_LOAD_AUTO或SOIL_LOAD_LA

得到的结果
我想要得到的结果,目前只有SOIL_LOAD_RGBA才有可能:

我真的不明白这是怎么回事,有什么原因会有如此奇怪的行为?
P.S.我的OpenGL版本是3.3核心配置文件和向前兼容性

解决方案:

我听从了接受答案的建议,放弃了SOIL。我使用了std_image,并正确地将纹理加载到OpenGL中。我的渲染代码没有改变,纹理加载代码现在看起来像这样:

void Texture::create() noexcept {
    if (hasLogger()) logger->info("Loading «" + fileName + "» texture...");

    if (isCreated()) {
        if (hasLogger()) logger->warn("Attempt to create texture «" + fileName + "» that is already created");
        return;
    }

    int width, height, nrChannels;
    stbi_set_flip_vertically_on_load(true); //flip y for opengl render correctly
    stbi_uc* data = stbi_load(("resources/textures/" + fileName + ".png").c_str(), &width, &height, &nrChannels, 0);

    if (data) {
        GLuint texture;

        glGenTextures(1, &texture);
        glBindTexture(GL_TEXTURE_2D, texture);

        GLenum format;
        switch (nrChannels) {
            case 1: { //L
                format = GL_RED;
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED); //Texture swizzle parameters
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED); //for correct store and render
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); //image with different channels
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE);
                break;
            }
            case 2: { //LA
                format = GL_RG;
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_RED);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_GREEN);

                for (int i = 0; i < 2 * width * height; i += 2) { //Multiply alpha
                    data[i] = (data[i] * data[i + 1] + 128) >> 8;
                }

                break;
            }
            case 3: default: { //RGB
                format = GL_RGB;
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_GREEN);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_BLUE);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE);
                break;
            }
            case 4: { //RGBA
                format = GL_RGBA;
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_GREEN);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_BLUE);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ALPHA);

                for (int i = 0; i < 4 * width * height; i += 4) { //Multiply alpha
                    data[i + 0] = (data[i + 0] * data[i + 3] + 128) >> 8;
                    data[i + 1] = (data[i + 1] * data[i + 3] + 128) >> 8;
                    data[i + 2] = (data[i + 2] * data[i + 3] + 128) >> 8;
                }

                break;
            }
        }

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, format == GL_RGBA || format == GL_RG ? GL_CLAMP_TO_EDGE : GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, format == GL_RGBA || format == GL_RG ? GL_CLAMP_TO_EDGE : GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
        glBindTexture(GL_TEXTURE_2D, NULL);

        stbi_image_free(data);

        this->texture = texture;
        created = true;
    }
    else {
        if (hasLogger()) logger->fatal("Failed to create texture «" + fileName + "»");
    }
}

我做了加载四种纹理类型,如Luminous,Luminous/Alpha,RGB,RGBA。为此,我必须使用GL_TEXTURE_SWIZZLE。此外,我没有找到SOIL_FLAG_MULTIPLY_ALPHA的类似物,没有它,透明纹理看起来与我预期的有点不同。所以我添加了代码来处理这个细节

h6my8fg2

h6my8fg21#

可加载的图像格式(force_channels)(SOIL.h)。

*SOIL_LOAD_AUTO=0)将保留找到的任何格式的图像。
*SOIL_LOAD_LA=2)强制图像加载为带Alpha的Luminous
*SOIL_LOAD_RGBA=4)强制图像加载为绿色蓝Alpha

在函数SOIL_internal_create_OGL_texture的第1187行中,您将看到,如果通道计数为12,则SOIL使用GL_LUMINANCEGL_LUMINANCE_ALPHA符号。
唯一的问题是,GL_LUMINANCEGL_LUMINANCE_ALPHA对于当前的OpenGL版本(4.6)是未知的。另一方面,在Legacy OpenGL和OpenGL ES中,glTexImage2D将这些符号识别为format参数。

解决方案

  • 使用传统OpenGL或OpenGL ES,
  • 将图像文件另存为rgb(a)(因此,如果您使用当前的OpenGL,则可以使用SOIL_LOAD_AUTO),
  • 强制SOIL_LOAD_RGB(A)(适用于所有OpenGL版本)或
  • 放弃SOIL,使用stb_image(SOIL基于它)并自己创建纹理(如果你想学习OpenGL,这是更好的方法)。
    示例(使用stb_image):
//if you want to flip the texture data vertically
//stbi_set_flip_vertically_on_load(1);

const char *filename = ""; //path of image file
int width;        //width of image
int height;       //height of image
int num_channels; //number of channels in image

//since we're interested in the channel count, set last param to 0
stbi_uc *data = stbi_load(filename, &width, &height, &num_channels, 0);

//num_channels to texture `format`
GLenum format;
switch (num_channels) {
    case 1: format = GL_RED;  break; //STBI_grey
    case 2: format = GL_RG;   break; //STBI_grey_alpha
    case 3: format = GL_RGB;  break; //STBI_rgb
    case 4: format = GL_RGBA; break; //STBI_rgb_alpha
    //default: something went wrong
}

//texture name
GLuint tex;

//generate texture name
glGenTextures(1, &tex);

//bind the texture to a specific texture target
glBindTexture(GL_TEXTURE_2D, tex);

//create the texture image
glTexImage2D(
    GL_TEXTURE_2D, 
    0,      //mipmap level 0,
            //use glGenerateMipmap(GL_TEXTURE_2D) for automation
    format, //internal format, does not have to be the same as format, but
            //then you have to consider the conversation effects
    width,  
    height, 
    0,      //no border
    format, 
    GL_UNSIGNED_BYTE, //stbi_uc -> unsigned char
    data
);

//set texture params (min, mag, wrap_s, wrap_t)
//glTexParameteri...

//unbind texture from target
glBindTexture(GL_TEXTURE_2D, 0);

//free image data
stbi_image_free(data);

相关问题