我的 FrameBuffer 上只渲染了一个纹理

Posted

技术标签:

【中文标题】我的 FrameBuffer 上只渲染了一个纹理【英文标题】:Only one texture gets rendered on my FrameBuffer 【发布时间】:2014-10-22 19:07:45 【问题描述】:

我正在尝试使用 SpriteBatch 和 FrameBuffer 渲染三张图像。

这些是我正在做的步骤。

    清除屏幕、深度和模板缓冲区。 绑定帧缓冲区。请参阅 FrameBuffer.bind()。 使用我的 SpriteBatch 渲染三个具有不同大小和不同位置的实体四边形。 解绑我的 FrameBuffer。请参阅 FrameBuffer.unbind()。 绑定我的 FrameBuffers colorTexture。 用我的 SpriteBatch 渲染我的 FrameBuffer。

这是我知道的关于这个问题的一些事实

纹理显示正确的颜色。他们不是白人。 显示了我渲染的第一个纹理,而其他两个没有。如果我更改顺序,仍然会渲染第一个纹理。 对于三个纹理,我的意思是三个独立的纹理 ID。 我已启用深度测试。 没有抛出 OpenGL 错误或 FrameBuffer 错误。 当使用我的 SpriteBatch 直接渲染到屏幕时,所有三个四边形都会显示出来。 我的 FrameBuffer 大小就是我的屏幕大小。

它应该是这样的

这是我的帧缓冲区的外观。

这是使用我的帧缓冲区并绘制第一个纹理两次的情况。仍然缺少其他两个纹理。定位不是问题。

这是我的 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;

下面是我创建纹理的方法:

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);

切换纹理时,我的 SpriteBatch 似乎有问题。以下是切换的工作原理。 我提醒你,使用我的 SpriteBatch 渲染到屏幕可以得到我想要的结果。但是渲染到 FrameBuffer 不起作用。

`

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;

` 将创建一个小示例。应该快起来了。

编辑: 问题不是我的 FrameBuffer。是我的 SpriteBatch。

【问题讨论】:

快速浏览一下您的代码,我注意到一个错误......当hasStencil hasDepth 都为真时会发生什么?您尝试将附件绑定到 DEPTH 和 STENCIL,但这在大多数情况下会导致兼容性问题。在这种特殊情况下,您应该添加第三种情况,将打包的深度+模板附件绑定到 GL_DEPTH_STENCIL_ATTACHMENT。这里更大的问题是您使用 16 位模板缓冲区,当您也有深度缓冲区时,这几乎永远不会起作用:-\ 是的,您可以为渲染缓冲区使用深度+模板图像格式。在这种情况下,您使用的是 32 位浮点深度缓冲区,因此您想要的格式是 GL_DEPTH32F_STENCIL8​。这实际上是一种特别讨厌的格式,硬件不喜欢 40 位数据,所以实际发生的情况是您浪费了额外的 24 位数据,因此打包的深度+模板被填充为 64 位。老实说,我建议您尝试使用 24 位深度缓冲区,因为这样您就可以使用 GL_DEPTH24_STENCIL8 而不会浪费任何内存/带宽。 不小心删除了我的问题。这里是为 Andon 的回答提供一些背景信息:我只需要创建一个渲染缓冲区吗? @AndonM.Coleman 我更新了代码,depth+stencil 的创建效果如何? 最后一件事:你能显示你实际清除缓冲区的代码吗?我期待在函数begin (...) 中看到它,但它不存在。您需要在绑定 FBO 时清除缓冲区,否则您只会清除与窗口关联的颜色/模板/深度缓冲区。 【参考方案1】:

您需要在绑定 FBO 时清除缓冲区,否则您只会清除与窗口关联的颜色/模板/深度缓冲区。这不是您想要的行为,因为这种情况下的深度测试使用您的 FBO 的深度而不是窗口的(默认帧缓冲区)。

public void begin () 是这样做最合乎逻辑的地方:

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);

【讨论】:

没有改变任何东西。 切换贴图好像有问题。 这是我没有做的事情。但还是。这不是答案。 @theodor:您显示的代码没有任何其他直接问题,但我认为问题与您的纹理绑定有关。除了应用纹理,您可以尝试为每个四边形分配不同的常量颜色(使用“颜色”制服)吗?这将排除这是纹理问题还是其他问题。 我很确定这是纹理绑定。我可以多次绘制相同的纹理,并且它们都被渲染,如您在第三张图像中看到的那样。我将尝试使用 glReadPixels 来查看它是否有效。 glReadPixels 和 glFramebufferTexture2D 有性能差异吗?。

以上是关于我的 FrameBuffer 上只渲染了一个纹理的主要内容,如果未能解决你的问题,请参考以下文章

libGDX:FrameBuffer 大小限制 == 最大纹理大小?

OpenGL ES 2.0 Framebuffer 渲染到纹理 iOS:没有显示

Framebuffer FBO渲染到纹理很慢,在Android上使用OpenGL ES 2.0,为啥?

在渲染纹理上绘制时出现问题。 (OpengL 3.3)

opengl Framebuffer离屏到纹理,颜色错误

是否可以在opengl中使用深度缓冲区渲染3D纹理