Strawhare
Strawhare

Reputation: 486

Rendering a Dynamic CubemapArray

So I'm writing a program that will use framebuffers to dynamically set up a number of cubemaps, which I'm hoping to pack into an array for use in my shaders. I currently have the code set up to allow for singlular cubemaps, but I'm having trouble getting it to work for cubemaparrays.

My setup function for the cubemapArray:

void createCubemapArray(GLuint& cubemap)
{
    //Generate an array texture
    glGenTextures(1, &cubemap);
    glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, cubemap);

    //Create storage for the texture. (4 layers of 1x1 texels)
    glTexStorage3D(GL_TEXTURE_CUBE_MAP_ARRAY,
        1,                    //No mipmaps as textures are 1x1
        GL_RGB8,              //Internal format
        width, height,                 //width,height
        4                   //Number of layers
    );

    for (unsigned int i(0); i != 4; ++i)
    {
        glTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY,
            0,                     //Mipmap number
            0, 0, i,                 //xoffset, yoffset, zoffset
            1, 1, 1,                 //width, height, depth
            GL_RGB,                //format
            GL_UNSIGNED_BYTE,      //type
            0);                //pointer to data
    }

    glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}

This is called in my main function. I'm hoping I've set it up to allow for textures to be added to it during runtime.

This is then how I add a framebuffer texture to it:

    vec3 position = mirrorCam.getPosition();
    view = look_at(position, position + normalise(viewsX[i]), vec3(0.0, -0.5, -0.5));
    drawloop(view, proj, fb[0][i].framebuffer);
    glFramebufferTexture3D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubemapArray, 0, 0);

drawloop() is my main rendering loop, which renders most of the scene to the framebuffer I pass into it. mirrorCam is a camera object that I've set up for the cubemaps.

Then, to pass it into my shaders, I have these lines:

glBindTexture(GL_TEXTURE_2D, cubemapArray);
glUniform1i(glGetUniformLocation(reflectiveShaderID, "cube_array"), 0);

Finally, my shader code. Shortened to the important parts:

uniform samplerCubeArray cube_array;
//....
void main()
{
//...
fragment_color = texture(cube_array, vec4(textureCoordinates, 0));   
}

When all this is rendered, I get a black object, ie, no texture. However, when I swap in a single cubemap, it works fine, so I'm convinced the problem is either in my initialization of the array, or in how I bind the images to the array. Any ideas?

Upvotes: 1

Views: 1356

Answers (2)

skeelz
skeelz

Reputation: 87

I am not sure if the OP ever resolved this, but whoever needs help with this, let me share some sample code and explanation.

So the challenge here is to store and use cubemaps in an array. OpenGL offers us some fancy functions and enums to generate cubemap textures, allocate memory, and use them as render targets. Let's go through them step by step.

  1. Generate cubemap array texture - don't worry about size / count, you just need a name.

     glGenTextures(1, &cubemap_array);
    
  2. Bind cubemap array texture - note the special texture target.

     glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY, cubemap_array);
    
  3. Go ahead and add some parameters - this is also an important step, without which you could just get some black textures.

     glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
     glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
     glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
     glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    
  4. In the render pass bind the framebuffer and bind a cubemap's face to the color attachment of the framebuffer. (You could probably do this outside the render pass as well.)

       glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cubemap_array, 0, cubemap_index * 6 + face);
    
       // Do your rendering stuff here.
    

The important thing to notice here are the last two parameters of the glFramebufferTextureLayer(...) call.

The first parameter of the two (second from the end), defines the mip-map level of the texture that you are attaching to the color attachment. So, Strawhare, your comment on the previous answer suggests that your code won't work as expected. Ref - glFramebufferTextureLayer

Secondly, the last parameter is the only one that determines what face of what cubemap stored in the cubemap array is used as the color attachment.

It is defined as : 'Face of a specific cubemap' = 'Position of cubemap in the array' * 6 + 'Face Index'

E.g. - You have 12 cube maps in your array, but you need to set the front face of the 6th cube map as the render target. So you would need to set the last parameter to be - (5) * 6 + 0.

You can learn more about face indices in the context of cubemap arrays here - Cubemap Texture

Upvotes: 3

Nicol Bolas
Nicol Bolas

Reputation: 473447

First, let's get the obvious out of the way:

uniform samplerCube cube_texture;

If you want to read from a cubemap array, you must use samplerCubeArray as your sampler type.

Now, for the slightly less obvious:

glFramebufferTexture3D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cubemapArray, 0, 0);

If you are trying to render to each face of the cubemap array, that's not how to do it. It's not clear exactly what i represents, but if it's supposed to be a face of the first layer in the cubemap array, that's not how you do it.

See, cubemap array textures don't ever use the cubemap face targets. Instead, they use layer-faces. Also, cubemap array textures don't use glFramebufferTexture3D; they are only allowed with glFramebufferTexture or glFramebufferTextureLayer. You probably mean to use the latter.

So if you want i to mean the face index of the first layer in the cube map, you would do this:

glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, cubemapArray, 0, i);

Upvotes: 2

Related Questions