Single-pass cubemap (SOLVED!)

All I can figure is it has something to do with immutable storage and the shader is trying to read it as a texture2d instead of cubemap or the the layered fbo is being created differently. Oddly, if I use Cinder’s fbocubemap(preferred) instead of a custom layered fbo, I get the same results.

I wanted to test that layers and viewports were being handled so I assigned them to red/blue values In the fragment shader. The geometry shader is recognizing outputting as it should but still only rendering to one face. The only layer I don’t think is being rendered is layer 3, which I believe is the only face that is being drawn into.
Capture
Kinda banging my head on a wall from this…
Is it perhaps be something to do with how Cinder handles cubemap textures? I still don’t understand why immutable storage won’t work.
Here’s how I’m setting up my fbos. I’m getting the same exact results whether I create a custom layered fbo or a fboCubeMap so that makes me beleive it has something to do with Cinder’s textureCubeMap.

void singlePassCubeApp::setupFbo()
{
	textureCubeMap = gl::TextureCubeMap::create(GRIDSIZE, GRIDSIZE, gl::TextureCubeMap::Format().internalFormat(GL_RGBA).mipmap(false));
	textureDepthCubeMap = gl::TextureCubeMap::create(GRIDSIZE, GRIDSIZE, gl::TextureCubeMap::Format().internalFormat(GL_DEPTH_COMPONENT32F).mipmap(false));
	mFboCube = gl::Fbo::create(GRIDSIZE, GRIDSIZE, gl::Fbo::Format().attachment(GL_COLOR_ATTACHMENT0, textureCubeMap).attachment(GL_DEPTH_ATTACHMENT, textureDepthCubeMap));

	mDynamicCubeMapFbo = gl::FboCubeMap::create(GRIDSIZE, GRIDSIZE, gl::FboCubeMap::Format().textureCubeMapFormat(gl::TextureCubeMap::Format().internalFormat(GL_RGBA).minFilter(GL_LINEAR_MIPMAP_LINEAR).magFilter(GL_LINEAR)));
	mDynamicCubeMapFbo->getFormat().setDepthBufferInternalFormat(GL_DEPTH_COMPONENT32F) ;
}

The real issue seems that the fbo isn’t binding correctly even though the shader is writing to gl_layer…

Wish I could try this for myself, but I am severely lacking time at the moment. Have you tried setting gl_Layer to a fixed value between 0 and 5 in the shader? I am curious to know if it renders to the correct face in that case. Maybe simply use a barebones shader with just the layer selection in there, no fancy single pass stuff. That way we know if there is something wrong with the Fbo.

Hey Paul. Thanks for helping me with this. I did try changing the values… the correct perspectives and layers are being drawn… but only to one face… the image above should be a cube cross… the shaded purple shades show that viewport and gl_layer are separately drawn, but again, just the one side is binding. The Cyan color is from clearing the fbo and it’s not even clearing all the faces… so it really makes me think I’m creating my layeredFbo/cubemap wrong. I’ll try a simpler setup for debugging… perhaps I’ll try my 3x2 viewport grid again on a single fbo using the extensions and see where it gets me… something tells me, I need to make a custom single-pass cubemap texture/fbo… if I do, maybe I can write one that extends Cinder’s and overrides the appropriate functions. It would be ideal to use a cubemapTexture as Cinder already has a drawEquirectangular() function straight out of the box… get it? “Straight out of the box?” Hehehe. I’ll share my findings and upload the code.

SOLVED!!!
When I bind a cubemapFbo

gl::ScopedFramebuffer scopedFbo(mDynamicCubeMapFbo);

It only binds to a single faceTarget… GL_TEXTURE_CUBE_MAP_POSITIVE_X
When I use bindFramebufferFace, I’m able to switch faces as in the Cinder examples.
bindFramebufferFace looks like this.

void FboCubeMap::bindFramebufferFace( GLenum faceTarget, GLint level, GLenum target, GLenum attachment )
{
bindFramebuffer( target );
glFramebufferTexture2D( target, attachment, faceTarget, mTextureCubeMap->getId(), level );
}

Notice that glFramebufferTexture2D is used to bind the faceTarget in bindFramebufferFace…
Now comparing that to nVidia’s cubemap example
Their fbo is bound with glFramebufferTexture for the single pass method and glFramebufferTexture2d for the traditional multipass method.

    // attach all 6 faces as a layered texture
    glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_cubemapColorTex, 0);
    glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, m_cubemapDepthTex, 0);

Looks as though I need to modify an appropriate bind for cubemapFbo to attach all 6 faces as a layered texture.

Here’s the working singlePass cubemap draw function
First setup a cubemapFbo

mDynamicCubeMapFbo = gl::FboCubeMap::create(GRIDSIZE, GRIDSIZE, gl::FboCubeMap::Format().textureCubeMapFormat(gl::TextureCubeMap::Format().internalFormat(GL_RGBA)));
mDynamicCubeMapFbo->getFormat().setDepthBufferInternalFormat(GL_DEPTH_COMPONENT32F) ;

Then bind to it properly…

// bind the fbo and enable depth testing
gl::context()->pushFramebuffer();
mDynamicCubeMapFbo->bindFramebuffer();
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textureCubeMap->getId(), 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, textureDepthCubeMap->getId(), 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST);
for (int face = 0; face < 6; face++) {
glViewportIndexedf(face, 0, 0, GRIDSIZE, GRIDSIZE);
}
glViewportSwizzleNV(0, nZ, nY, pW, pX); // positive X face
glViewportSwizzleNV(1, pZ, nY, pW, nX); // negative X face
glViewportSwizzleNV(2, pX, pZ, pW, pY); // positive Y face
glViewportSwizzleNV(3, pX, nZ, pW, nY); // negative Y face
glViewportSwizzleNV(4, pX, nY, pW, pZ); // positive Z face
glViewportSwizzleNV(5, nX, nY, pW, nZ); // negative Z face
gl::ScopedDepth scopedDepthTest(true);
gl::ScopedMatrices dMat;
sats.draw();

gl::context()->popFramebuffer();

//reset viewports
for (int face = 0; face < 6; face++) {
glViewportSwizzleNV(face, pX, nY, pZ, pW); // positive Z face
}

One of my swizzles was wrong but it was an easy fix.

Then it’s drawn with the FramebufferTexture, not the fbo

gl::drawEquirectangular(textureCubeMap, Rectf(0, 0, getWindowWidth(), getWindowWidth()*.5), 0);

Capturea

1 Like

Congratulations for solving this! Maybe the ScopedFrameBuffer should be changed to work with an FboCubemap as well. And perhaps you can create a nice sample from this!

Have you compared performance?

Performance comparison WIN32 build on a gtx 1080 ti with a 1024 cubemap
Multi-pass (Cinder example method):
fps 1100
gpu% 90

Single-pass (This method):
fps 1300
gpu% 45

I’m curious to see how it performs with my larger scenes. The fps in this example is only about %15-20 faster but the GPU workload is a %200 improvement! Exactly what I needed. I also want to do a x64 build comparison…
I’ll upload a working example soon.

Here’s a working example of a single-Pass cubemap. It requires Paul’s reverse-z build of Cinder using GLAD. See the readme for details on how to set up.

3 Likes