5

I implemented a new rendering pipeline in my engine and rendering is broken now. When I directly draw a texture of the G-Buffer to screen, it shows up correctly. So the G-Buffer is fine. But somehow the lighting pass makes trouble. Even if I don't use the resulting texture of it but try to display albedo from G-Buffer after the lighting pass, it shows a solid gray color.

I can't explain this behavior and the strange thing is that there are no OpenGL errors at any point.

Vertex Shader to draw a fullscreen quad.

#version 330

in vec4 vertex;

out vec2 coord;

void main()
{
    coord = vertex.xy;
    gl_Position = vertex * 2.0 - 1.0;
}

Fragment Shader for lighting.

#version 330

in vec2 coord;
out vec3 image;

uniform int type = 0;
uniform sampler2D positions;
uniform sampler2D normals;
uniform vec3 light;
uniform vec3 color;
uniform float radius;
uniform float intensity = 1.0;

void main()
{
    if(type == 0) // directional light
    {
        vec3 normal = texture2D(normals, coord).xyz;
        float fraction = max(dot(normalize(light), normal) / 2.0 + 0.5, 0);
        image = intensity * color * fraction;
    }
    else if(type == 1) // point light
    {
        vec3 pixel = texture2D(positions, coord).xyz;
        vec3 normal = texture2D(normals, coord).xyz;
        float dist = max(distance(pixel, light), 1);
        float magnitude = 1 / pow(dist / radius + 1, 2);
        float cutoff = 0.4;
        float attenuation = clamp((magnitude - cutoff) / (1 - cutoff), 0, 1);
        float fraction = clamp(dot(normalize(light - pixel), normal), -1, 1);
        image = intensity * color * attenuation * max(fraction, 0.2);
    }
}

Targets and samplers for the lighting pass. Texture ids are mapped to attachment respectively shader location.

unordered_map<GLenum, GLuint> targets;
targets.insert(make_pair(GL_COLOR_ATTACHMENT2, ...)); // light
targets.insert(make_pair(GL_DEPTH_STENCIL_ATTACHMENT, ...)); // depth and stencil

unordered_map<string, GLuint> samplers; 
samplers.insert(make_pair("positions", ...)); // positions from G-Buffer
samplers.insert(make_pair("normals", ...)); // normals from G-Buffer

Draw function for lighting pass.

