2

enter image description here

I have written a crude shadow mapping implementation which renders the scene 6 times with 6 different view matrices to create the cubemap.

As an optimisation, I am trying to upgrade to the single pass approach using the geometry shader but struggling to get any output from my shaders.

My problem begins when binding the cubemap texture. With multi-pass, I simply bind 1 of 6 FBOs at a time, however with single-pass I need to bind all 6 cubemap faces at once and I don't have a handle for that. In these cases, the documentation suggests creating a single FBO with FrameBufferTexture that contains all 6 faces. Have I done this correctly?

Alternatively, the issue could be routed in the geometry shader, but seeing as I'm getting no output whatsoever, it's very difficult to debug there.

SINGLE PASS (no output)

Fbo creation

// Create the FBO
GL.GenFramebuffers(1, out FBO_handle);

// Create and bind the cubemap Texture
GL.GenTextures(1, out cubeMapTexture);
GL.BindTexture(TextureTarget.TextureCubeMap, cubeMapTexture);

// Generate each of the single cubemap faces as 2D texture
GL.TexImage2D(TextureTarget.TextureCubeMapPositiveX, 0, PixelInternalFormat.R16f, size, size, 0, PixelFormat.Red, PixelType.Float, IntPtr.Zero);
GL.TexImage2D(TextureTarget.TextureCubeMapNegativeX, 0, PixelInternalFormat.R16f, size, size, 0, PixelFormat.Red, PixelType.Float, IntPtr.Zero);
GL.TexImage2D(TextureTarget.TextureCubeMapPositiveY, 0, PixelInternalFormat.R16f, size, size, 0, PixelFormat.Red, PixelType.Float, IntPtr.Zero);
GL.TexImage2D(TextureTarget.TextureCubeMapNegativeY, 0, PixelInternalFormat.R16f, size, size, 0, PixelFormat.Red, PixelType.Float, IntPtr.Zero);
GL.TexImage2D(TextureTarget.TextureCubeMapPositiveZ, 0, PixelInternalFormat.R16f, size, size, 0, PixelFormat.Red, PixelType.Float, IntPtr.Zero);
GL.TexImage2D(TextureTarget.TextureCubeMapNegativeZ, 0, PixelInternalFormat.R16f, size, size, 0, PixelFormat.Red, PixelType.Float, IntPtr.Zero);

// Set the suitable texture parameters
GL.TexParameter(TextureTarget.TextureCubeMap, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
GL.TexParameter(TextureTarget.TextureCubeMap, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.TextureCubeMap, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.TextureCubeMap, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.TextureCubeMap, TextureParameterName.TextureWrapR, (int)TextureWrapMode.ClampToEdge);
GL.TexParameter(TextureTarget.TextureCubeMap, TextureParameterName.TextureBaseLevel, 0);
GL.TexParameter(TextureTarget.TextureCubeMap, TextureParameterName.TextureMaxLevel, 0);

// Attach cubemap texture as the FBO's color buffer
GL.BindFramebuffer(FramebufferTarget.Framebuffer, FBO_handle);
GL.FramebufferTexture(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, cubeMapTexture, 0);

Geometry Shader

#version 330

layout(triangles) in;
layout (triangle_strip, max_vertices=18) out;

uniform mat4 projectionMatrix;
uniform mat4 shadowTransforms[6];
out vec4 frag_position;

void main()
{   
    for(int face = 0; face < 6; face++)
    {
        gl_Layer = face;
        for(int i=0; i<3; i++)
        {
            frag_position = shadowTransforms[face] * gl_in[i].gl_Position;
            gl_Position = projectionMatrix * shadowTransforms[shadowTransforms] * gl_in[i].gl_Position;

            EmitVertex();
        }
        EndPrimitive();
    }
}

Rendering

for (int j = 0; j < lights.Count; j++)
{
    // GL setup
    GL.BindFramebuffer(FramebufferTarget.Framebuffer, shadowBuffers[j].FBO_handle);
    GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
    Matrix4 viewMatrix = Matrix4.Identity;

    // Create the light's view matrices
    List<Matrix4> shadowTransforms = new List<Matrix4>();
    shadowTransforms.Add(Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(1, 0, 0), new Vector3(0, -1, 0)));
    shadowTransforms.Add(Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(-1, 0, 0), new Vector3(0, -1, 0)));
    shadowTransforms.Add(Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(0, 1, 0), new Vector3(0, 0, -1)));
    shadowTransforms.Add(Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(0, -1, 0), new Vector3(0, 0, -1)));
    shadowTransforms.Add(Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(0, 0, 1), new Vector3(0, -1, 0)));
    shadowTransforms.Add(Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(0, 0, -1), new Vector3(0, -1, 0)));

    // Send them to the shader
    for (int i = 0; i < 6; ++i)
    {
        Matrix4 shadowTransform = shadowTransforms[i];
        GL.UniformMatrix4(shader.getUniformID("shadowTransforms[" + i + "]"), false, ref shadowTransform);
    }

    // Draw Scene
    for (int r = 0; r < renderables.Count; r++)
        renderables[r].draw(shader);
}

I apologise for the massive code dump, FBOs are kinda lengthy, I'm sure you know. For those unfamiliar with the OpenTK c# wrapper, I figure it'd probably be beneficial if I also provide the working multi pass code.

MULTI PASS (working fine)

Fbo creation

