延迟渲染 - Renderbuffer vs Texture

5

我一直在研究这个问题,但仍未得出结论。有些实例使用纹理作为其渲染目标,有些人使用渲染缓冲区,还有一些人同时使用两者!

例如,只使用纹理:

// Create the gbuffer textures
glGenTextures(ARRAY_SIZE_IN_ELEMENTS(m_textures), m_textures);
glGenTextures(1, &m_depthTexture);

for (unsigned int i = 0 ; i < ARRAY_SIZE_IN_ELEMENTS(m_textures) ; i++) {
    glBindTexture(GL_TEXTURE_2D, m_textures[i]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, WindowWidth, WindowHeight, 0, GL_RGB, GL_FLOAT, NULL);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, m_textures[i], 0);
}

both:

glGenRenderbuffersEXT ( 1, &m_diffuseRT );
glBindRenderbufferEXT ( GL_RENDERBUFFER_EXT, m_diffuseRT );
glRenderbufferStorageEXT ( GL_RENDERBUFFER_EXT, GL_RGBA, m_width, m_height );
glFramebufferRenderbufferEXT ( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, m_diffuseRT );
glGenTextures ( 1, &m_diffuseTexture );
glBindTexture ( GL_TEXTURE_2D, m_diffuseTexture );
glTexImage2D ( GL_TEXTURE_2D, 0, GL_RGBA, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL );
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
// Attach the texture to the FBO
glFramebufferTexture2DEXT ( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_diffuseTexture, 0 );

有什么不同?创建纹理、渲染缓冲区,然后将一个分配给另一个有什么意义?当您成功地为纹理提供图像时,它已经分配了内存,那么为什么需要将其绑定到渲染缓冲区?

为什么要使用纹理或渲染缓冲区?有什么优点?

我读到过,无法从渲染缓冲区读取数据,只能从纹理中读取。那么它有什么用处呢?

编辑: 因此,我当前的GBuffer代码如下:

    enum class GBufferTextureType
        {
        Depth = 0,
        Position,
        Diffuse,
        Normal,
        TexCoord
        };

. . .

glGenFramebuffers ( 1, &OpenGLID );
if ( Graphics::GraphicsBackend->CheckError() == false )
    {
    Delete();
    return false;
    }

glBindFramebuffer ( GL_FRAMEBUFFER, OpenGLID );
if ( Graphics::GraphicsBackend->CheckError() == false )
    {
    Delete();
    return false;
    }

uint32_t TextureGLIDs[5];
glGenTextures ( 5, TextureGLIDs );
if ( Graphics::GraphicsBackend->CheckError() == false )
    {
    Delete();
    return false;
    }

// Create the depth texture
glBindTexture ( GL_TEXTURE_2D, TextureGLIDs[ ( int ) GBufferTextureType::Depth] );
glTexImage2D ( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, In_Dimensions.x, In_Dimensions.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL );
glFramebufferTexture2D ( GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, TextureGLIDs[ ( int ) GBufferTextureType::Depth], 0 );

// Create the color textures
for ( unsigned cont = 1; cont < 5; ++cont )
    {
    glBindTexture ( GL_TEXTURE_2D, TextureGLIDs[cont] );
    glTexImage2D ( GL_TEXTURE_2D, 0, GL_RGB32F, In_Dimensions.x, In_Dimensions.y, 0, GL_RGB, GL_FLOAT, NULL );
    glFramebufferTexture2D ( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + cont, GL_TEXTURE_2D, TextureGLIDs[cont], 0 );
    }

// Specify draw buffers
GLenum DrawBuffers[4];
for ( unsigned cont = 0; cont < 4; ++cont )
    DrawBuffers[cont] = GL_COLOR_ATTACHMENT0 + cont;

glDrawBuffers ( 4, DrawBuffers );

if ( Graphics::GraphicsBackend->CheckError() == false )
    {
    Delete();
    return false;
    }