void DrawLights(unordered_map<string, GLuint> Samplers, GLuint Program)
{
    auto lis = Entity->Get<Light>();

    glClear(GL_COLOR_BUFFER_BIT);

    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE, GL_ONE);

    glUseProgram(Program);
    int n = 0; for(auto i : Samplers)
    {
        glActiveTexture(GL_TEXTURE0 + n);
        glBindTexture(GL_TEXTURE_2D, i.second);
        glUniform1i(glGetUniformLocation(Program, i.first.c_str()), n);
        n++;
    }

    mat4 view = Entity->Get<Camera>(*Global->Get<unsigned int>("camera"))->View;

    for(auto i : lis)
    {
        int type = i.second->Type == Light::DIRECTIONAL ? 0 : 1;
        vec3 pos = vec3(view * vec4(Entity->Get<Form>(i.first)->Position(), !type ? 0 : 1));
        glUniform1i(glGetUniformLocation(Program, "type"),      type);
        glUniform3f(glGetUniformLocation(Program, "light"),     pos.x, pos.y, pos.z);
        glUniform3f(glGetUniformLocation(Program, "color"),     i.second->Color.x, i.second->Color.y, i.second->Color.z);
        glUniform1f(glGetUniformLocation(Program, "radius"),    i.second->Radius);
        glUniform1f(glGetUniformLocation(Program, "intensity"), i.second->Intensity);

        glBegin(GL_QUADS);
            glVertex2i(0, 0);
            glVertex2i(1, 0);
            glVertex2i(1, 1);
            glVertex2i(0, 1);
        glEnd();
    }

    glDisable(GL_BLEND);
    glActiveTexture(GL_TEXTURE0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindTexture(GL_TEXTURE_2D, 0);
}
danijar
  • 32,406
  • 45
  • 166
  • 297
  • 1
    You're calling `glDrawBuffers` here, so how do you know what attachment you are rendering to? Post output part for your frag shader. Also, what's the exact internalformat, format and type for the texture attachments? – Grimmy Jun 19 '13 at 11:26
  • The fragment shader uses `layout(location = 0)` and so on to write to the target attached to `GL_COLOR_ATTACHMENT0` and so on. For the G-Buffer pass there are three textures of the type `GL_RGB16F` `GL_RGB` `GL_FLOAT` and one of the type `GL_DEPTH24_STENCIL8` `GL_DEPTH_STENCIL` `GL_UNSIGNED_INT_24_8`. I can say that depth and stencil work, but not the other three targets. All targets get attached correctly, according to `glGetFramebufferAttachmentParameteriv`. – danijar Jun 19 '13 at 11:39
  • Well, given that it's the shaders that write into the G-Buffers I guess there might be a need to post them for more insight. – Christian Rau Jun 19 '13 at 13:08
  • @danijar *"The fragment shader uses `layout(location = 0)` and so on to write to the target attached to `GL_COLOR_ATTACHMENT0` and so on."* - That isn't quite correct though. The `location` specifies the draw buffer to write into (thus `location=0` would be the first attachment in the array given to `glDrawBuffers`) and if your targets don't contain `GL_COLOR_ATTACHMENTi` consecutively without gaps, then those two things won't be the same. Maybe that is the cause for your error? – Christian Rau Jun 19 '13 at 13:12
  • How are you checking that the albedo and normal textures are zero? Is it possible that you're sampling a mipmap level which hasn't been generated? – GuyRT Jun 19 '13 at 13:55
  • @ChristianRau I pasted the code above. Though I think it is exactly the case that attachment locations are consecutively. But thanks anyway, I didn't knew that. – danijar Jun 19 '13 at 14:11
  • "That's what she's said." – Jonathon Reinhart Jun 19 '13 at 14:12
  • @GuyRT How could I sample a mipmap level? I simply use `texture2D(sampler, texcoord)` in my shader code. – danijar Jun 19 '13 at 14:12
  • @danijar If the texture doesn't have mipmaps (which would be rather strange for a G-buffer) just use a non-mipmapped filter. But I would be surpirsed if you really used anything else than `GL_NEAREST` for a G-buffer access. – Christian Rau Jun 19 '13 at 14:16
  • Could you also show the shader code that accesses the G-buffer (i.e. the lighting shader)? And even better (and already asked) question, how do you see that the G-buffer images contain 0s? – Christian Rau Jun 19 '13 at 14:22
  • @ChristianRau, Yes, that's true. In normal use only top level of the the g-buffer would be sampled anyway (so it wouldn't need mip-maps). I just wondered whether danijar was displaying a little quad as a debug tool. I've wasted time before when the bug has been in my debugging (if you see what I mean) – GuyRT Jun 19 '13 at 14:24
  • @GuyRT Yeah, I see. Your comment was a good idea (and I'm still waiting for an answer by the OP on the general question). I was just responding to his bringing that comment into relation with his texture sampling method in the shader. – Christian Rau Jun 19 '13 at 14:31
  • @ChristianRau I use `GL_LINEAR` filtering for all render textures since there are half resolution passes for some effects. But thank you very much for the constructive question how I could know if the G-Buffer is actually filled with zeroes. It leads me to directly displaying the albedo to screen and it showed up correctly. So the issue must be in lighting shader or in binding its samplers. I've rewritten the question and included lighting pass related code. – danijar Jun 19 '13 at 16:39
  • @GuyRT The issue is about the lighting pass, the G-Buffer is fine. But strangely, I displaying albedo from the G-Buffer *after* performing the lighting pass results in a solid gray window. Without the lighting pass albedo is displayed correctly. – danijar Jun 19 '13 at 16:40
  • Is the lighting shader compiling OK? I noticed you have some integer constants in some otherwise floating point expressions. Some glsl compilers are fussier than others. – GuyRT Jun 19 '13 at 16:53
  • @GuyRT There are no compiler errors. Moreover, I used the same shader in my old rendering pipeline without problems. That makes it even harder to find the problem. I can understand why the light pass breaks the displaying something from the G-Buffer even if its result isn't used. – danijar Jun 19 '13 at 16:58

1 Answers1

0

I found the error and it was such a stupid one. The old rendering pipeline bound the correct framebuffer before calling the draw function of that pass. But the new one didn't so each draw function had to do that itself. Therefore I wanted to update all draw function, but I missed the draw function of the lighting pass.

Therefore the framebuffer of the G-Buffer was still bound and the lighting pass changed its targets.

Thanks to you guys, you had no change to find that error, since I hadn't posted my complete pipeline system.

danijar
  • 32,406
  • 45
  • 166
  • 297