Layered rendering FBO

I am trying to implement layered rendering to render cascaded shadow maps in one pass via geometry shader. The idea is to use GL_TEXTURE_2D_ARRAY with 3 texture for each cascade. This is the framebuffer init code:

CSMTarget::CSMTarget(int w, int h) : _width(w), _height(h){
    // generate buffers:
    glGenTextures(1, &_layeredTexture);
    glGenFramebuffers(1, &_frameBuffer);
    //glGenRenderbuffers(1, &_depthrenderbuffer);

    // configure texture:
    glBindTexture(GL_TEXTURE_2D_ARRAY, _layeredTexture);
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGB32F, _width, _height, 3, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
    glBindTexture(GL_TEXTURE_2D_ARRAY, 0);

    glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
    glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _layeredTexture, 0);

    // depth render buffer:
    //glBindRenderbuffer(GL_RENDERBUFFER, _depthrenderbuffer);
    //glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, _width, _height);
    //glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthrenderbuffer);

    GLenum DrawBuffers[1] = { GL_COLOR_ATTACHMENT0 };
    glDrawBuffers(1, DrawBuffers);

    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

Frame buffer is complete.
Rendering to framebuffer:

glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
glViewport(0, 0, _width, _height);
//bind and update shader
//geometry pass
//unbind framebuffer

geometry shader:

#version 330
layout(triangles) in;
layout(triangle_strip, max_vertices = 3) out;
in vec2 uv_vert0[];
out vec2 uv_frag0;

uniform mat4 viewProjMatrices[3];
void main() {
    for(int face = 0; face < 3; face++) {
        gl_Layer = face;
        for(int i = 0; i < 3; i++) {
            uv_frag0 = uv_vert0[i];
            gl_Position = viewProjMatrices[face] * gl_in[i].gl_Position;
            EmitVertex();
        }
        EndPrimitive();
    }
}

Matrices are updated by glUniformMatrix4fv from array of 3 4x4 matrices.
And this is the part of directional light fragment shader that checks whether pixel is in shadow or not.

#version 330
vec2 sampleSM_A(inout float bias){
    vec4 shadowUV = pc.dirLight.matrixA * vec4(data.worldPos,1);
    shadowUV.xyz = shadowUV.xyz * 0.5 + 0.5;
    if(shadowUV.x > 1 || shadowUV.y > 1 || shadowUV.x < 0 || shadowUV.y < 0) return vec2(-1.0, 0.0);

    bias = 0.00025;

    return vec2(texture(shadowMap, vec3(shadowUV.xy, 0)).x, shadowUV.z);
}

For sampleSM_B and C shadow map is sampled with texture(shadowMap, vec3(shadowUV.xy, LAYER_ID)). Red is first frustum split, green and pink are second and third.

The problem is that only first split works correctly. Shadow mapping works fine for all three splits when I use 3 frame buffers with texture_2D and 3 passes.
This is enabled glEnable(GL_TEXTURE_2D_ARRAY).

Is it a good idea if I use MRT with 3 textures instead of layered rendering?

5I9UK

You generate 3 triangles from 1 triangle. 3 triangles are 9 vertices, so you should set max_vertices to 9, not 3.

From my experience, it is not a good idea to use a geometry shader to render your objects into multiple frustum splits within a single pass. In my case frustum culling on CPU was too coarse and I wasted lots of GPU-cycles just to clip triangles that weren’t visible in the final image. See this thread for the details:
https://www.opengl.org/discussion_boards/showthread.php/200731-Optimizing-cascaded-shadow-mapping?p=1291643
As always, there is no general answer to this, I would implement several methods, profile the code and then you’ll see what gives the best performance.

My experience agrees with fleissna’s here as well. The geometry shader is an expensive way to replicate a lot of geometry, and it particularly stinks when there is a lot of out-of-frustum geometry, which there is in your case. But definitely try it both ways and see for yourself. You can find mention of this geometry shader performance issue in many graphics talks and presentations.

This is enabled glEnable(GL_TEXTURE_2D_ARRAY).

Yeah, “enabling” texture units drove shader generation with the ol’ pre-shaders fixed-function pipeline, which you don’t appear to be using (given your geometry shader). You don’t enable/disable them anymore. You just bind textures to them and then use the bound textures in your shader code.

Is it a good idea if I use MRT with 3 textures instead of layered rendering?

No, definitely not when your replicating so much out-of-frustum geometry that’s just going to get thrown in the bit bucket later. And even if you’re not, they’re still expensive. Use them with care.