I've got my 6 cameras at the position of the object I'd like to environment map, but now I'm not sure how to render to a texture for a cubemap using FBOs. I've got rendering to a GL_TEXTURE_2D target working fine, but if I bind my the current frame buffer that's rendering to a Gl_TEXTURE_CUBE_MAP target, how do I set which cubemap target to my rendering camera?
For example, if I have a camera pointing along the positive Z axis, I want to render to the cubemap's GL_TEXTURE_CUBE_MAP_POSITIVE_Z sub-texture each frame. How would I do that?
Rendering to Cubemap for Environment Mapping
For example, if I have a camera pointing along the positive Z axis, I want to render to the cubemap's GL_TEXTURE_CUBE_MAP_POSITIVE_Z sub-texture each frame. How would I do that?
Simply by using GL_TEXTURE_CUBE_MAP_POSITIVE_Z as the texture target to a glFramebufferTexture2D call, eg:
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, texture_id, 0);
Alternatively, if you want to render the cube in a single pass, you can attach the entire cubemap using
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture_id, 0);
and select the target face in the geometry shader.
That last method you mentioned was what I was doing. Can you elaborate on how I'd select the target face in the shader? I'm guessing it's by using a cube map sampler since I'm passing a cube map through.
Just so that we understand each other: you want to render to a cubemap, right ? I got a bit confused when you started talking about texture samplers.
The first code snippet I posted uses a six pass technique. You render your scene six times, once for each of your 6 cameras. glFramebufferTexture2D is called to attach a specific cubemap side as the target for each of the rendering passes. A cubemap side attached this way will behave like a normal 2D texture target. You gradually build up your cubemap by cycling through all the GL_TEXTURE_CUBE_MAP_* constants as targets.
The second method is a single pass approach. You bind the entire cubemap as a target, and only perform a single render pass. This method requires a geometry shader that will duplicate each primitive six times, projecting it onto all six cubemap faces simultaneously (through your six view-projection matrices). The geometry shader directs each duplicated primitive to the appropriate cube target face using the gl_Layer variable.
The first code snippet I posted uses a six pass technique. You render your scene six times, once for each of your 6 cameras. glFramebufferTexture2D is called to attach a specific cubemap side as the target for each of the rendering passes. A cubemap side attached this way will behave like a normal 2D texture target. You gradually build up your cubemap by cycling through all the GL_TEXTURE_CUBE_MAP_* constants as targets.
The second method is a single pass approach. You bind the entire cubemap as a target, and only perform a single render pass. This method requires a geometry shader that will duplicate each primitive six times, projecting it onto all six cubemap faces simultaneously (through your six view-projection matrices). The geometry shader directs each duplicated primitive to the appropriate cube target face using the gl_Layer variable.
[color="#1C2837"]Just so that we understand each other: you want to render to a cubemap, right ? I got a bit confused when you started talking about texture samplers.[/quote]
My wording wasn't that clear, but you are correct. There are two reasons I'm rendering to a cubemap: to make a cube depth texture for shadow maps, and an environment map for reflective surfaces and water.
So, after I setup my FBO, I'd do something like this?
// add the color attachments to the cubemap
for(int i=0;i<6;i++)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+i, GL_TEXTURE_CUBE_MAP_POSITIVE_Z+i, textureHandle, 0);
Now for rendering to the cubemap in my Scene management class:
// only render to a texture
void Scene::RenderEnvironmentMap()
{
// cancel early if the cubemap render target is invalid
if(!cubeTarget) return;
// set the cubemap render target's FBO
int oldFBO = GetBoundFBO();
SetBoundFBO(cubeTarget->GetFBO());
// render the scene to each face of the cubemap
for(int i=0;i<6;i++)
{
// set the current camera from the managed cube camera pointers
SetCurrentCamera(cubeCamera);
glBindRenderbuffer(GL_TEXTURE_CUBE_MAP_POSITIVE_Z+i, cubeTarget->GetFaceTextureHandle(i));
RenderObjects(); // render the scene
}
// set the FBO back to the original one
SetBoundFBO(oldFBO);
}
You are mixing up different concepts here (cube map faces, MRTs, render buffers, etc).
It looks like you are indeed rendering the scene 6 times. That would be the first approach I outlined earlier. For this to work, you will need to bind the cube sides one after the other, while rendering the matching camera view:
[source]
// Bind the FBO
glBindFramebuffer(GL_FRAMEBUFFER, fboHandle);
// Attach a depth renderbuffer to the FBO
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbufferHandle);
// Render the six views
for( int i = 0; i < 6; ++i ) {
// Bind the i'th face of the cube texture as target to color attachment 0
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubeTextureHandle, 0);
// All following render calls will now end up on the i'th side of the cubemap !
// Setup camera and render scene for the i'th view into the previously bound cubemap face
SetCurrentCamera(cubeCamera);
RenderObjects();
}
// Unbind FBO
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Cubemap at cubeTextureHandle is now complete.
[/source]
This assumes that your are only interested in a color cubemap texture and it uses a shared depth buffer for the rendering. If you want a depth cubemap generated in the same 6 passes along with the color cubemap, then drop the render buffer and add a second glFramebufferTexture2D call to the loop, attaching the i'th depth cube texture face to GL_DEPTH_ATTACHMENT. Something like:
It looks like you are indeed rendering the scene 6 times. That would be the first approach I outlined earlier. For this to work, you will need to bind the cube sides one after the other, while rendering the matching camera view:
[source]
// Bind the FBO
glBindFramebuffer(GL_FRAMEBUFFER, fboHandle);
// Attach a depth renderbuffer to the FBO
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbufferHandle);
// Render the six views
for( int i = 0; i < 6; ++i ) {
// Bind the i'th face of the cube texture as target to color attachment 0
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubeTextureHandle, 0);
// All following render calls will now end up on the i'th side of the cubemap !
// Setup camera and render scene for the i'th view into the previously bound cubemap face
SetCurrentCamera(cubeCamera);
RenderObjects();
}
// Unbind FBO
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Cubemap at cubeTextureHandle is now complete.
[/source]
This assumes that your are only interested in a color cubemap texture and it uses a shared depth buffer for the rendering. If you want a depth cubemap generated in the same 6 passes along with the color cubemap, then drop the render buffer and add a second glFramebufferTexture2D call to the loop, attaching the i'th depth cube texture face to GL_DEPTH_ATTACHMENT. Something like:
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, depthCubeTextureHandle, 0);
That looks like what I'm looking for!
Now, not only does this work for setting up the cubemap, but could I use this to re-render the scene each frame?
If I wanted to update the cubemap, could I call this on render frames:
Does the call to glFramebufferTexture2D() work, or does that attempt to add another GL_COLOR_ATTACHMENT0 to the framebuffer?
Now, not only does this work for setting up the cubemap, but could I use this to re-render the scene each frame?
If I wanted to update the cubemap, could I call this on render frames:
glBindFramebuffer(GL_FRAMEBUFFER, fboHandle);
for(int i=0;i<6;i++)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubeTextureHandle, 0);
SetCurrentCamera(cubeCamera);
RenderObjects();
}
Does the call to glFramebufferTexture2D() work, or does that attempt to add another GL_COLOR_ATTACHMENT0 to the framebuffer?
It works. No, it doesn't add other attachements. If you want to add others, you use GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, etc. whatever the max is for your GPU.
When rendering to my cube map texture, I'm getting a black texture.
Here's how I create my cube map and FBO:
Here's how I render my scene to a texture each frame:
The environment map was also allocated to the dimensions of <128, 128> per face.
Here's how I create my cube map and FBO:
void EnvironmentMap::CreateFBO()
{
// check if the dimensions are greater than supported
int size;
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &size);
if(width > size || height > size)
return;
DestroyFBO(); // release everything
// allocate the new buffers
int oldFBO = GetCurrentFBO();
glGenTextures(1, &handle);
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
// setup the texture
SetBoundTexture(GL_TEXTURE_CUBE_MAP, handle);
SetFilter(GL_TEXTURE_MAG_FILTER, GL_LINEAR);
SetFilter(GL_TEXTURE_MIN_FILTER, GL_LINEAR);
SetWrapMode(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
SetWrapMode(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// allocate the texture
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
// set the texture rendering targets
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X, handle, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, handle, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, handle, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, handle, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, handle, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, handle, 0);
SetBoundTexture(GL_TEXTURE_CUBE_MAP, -1);
// add a depth attachment
if(useDepth)
{
glGenRenderbuffers(1, &depthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
}
// check framebuffer status
unsigned int status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
switch(status)
{
case GL_FRAMEBUFFER_COMPLETE: printf("GL_FRAMEBUFFER_COMPLETE\n"); break;
case 0x8CDB: printf("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER\n"); break;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: printf("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT\n"); break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: printf("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT\n"); break;
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: printf("GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS\n"); break;
case GL_FRAMEBUFFER_UNSUPPORTED: printf("GL_FRAMEBUFFER_UNSUPPORTED\n"); break;
default: printf("Unknown issue (%X).\n",status); break;
}
// set clear the FBO to white
ColorRGBA oldColor = GetClearColor();
SetClearColor(ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// restore the previous FBO
SetBoundFBO(oldFBO); // set the old FBO
SetClearColor(oldColor); // set the old clear color
}
Here's how I render my scene to a texture each frame:
// render the environment map
EnvironmentMap *envMap = sceneOBJModel->GetMesh(0)->GetMaterial()->GetEnvironmentMap();
SetViewport(ViewportData(0, 0, 128,128));
SetBoundFBO(envMap->GetFBO());
for(int i=0;i<6;i++)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, envMap->GetHandle(), 0);
if(cubeCameras) scene->SetCurrentCamera(cubeCameras);
if(scene) scene->Render();
}
The environment map was also allocated to the dimensions of <128, 128> per face.
This topic is closed to new replies.
Advertisement
Popular Topics
Advertisement