سؤال

I try to render skybox on my scene, but it renders black like texture is not loaded properly. I use FreeImage library to load a texture:

#include <FreeImage.h>

Here's my Skybox class:

#include "Skybox.h"


CSkybox::CSkybox(){
    el::Logger* logger = el::Loggers::getLogger("Skybox");
}

void CSkybox::init(string home_dir, string pos_x, string neg_x, string pos_y, string neg_y, string pos_z, string neg_z)
{
    cubeFilesPaths[0] = home_dir+pos_x;
    cubeFilesPaths[1] = home_dir+neg_x;
    cubeFilesPaths[2] = home_dir+pos_y;
    cubeFilesPaths[3] = home_dir+neg_y;
    cubeFilesPaths[4] = home_dir+pos_z;
    cubeFilesPaths[5] = home_dir+neg_z;

    loadShaders("shaders/skyboxShader.vp", "shaders/skyboxShader.fp");

    glGenTextures(1, &cubeTexture);
    glBindTexture(GL_TEXTURE_CUBE_MAP, cubeTexture);

    for(int i = 0; i < 6; i++){        
        verify(readTexture(i), "Texture "+cubeFilesPaths[i]+" not loaded"); 
    }

    CLOG(INFO, "Skybox")<<"Skybox texture loaded.";

    vao.createVAO();
    vao.bindVAO();
}

void CSkybox::loadShaders(string vsPath, string fsPath){
    verify(skyboxVertexShader.loadShader(vsPath, GL_VERTEX_SHADER), "Skybox vertex shader not loaded");
    verify(skyboxFragmentShader.loadShader(fsPath, GL_FRAGMENT_SHADER), "Skybox fragment shader not loaded");

    skyboxShaderProgram.createProgram();
    verify(skyboxShaderProgram.addShaderToProgram(&skyboxVertexShader), "Skybox vertex shader not added to a program");
    verify(skyboxShaderProgram.addShaderToProgram(&skyboxFragmentShader), "Skybox fragment shader not added to a program");

    verify(skyboxShaderProgram.linkProgram(), "Shader program not linked"); 
}

void CSkybox::render(glm::mat4 viewMatrix){
    //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    //glDisable(GL_CULL_FACE);

    skyboxShaderProgram.bindProgram();
    vao.bindVAO();

    skyboxShaderProgram.setUniform("view_matrix", &viewMatrix);
    glBindTexture(GL_TEXTURE_CUBE_MAP, cubeTexture);

    glDisable(GL_DEPTH_TEST);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    glEnable(GL_DEPTH_TEST);

    vao.unbindVAO();
    //glEnable(GL_CULL_FACE);
    skyboxShaderProgram.unbindProgram();
    //glPolygonMode(GL_FRONT, GL_FILL);
}

void CSkybox::release(){
    glDeleteTextures(1, &cubeTexture);
    skyboxShaderProgram.deleteProgram();
    skyboxVertexShader.deleteShader();
    skyboxFragmentShader.deleteShader();
    vao.releaseVAO();
}

