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:
cubeEnum
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 };
}
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.
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.
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.