I'm trying to make a skybox that can change with a button.
My solution for this is to bind my skyboxes and load my textures in the following way:
//SkyBox
GLuint skyboxVBO, skyboxVAO;
glGenVertexArrays(1, &skyboxVAO);
glGenBuffers(1, &skyboxVBO);
glBindVertexArray(skyboxVAO);
glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), &skyboxVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid *)0);
// Load textures
vector<const GLchar*> facesN;
facesN.push_back("SkyBox/night/right.tga");
facesN.push_back("SkyBox/night/left.tga");
facesN.push_back("SkyBox/night/top.tga");
facesN.push_back("SkyBox/night/bottom.tga");
facesN.push_back("SkyBox/night/back.tga");
facesN.push_back("SkyBox/night/front.tga");
GLuint cubemapTextureN = TextureLoading::LoadCubemap(facesN);
vector<const GLchar*> faces;
faces.push_back("SkyBox/day/right.tga");
faces.push_back("SkyBox/day/left.tga");
faces.push_back("SkyBox/day/top.tga");
faces.push_back("SkyBox/day/bottom.tga");
faces.push_back("SkyBox/day/back.tga");
faces.push_back("SkyBox/day/front.tga");
GLuint cubemapTexture = TextureLoading::LoadCubemap(faces);
Then, in the program loop I draw the skybox with the following:
// Draw skybox as last
glDepthFunc(GL_LEQUAL); // Change depth function so depth test passes when values are equal to depth buffer's content
SkyBoxshader.Use();
view = glm::mat4(glm::mat3(camera.GetViewMatrix())); // Remove any translation component of the view matrix
glUniformMatrix4fv(glGetUniformLocation(SkyBoxshader.Program, "view"), 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(glGetUniformLocation(SkyBoxshader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
// skybox cube
glBindVertexArray(skyboxVAO);
glActiveTexture(GL_TEXTURE1);
if (daytime == 1.0f) {
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);
}
else {
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTextureN);
}
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
glDepthFunc(GL_LESS); // Set depth function back to default
In theory, whenever I press the button that changes the value of daytime
, the skybox should change its textures to cubemapTextureN
and, with another press of the button, change back to cubemapTexture
. Instead, it just draws the texture that was loaded last, cubemapTexture
in this case.
I already tested daytime
and it does change, so it's not that.
Also, I tried loading cubemapTextureN
after cubemapTexture
and by doing so cubemapTextureN
gets drawn instead, but still no change. I'm assuming somehow while loading the second textures, the first ones get updated, which shouldn't happen since they have different names, unless i'm understanding it wrong.
Easy solution would be to just make models of the entire skyboxes and work with that, but if possible I want to see if I can use this solution.
Edit:
These are the Shaders for the skybox.
Fragment shader:
#version 330 core
in vec3 TexCoords;
out vec4 color;
uniform samplerCube skybox;
void main()
{
color = texture(skybox, TexCoords);
}
Vertex shader:
#version 330 core
layout (location = 0) in vec3 position;
out vec3 TexCoords;
uniform mat4 projection;
uniform mat4 view;
void main()
{
vec4 pos = projection * view * vec4(position, 1.0);
gl_Position = pos.xyww;
TexCoords = position;
}
And their use:
Shader SkyBoxshader("Shaders/SkyBox.vs", "Shaders/SkyBox.frag");
Also, if this is usefull, this is the header with the definition of TextureLoading
, which contains LoadCubemap
#pragma once
// GLEW
#include <GL/glew.h>
// Other Libs
#include "stb_image.h"
// Other includes
#include "Model.h"
#include <vector>
class TextureLoading
{
public:
static GLuint LoadTexture(GLchar *path)
{
unsigned int textureID;
glGenTextures(1, &textureID);
int width, height, nrComponents;
unsigned char *data = stbi_load(path, &width, &height, &nrComponents, 0);
if (data)
{
GLenum format;
if (nrComponents == 1)
format = GL_RED;
else if (nrComponents == 3)
format = GL_RGB;
else if (nrComponents == 4)
format = GL_RGBA;
glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
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_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
stbi_image_free(data);
}
else
{
std::cout << "Failed to load texture" << path << std::endl;
stbi_image_free(data);
}
return textureID;
}
static GLuint LoadCubemap(vector<const GLchar * > faces)
{
GLuint textureID;
glGenTextures(1, &textureID);
int width, height, nrChannels;
for (unsigned int i = 0; i < faces.size(); i++)
{
unsigned char *data = stbi_load(faces[i], &width, &height, &nrChannels, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
stbi_image_free(data);
}
else
{
std::cout << "Cubemap texture failed to load at path: " << faces[i] << std::endl;
stbi_image_free(data);
}
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
return textureID;
}
};