// Create cubemap texture
GL.GenTextures(1, out cubeTex);
GL.BindTexture(TextureTarget.TextureCubeMap, cubeTex);
GL.TexParameter(TextureTarget.TextureCubeMap, TextureParameterName.TextureBaseLevel, 0);
GL.TexParameter(TextureTarget.TextureCubeMap, TextureParameterName.TextureMaxLevel, 0);
GL.TexParameter(TextureTarget.TextureCubeMap, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
GL.TexParameter(TextureTarget.TextureCubeMap, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
GL.TexImage2D(TextureTarget.TextureCubeMapPositiveX, 0, PixelInternalFormat.R16f, size, size, 0, PixelFormat.Red, PixelType.Float, IntPtr.Zero);
GL.TexImage2D(TextureTarget.TextureCubeMapNegativeX, 0, PixelInternalFormat.R16f, size, size, 0, PixelFormat.Red, PixelType.Float, IntPtr.Zero);
GL.TexImage2D(TextureTarget.TextureCubeMapPositiveY, 0, PixelInternalFormat.R16f, size, size, 0, PixelFormat.Red, PixelType.Float, IntPtr.Zero);
GL.TexImage2D(TextureTarget.TextureCubeMapNegativeY, 0, PixelInternalFormat.R16f, size, size, 0, PixelFormat.Red, PixelType.Float, IntPtr.Zero);
GL.TexImage2D(TextureTarget.TextureCubeMapPositiveZ, 0, PixelInternalFormat.R16f, size, size, 0, PixelFormat.Red, PixelType.Float, IntPtr.Zero);
GL.TexImage2D(TextureTarget.TextureCubeMapNegativeZ, 0, PixelInternalFormat.R16f, size, size, 0, PixelFormat.Red, PixelType.Float, IntPtr.Zero);

// Create cubemap FBOs
GL.GenFramebuffers(6, cubeFBOs);
for (int i = 0; i < 6; i++) 
{
    GL.BindFramebuffer(FramebufferTarget.Framebuffer, cubeFBOs[i]);
    GL.FramebufferTexture2D(FramebufferTarget.Framebuffer, FramebufferAttachment.ColorAttachment0, TextureTarget.TextureCubeMapPositiveX + i, cubeTex, 0);
}

Geometry shader

#version 330

layout(triangles) in;
layout(triangle_strip, max_vertices=3) out;

uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
out vec4 frag_position;

void main()
{
    for(int i=0; i<3; i++)
    {
        frag_position = viewMatrix * gl_in[i].gl_Position;
        gl_Position = projectionMatrix * viewMatrix * gl_in[i].gl_Position;
        EmitVertex();
    }
    EndPrimitive();
}

Rendering

for (int j = 0; j < lights.Count; j++)
{
    for (int i = 0; i < 6; ++i)
    {
        GL.BindFramebuffer(FramebufferTarget.Framebuffer, shadowBuffers[j].cubeFBOs[i]);
        GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
        Matrix4 viewMatrix = Matrix4.Identity;

        if (i == 0) viewMatrix = Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(1, 0, 0), new Vector3(0, -1, 0));
        if (i == 1) viewMatrix = Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(-1, 0, 0), new Vector3(0, -1, 0));
        if (i == 2) viewMatrix = Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(0, 1, 0), new Vector3(0, 0, -1));
        if (i == 3) viewMatrix = Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(0, -1, 0), new Vector3(0, 0, -1));
        if (i == 4) viewMatrix = Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(0, 0, 1), new Vector3(0, -1, 0));
        if (i == 5) viewMatrix = Matrix4.LookAt(lights[j].position, lights[j].position + new Vector3(0, 0, -1), new Vector3(0, -1, 0));
        GL.UniformMatrix4(shader.getUniformID("viewMatrix"), false, ref viewMatrix);

        // Draw Scene
        for (int r = 0; r < renderables.Count; r++)
            renderables[r].draw(shader);
    }
}

Fragment Shader

#version 330

precision lowp float;

in vec4 frag_position;

layout (location = 0) out vec4 outColor;

void main() {
    float depth = length( vec3(frag_position) ) / 20;

    float moment1 = depth;
    float moment2 = depth * depth;

    float dx = dFdx(depth);
    float dy = dFdy(depth);
    moment2 += 0.25*(dx*dx+dy*dy);

    outColor = vec4( moment1, moment2, 0.0, 0.0);
}

Any insight would be greatly appreciated, thank you!

livin_amuk
  • 1,285
  • 12
  • 26
  • So... what about your depth buffer? Did you check if your FBO was complete? Or if you got OpenGL errors? – Nicol Bolas Oct 24 '16 at 15:39
  • I'm storing the depth in a texture for the time being. I'll add the fragment shader above, it will make sense once you see that. – livin_amuk Oct 24 '16 at 15:43
  • FBO is complete in both cases. I'm not sure how to check for OpenGL errors but I'll get onto it. – livin_amuk Oct 24 '16 at 15:52

1 Answers1

2

Embarrassingly, the cause was a combination of typos in the geometry shader.

shadowTransforms[shadowTransforms] needed to be shadowTransforms[face] and

uniform mat4 shadowTransforms[6] needed to be uniform mat4 shadowTransforms[].

enter image description here

This marks a huge break through for me. Multiple shadow casting point lights at 60 fps. Thank you to everybody who has helped me along the way.

livin_amuk
  • 1,285
  • 12
  • 26
  • And for the record Alfonse, good call on the depth buffer, it was basically a fluke that it was working without it. Shadows were leaking into other rooms, I simply hadn't checked them. – livin_amuk Oct 24 '16 at 17:29