bool CSkybox::readTexture(int i){
    cout<<"Reading: "<<i<<endl;

    FREE_IMAGE_FORMAT fif = FIF_UNKNOWN;
    FIBITMAP* dib(0);

    fif = FreeImage_GetFileType(cubeFilesPaths[i].c_str(), 0); // Check the file signature and deduce its format

    if(fif == FIF_UNKNOWN) // If still unknown, try to guess the file format from the file extension
        fif = FreeImage_GetFIFFromFilename(cubeFilesPaths[i].c_str());

    if(fif == FIF_UNKNOWN) // If still unknown, return failure
        return false;

    if(FreeImage_FIFSupportsReading(fif)) // Check if the plugin has reading capabilities and load the file
        dib = FreeImage_Load(fif, cubeFilesPaths[i].c_str());
    if(!dib)
        return false;

    BYTE* bits = FreeImage_GetBits(dib); // Retrieve the image data

    // If somehow one of these failed (they shouldn't), return failure
    if(bits == NULL || FreeImage_GetWidth(dib) == 0 || FreeImage_GetHeight(dib) == 0)
        return false;

    cout<<FreeImage_GetBPP(dib)<<endl;

    GLenum format = FreeImage_GetBPP(dib) == 24 ? GL_BGR : FreeImage_GetBPP(dib) == 8 ? GL_LUMINANCE : 0;
    GLenum internalFormat = FreeImage_GetBPP(dib) == 24 ? GL_RGB : GL_DEPTH_COMPONENT;
    GLsizei iWidth = FreeImage_GetWidth(dib);
    GLsizei iHeight = FreeImage_GetHeight(dib);

    cout<<iWidth<<endl;
    cout<<iHeight<<endl;

    glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internalFormat, iWidth, iHeight, 0, format, GL_UNSIGNED_BYTE, bits);

    GLenum error = glGetError();
    if(error!= GL_NO_ERROR){
        cout<<error<<endl;
    }

    FreeImage_Unload(dib);
    return true;
}

void CSkybox::verify(bool value, string message){
    if(!value){
        CLOG(ERROR, "Skybox")<<"Error: "<<message;
    }
}

Vertex shader:

#version 430 core

out VS_OUT
{
    vec3 tc;
} vs_out;

uniform mat4 view_matrix;

void main(void)
{
    vec3[4] vertices = vec3[4](vec3(-1.0, -1.0, 1.0),
                               vec3( 1.0, -1.0, 1.0),
                               vec3(-1.0,  1.0, 1.0),
                               vec3( 1.0,  1.0, 1.0));

    vs_out.tc = mat3(view_matrix) * vertices[gl_VertexID];

    gl_Position = vec4(vertices[gl_VertexID], 1.0);
}

and fragment shader:

#version 430 core

layout (binding = 0) uniform samplerCube tex_cubemap;

in VS_OUT
{
    vec3    tc;
} fs_in;

layout (location = 0) out vec4 color;

void main(void)
{
    color = texture(tex_cubemap, fs_in.tc);
}

Unfortunately render() method renders only a black rectangle. If i hardcode an other color in a fragment shader or vertices coords in a vertex shader, a rectangle changes a color and a placement. I use shader and vao helper classes to render particles as well, so they are probably good and problem is with a texture loading.

Final effect looks like this:

enter image description here

هل كانت مفيدة؟

المحلول

You have at least two different variables named cubeEnum

There is a cubeEnum declared at function scope in the CSkybox constructor:

CSkybox::CSkybox(){
    el::Logger* logger = el::Loggers::getLogger("Skybox");

    GLenum  cubeEnum[6] = {  GL_TEXTURE_CUBE_MAP_POSITIVE_X,
        GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
        GL_TEXTURE_CUBE_MAP_POSITIVE_Y,
        GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
        GL_TEXTURE_CUBE_MAP_POSITIVE_Z,
        GL_TEXTURE_CUBE_MAP_NEGATIVE_Z };
}

There is another cubeEnum declared at a different scope used by readTexture (...):

glTexImage2D(cubeEnum[i], 0, internalFormat, iWidth, iHeight, 0, format, GL_UNSIGNED_BYTE, bits);

I suspect that the other array that is also called cubeEnum is uninitialized so you are passing garbage to glTexImage2D (...). However, the simple truth is that you do not need an array in the first place.

This particular set of enums is sequential, thus you can write the following:

glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internalFormat, iWidth, iHeight, 0, format, GL_UNSIGNED_BYTE, bits);

And get rid of all of the separate variables you have named cubeEnum. Then it will no longer matter that you are shadowing a variable of the same name at a different scope.


Update:

Additionally, you were using the default texture minification filter: GL_NEAREST_MIPMAP_LINEAR on a cubemap that did not have the appropriate set of mipmap LODs (GL textures have 1000 LODs by default and you only defined LOD 0). Using that filter on a "mipmap incomplete" texture results in undefined behavior, and this was the cause of your black texture.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top