0
votes

I've three images which I'm trying to render with my SpriteBatch and FrameBuffer.

These are the steps I'm doing.

  1. Clear the screen, depth and stencil buffers.
  2. Bind the FrameBuffer. See FrameBuffer.bind().
  3. Use my SpriteBatch to render three solid quads with different sizes and different positions.
  4. Unbind my FrameBuffer. See FrameBuffer.unbind().
  5. Bind my FrameBuffers colorTexture.
  6. Render my FrameBuffer with my SpriteBatch.

Here's some facts I know about the this problem

  • The textures shows the right color. They are not white.
  • The first texture I render is shown, while the other two isn't. If I change the order, the first texture still gets rendered.
  • With three textures, I mean three separate texture ids.
  • I've depth test enabled.
  • No OpenGL error or FrameBuffer error is being thrown.
  • When rendering using my SpriteBatch directly to the screen all three quads is shown.
  • The size of my FrameBuffer is the size of my screen.

Here's how it's supposed to look like Here's how it's supposed to look like

Here's how it's looking with my framebuffer. Here's how it's looking with my framebuffer.

Here's how it's looking with my framebuffer and drawing the first texture two times. Still missing the other two textures. Position in not the problem. Here's how it's looking with my framebuffer and drawing the first texture two times. Still missing the other two textures. Position in not the problem.

Here's the code for my FrameBuffer :

// Stencil is broken currently broken, in my example. I'm not using any stencil buffers.
public FrameBuffer(int width, int height, boolean hasDepth, boolean hasStencil) {
    this.width = width;
    this.height = height;
    frameBufferID = GL30.glGenFramebuffers();
    GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, frameBufferID);

    if (hasDepth && hasStencil) {
        int depthAndStencilBufferID = GL30.glGenRenderbuffers();

        GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, depthAndStencilBufferID);
        GL30.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL30.GL_DEPTH24_STENCIL8, width, height);
        GL30.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_STENCIL_ATTACHMENT, GL30.GL_RENDERBUFFER, depthAndStencilBufferID);
    }

    else if (hasDepth) {
        int depthBufferID = GL30.glGenRenderbuffers();

        GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, depthBufferID);
        GL30.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL30.GL_DEPTH_COMPONENT32F, width, height);
        GL30.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_ATTACHMENT, GL30.GL_RENDERBUFFER, depthBufferID);
    }

    else if (hasStencil) {
        int stencilBufferID = GL30.glGenRenderbuffers();
        GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, stencilBufferID);
        GL30.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL30.GL_STENCIL_INDEX16, width, height);
        GL30.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_STENCIL_ATTACHMENT, GL30.GL_RENDERBUFFER, stencilBufferID);
    }

    colorTexture = new Texture(width, height);
    colorTexture.bind();

    GL30.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL11.GL_TEXTURE_2D, colorTexture.getTextureID(), 0);
    int result = GL30.glCheckFramebufferStatus(GL30.GL_FRAMEBUFFER);
    //error handling
    RenderUtil.unbindFrameBuffer();
}

private Texture colorTexture;

private int width, height;

private int frameBufferID, depthBufferID, stencilBufferID;

public void begin () {
    GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, frameBufferID);
    GL11.glViewport(0, 0, width, height);
}

public void end () {
    GL11.glViewport(0, 0, RenderingEngine.getWidth(), RenderingEngine.getHeight());
    RenderUtil.unbindFrameBuffer();
    RenderUtil.unbindTextures();
}

public Texture getColorTexture () {
    return colorTexture;
}

public int getWidth () {
    return width;
}

public int getHeight () {
    return height;
}

Here's how I create a Texture if it's to any use :

private void loadTexture (ByteBuffer buffer, int textureID, int width, int height) {
    GL13.glActiveTexture(GL13.GL_TEXTURE0);
    GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID);

    GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);

    GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, width, width, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer);

    GL30.glGenerateMipmap(GL11.GL_TEXTURE_2D);

    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);

    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
    GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_LINEAR);
}

There seems to be a problem in my SpriteBatch when switching texture. Here's how the switching works. I remind you, using my SpriteBatch to render to the screen gives my the wanted result. But rendering to a FrameBuffer doesn't work.

`

public void flush () {

    flushes++;
    buffer.flip();
    MESH.setVertices(buffer);

    if (lastTexture != null) {
        lastTexture.bind();
    }

    MESH.draw();

    buffer.clear();
}

/** Begins the SpriteBatch. */
public void begin () {
    if (rendering) {
        new IllegalStateException("You have to SpriteBatch.end() before calling begin again!").printStackTrace();
    }
    flushes = 0;
    rendering = true;
    shader.bind();
    shader.setUniformMat4f("projection", camera.getCombined());
    shader.setUniformVec4("color", color);
}

/** Ends the SpriteBatch, also flushes it. */
public void end () {
    if (!rendering) {
        new IllegalStateException("You've to SpriteBatch.begin before ending the spritebatch").printStackTrace();
    }
    rendering = false;
    flush();
    RenderUtil.unbindShaders();
    RenderUtil.unbindTextures();
}

/** Switches the textures and flushes the old one.
 * @param t */
private void switchTextures (Texture t) {
    if (lastTexture != null) {
        flush();
    }
    lastTexture = t;
}

` Going to create a small example. Should be up soon.

EDIT: Problem wasn't my FrameBuffer. Was my SpriteBatch.

1
Quickly glancing at your code, I notice one mistake... what happens when hasStencil and hasDepth are both true? You try to bind attachments to DEPTH and STENCIL, but that will result in compatibility issues most of the time. You should add a third case that binds a packed depth+stencil attachment to GL_DEPTH_STENCIL_ATTACHMENT in that special case. An even bigger issue here is your use of a 16-bit stencil buffer, that's pretty much never going to work when you also have a depth buffer :-\ - Andon M. Coleman
Yes, you would use a depth+stencil image format for the renderbuffer. In this case, you're using a 32-bit floating-point depth buffer, so the format you would want is GL_DEPTH32F_STENCIL8​. That is actually a particularly nasty format, hardware doesn't like 40-bit data, so what actually happens is you waste an additional 24-bits worth of data so that the packed depth+stencil is padded out to 64-bit total. I would honestly suggest you try a 24-bit depth buffer instead, because then you can use GL_DEPTH24_STENCIL8 and not waste any memory/bandwidth. - Andon M. Coleman
Accidentally removed my question. Here it is to give some context to Andon's answer : Do I only need to only create one render buffer? - thetheodor
@AndonM.Coleman I updated the code, how does the creating of depth+stencil look? - thetheodor
One last thing: Can you show the code where you actually clear the buffers? I was expecting to see it in the function begin (...), but it's not there. You need to clear your buffers while your FBO is bound, otherwise you're only clearing the color/stencil/depth buffer associated with your window. - Andon M. Coleman

1 Answers

3
votes

You need to clear your buffers while your FBO is bound, otherwise you're only clearing the color/stencil/depth buffer associated with your window. That is not the behavior you want, because the depth test in this scenario uses your FBO's depth rather than the window's (default framebuffer).

public void begin () is the most logical place to do this:

public void begin () {
    GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, frameBufferID);
    GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT);
    GL11.glViewport(0, 0, width, height);
}