GLenum Status = glCheckFramebufferStatus ( GL_FRAMEBUFFER );
if ( Status != GL_FRAMEBUFFER_COMPLETE )
    {
    Delete();
    return false;
    }

Dimensions = In_Dimensions;

// Unbind
glBindFramebuffer ( GL_FRAMEBUFFER, 0 );

这是正确的方法吗?我还需要编写对应的着色器...


第二个版本完全没有意义,因为它覆盖了渲染缓冲的绑定。 - BDL
2个回答

9
“创建纹理,渲染缓冲区,然后将一个分配给另一个的意义是什么?” 答:实际上并不是这样的。但没关系,因为第二个代码示例是错误的荒谬的。 'glFramebufferTexture2DEXT' 覆盖了 'glFramebufferRenderbufferEXT' 的绑定。渲染缓冲区在创建后实际上从未被使用过。
“我读到过不能从渲染缓冲区中读取,只能从纹理中读取。那么它的用途是什么?” 答:这完全是它们存在的原因:您使用渲染缓冲区来存储您不需要读取的图像。这对于延迟渲染来说没有用,因为您确实希望从它们中读取。
但是想象一下,如果您正在生成场景的反射图像,并且稍后将其用作主要场景中的纹理呢?好吧,在渲染反射场景时,您需要一个深度缓冲区。但是您不会从该深度缓冲区中读取(无论如何都不会以纹理的形式),您需要深度缓冲区进行深度测试。但是,之后您将要从中读取的唯一图像就是颜色图像。
因此,您可以将深度缓冲区设置为渲染缓冲区。这告诉实现可以将图像放入最适合用作深度缓冲区的存储中,而无需担心读回性能。这可能会或可能不会对性能产生影响。但是,至少它不会比使用纹理慢。

好的。我要试一试。这些代码示例在几个不同的地方被发现,所以我对所有这些感到非常困惑。 - Joao Pincho

2
大多数渲染场景需要深度和/或模板缓冲区,尽管你很少需要从着色器中对存储在模板缓冲区中的数据进行采样。
如果你的帧缓冲区没有位置来存储这些数据,那么深度/模板测试就是不可能的,任何使用这些片段测试的渲染通道都需要附加适当图像的帧缓冲区。
如果你不打算在着色器中使用深度/模板缓冲区数据,那么渲染缓冲区将满足固定功能片段测试的存储需求。相比纹理,渲染缓冲区的格式限制更少,特别是在多重采样方面。
D3D10引入了对多重采样颜色纹理的支持,但省略了多重采样深度纹理;D3D10.1之后解决了这个问题,GL3.0在D3D10的初始设计失误得到修正后才最终确定。

在GL3 / D3D10.1之前的设计将在GL中表现为一个多重采样帧缓对象,允许使用纹理或渲染缓存作为颜色附件,但强制使用渲染缓存作为深度附件。


渲染缓冲区最终是存储的最低公共分母,它们可以帮助您在功能受限的硬件上度过难关。在某些情况下,您实际上可以将渲染缓冲区中存储的数据转移到纹理中,而在直接绘制到纹理不可行的情况下。
为此,您可以通过从一个帧缓冲区向另一个帧缓冲区转移来将多重采样渲染缓冲区解析为单一采样纹理。这是隐式多重采样,它(将)允许您使用先前渲染通道的反锯齿结果和标准纹理查找。不幸的是,它对于延迟着色中的抗锯齿完全没有用处--您需要显式多重采样解析才能进行抗锯齿处理。 尽管如此,说渲染缓冲区不可读是不正确的;从每个方面来说,它都是可读的,但由于您的目标是延迟着色,因此需要额外的GL命令将数据复制到纹理中。

我强烈怀疑您在第二个示例中尝试做/展示的实际上是帧缓冲之间的 blit。简单地用不同类型的图像替换原始图像并不能做任何事情。 - Andon M. Coleman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接