1
\$\begingroup\$

I'm trying to implement deferred rendering and for now I only have diffuse, normal (in world space) and position (in world space) as textures inside the GBuffer. I create the GBuffer like this:

void GBuffer::Initialize(unsigned int width, unsigned int height)
{
    glGenTextures(1,&_diffuseTexture);
    glGenTextures(1,&_worldPosTexture);
    glGenTextures(1,&_normalTexture);

    auto textureSetup = [](unsigned int width, unsigned int height) {
        glTexImage2D(GL_TEXTURE_2D,0,GL_RGB32F,width,height,0,GL_RGB,GL_FLOAT,0);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
    };

    glBindTexture(GL_TEXTURE_2D,_diffuseTexture);
    textureSetup(resolutionX,resolutionY);
    glBindTexture(GL_TEXTURE_2D,_worldPosTexture);
    textureSetup(resolutionX,resolutionY);
    glBindTexture(GL_TEXTURE_2D,_normalTexture);
    textureSetup(resolutionX,resolutionY);

    glGenFramebuffers(1,&_framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER,_framebuffer);

    glGenRenderbuffers(1,&_depthBuffer);
    glBindRenderbuffer(GL_RENDERBUFFER,_depthBuffer);
    glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH_COMPONENT,width,height);

    //attach the depth buffer as well as the textures to the framebuffer
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,GL_RENDERBUFFER,_depthBuffer);
    glFramebufferTexture(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,_diffuseTexture,0);
    glFramebufferTexture(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT1,_worldPosTexture,0);
    glFramebufferTexture(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT2,_normalTexture,0);

    GLenum drawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2};
    glDrawBuffers(3,drawBuffers);

    glBindFramebuffer(GL_FRAMEBUFFER,0);

}

As you can see, at the bottom of the function glDrawBuffers is called so that (at least intentionally) first the diffuse texture is drawn to, then the position texture, and then the normal texture.

The shaders for them look like this (here worldViewProjection and world matrices are attributes because I use them in instanced rendering. They're actually only advanced once per instance of the mesh that I draw):

Vertex:

#version 430

layout(location = 0) in vec4 position;
layout(location = 1) in vec4 vertexColor;
layout(location = 2) in vec4 vertexNormal;
layout(location = 3) in mat4 worldViewProjection;
layout(location = 7) in mat4 world;

smooth out vec3 diffuse;
smooth out vec3 worldPosition;
smooth out vec3 normal;


void main()
{
    gl_Position = worldViewProjection * position;
    worldPosition = (world * position).xyz;
    normal = world * vertexNormal;
    diffuse = vertexColor;
}

Fragment:

#version 430

smooth in vec3 diffuse;
smooth in vec3 worldPosition;
smooth in vec3 normal;


out vec3 outDiffuse;
out vec3 outWorldpos;
out vec3 outNormal;

void main()
{
    outDiffuse = diffuse.xyz;
    outWorldpos = worldPosition;
    outNormal = normalize(normal);
}

During rendering I do nothing but

glBindFramebuffer(GL_FRAMEBUFFER,gbuffer->_framebuffer);

before I start rendering, then draw the geometry using the shader program.

The rendered results look correct, the only problem is that the program draws results of the position into the normal texture, and vice versa (weirdly enough, the diffuse texture is correctly drawn to).

This is a screenshot of the test draw, where I expect bottom left to be diffuse texture (correct), bottom right position texture and top left normal texture (those two are switched):

enter image description here

Just to make sure that I don't just draw them on the screen reversed, this is how I actually display the textures:

void sun::graphics::DrawTexture(GLuint texture,  unsigned int posX, unsigned int posY , unsigned int width , unsigned int height )
{
    if(!inited2)
    {
        glGenBuffers(1,&buffer2);
        inited2 = true;

        /*glGenSamplers(1, &sampler);
        glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);*/
    }
    glViewport(posX,posY,width,height);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,texture);

    FullscreenTextureShader->Bind();
    GLuint loc = glGetUniformLocation(FullscreenTextureShader->_program,"tex");
    glUniform1i(loc,0);

    float vertices[] = 
    {
        -1.0f,1.0f,-1.0f,1.0f,
        1.0f,1.0f,-1.0f,1.0f,
        1.0f,-1.0f,-1.0f,1.0f,
        1.0f,-1.0f,-1.0f,1.0f,
        -1.0f,-1.0f,-1.0f,1.0f,
        -1.0f,1.0f,-1.0f,1.0f,
    };
    glBindBuffer(GL_ARRAY_BUFFER,buffer2);
    glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STREAM_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0,4,GL_FLOAT,GL_FALSE,0,0);

    glDrawArrays(GL_TRIANGLES,0,6);

    glDisableVertexAttribArray(0);

    FullscreenTextureShader->Unbind();
}

