Dynamic Cube Mapping In OpenGL

I had quite a bit of trouble getting dynamic environment cube mapping work when i first tried it so i thought i would make a blog post on how to set up the framebuffer and rendering the scene to a cubemap. I should note that this method does not do it in one render pass. This is something i will come back to and make a future blog post on. For now this is how i set up a cube map by rendering the scene 6 times, each render pass for a different side of the cube.

First we will need to create an empty cubemap that we will render the scene into.

When binding the texture, specifie GL_TEXTURE_CUBE_MAP as we are created an empty cube map to allocate mutable storage for the 6 faces of a cubemap’s mipmap chain.

/*
	Create dynamic cubemap
*/
// Create empty cubemap
glGenTextures(1, &dynamicCubeTex);
glBindTexture(GL_TEXTURE_CUBE_MAP, dynamicCubeTex);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_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);

// Allocate space for each side of the cube map
for (GLuint i = 0; i < 6; ++i)
{
	glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGBA, cubeMapSize,
		cubeMapSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
}

When allocating space for each side of the cube map we increment GL_TEXTURE_CUBE_MAP_POSITIVE_X by 1 each time. We can do this because of how GL_TEXTURE_CUBE_MAP_POSITIVE_X  is define in glcorearb.h. By incrementing by 1 we can allocated for the next face of the cube map.

#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516
#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518
#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A

Next is to create the framebuffer and depth renderbuffer. We first generate the framebuffer and renderbuffer objects and bind them. Since we want both depth and color information in the cube map we first need to initalize the renderbuffer object data so that it can hold a depth-renderable internal format. Next attach the first face of the cube map to the framebuffer and attach the depth buffer to the render buffer. Unbind the framebuffer back to the deafult framebuffer which in this case is equal to glBindFramebuffer(GL_FRAMEBUFFER, 0);

// Create framebuffer
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glGenRenderbuffers(1, &depthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, cubeMapSize, cubeMapSize);
// Attach one of the faces of the cubemap texture to current framebuffer
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
	GL_TEXTURE_CUBE_MAP_POSITIVE_X, dynamicCubeTex, 0);
// Attach depth buffer to framebuffer
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
// Attach only the +X cubemap texture (for completeness)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X, dynamicCubeTex, 0);

// Check if current configuration of framebuffer is correct
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
std::cout << "Framebuffer not complete!" Program, "projection"), 1, GL_FALSE, &projection.data[0]);
	glActiveTexture(GL_TEXTURE0);
	glUniform1i(glGetUniformLocation(shaderSkyBox->Program, "skybox"), 0);
	glBindTexture(GL_TEXTURE_CUBE_MAP, cubeMapTextureID);
	SceneModels::RenderSkybox();

	shaderEnviromentObject->Use();
	glUniform1i(glGetUniformLocation(shaderEnviromentObject->Program, "diffuseTexture"), 1);
	model = Matrix4();
	glUniformMatrix4fv(glGetUniformLocation(shaderEnviromentObject->Program, "view"), 1, GL_FALSE, &currentCubeMapView.data[0]);
	glUniformMatrix4fv(glGetUniformLocation(shaderEnviromentObject->Program, "projection"), 1, GL_FALSE, &projection.data[0]);
	glUniformMatrix4fv(glGetUniformLocation(shaderEnviromentObject->Program, "model"), 1, GL_FALSE, &model.data[0]);
	RenderScene(*shaderEnviromentObject, deltaTime);
}

glBindFramebuffer(GL_FRAMEBUFFER, 0); // Unbind FBO, set default framebuffer

// Render scene as normal with the dynamic cube texture applied to the model
glViewport(0, 0, screenWidth, screenHeight);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// <--- Render scene here.

Feel free to email at darrensweeneydev@gmail.com

Here is the code in action:

Full source of my OpenGL Tech Demo is on GitHub: Repository