and call them like this (never mind attribute access, I just made everything public for debugging comfort, will switch to private soon):

DrawTexture(renderer->_gbuffer._diffuseTexture,0,0,400,300); //bottom left
DrawTexture(renderer->_gbuffer._worldPosTexture,400,0,400,300); //bottom right
DrawTexture(renderer->_gbuffer._normalTexture,0,300,400,300); //top left

Where could the problem be here?

edit: to confirm that there's nothing wrong with how it's displayed on the screen, if I disable two of those calls and only draw, e.g. worldPosTexture, it does indeed show up with the contents of what should be in the normal texture.

Also, if I reverse the last two elements in the array that I give to glDrawBuffers, it's indeed drawn correctly.

\$\endgroup\$
  • 3
    \$\begingroup\$ Have you tried using the layout qualifier for those outputs in the fragment shader, like layout(location = 0) out vec3 outDiffuse; and so on? \$\endgroup\$ – kolrabi Aug 15 '13 at 6:59
  • \$\begingroup\$ @kolrabi No, not yet. But if they're actually required that would of course be the reason. Is the layout location for color attachments or depth attachments (if I use them as output textures) defined so that the first attachment I add will use location 0? Otherwise how do I assign the color, depth or stencil attachments to a certain location in the shader? \$\endgroup\$ – TravisG Aug 15 '13 at 18:27
  • \$\begingroup\$ @kolrabi I have found part of the official reference that explains this, thanks! opengl.org/wiki/Fragment_Shader#Outputs \$\endgroup\$ – TravisG Aug 15 '13 at 18:35
4
\$\begingroup\$
out vec3 outDiffuse;
out vec3 outWorldpos;
out vec3 outNormal;

Remember how you did this in your vertex shader?

layout(location = 0) in vec4 position;

The location qualifier specified which attribute the position comes from. Fragment shader outputs have the same concept. Each output goes to a location; if you don't explicitly specify the location, then the shader will arbitrarily assign them. And no, the shader is not required to start from zero and assign them sequentially.

So you should use the same syntax in your fragment shader. You set up your draw buffers like this:

GLenum drawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2};

So you need to assign the locations appropriately. Given the names of the textures you used, you should use the following:

layout(location = 0) out vec3 outDiffuse;
layout(location = 1) out vec3 outWorldpos;
layout(location = 2) out vec3 outNormal;

Note that the locations are not the GL_COLOR_ATTACHMENT# numbers. They're the indices into the draw buffers array. So 0 in this case means drawBuffers[0]. It only refers to GL_COLOR_ATTACHMENT0 because that's what is in drawBuffers[0]. You could just as easily set GL_COLOR_ATTACHMENT1 to index 0; the outDiffuse would then be written to GL_COLOR_ATTACHMENT1

\$\endgroup\$
-1
\$\begingroup\$

I can see that your fragment shader is the culprit. The problem is in the way you are storing the output of the fragment shader in the Gbuffer.

This is how I do it, and how it works for me flawlessly:

out vec4 GBuffer[3]; // how I declare it
...
GBuffer[0] = diffuse; // and how I set them
GBuffer[1] = normal;
GBuffer[2] = position;
\$\endgroup\$
  • \$\begingroup\$ If this works that would solve it pretty easily. I'll try it and come back and accept if it works, thanks! \$\endgroup\$ – TravisG Aug 15 '13 at 18:27
  • 2
    \$\begingroup\$ -1: @TravisG: if that worked for you, it only did so by accident. The OpenGL implementation is not required to make that work. \$\endgroup\$ – Nicol Bolas Aug 16 '13 at 0:39
  • \$\begingroup\$ @NicolBolas I shouldn't have blindly trusted my compiler... I see it isn't right, but could you point me to the part in the specification that describes this behavior? \$\endgroup\$ – akaltar Aug 16 '13 at 9:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.