幀緩衝的基礎知識

Framebuffer 是一種緩衝區,用於儲存記憶體中畫素的顏色值,深度模板資訊。當你在 OpenGL 中繪製某些內容時,輸出將儲存在預設的幀緩衝區中,然後你實際上會在螢幕上看到此緩衝區的顏色值。你也可以製作自己的幀緩衝,可用於很多很酷的後期處理效果,如灰度,模糊,景深,失真,反射 ……

首先,你需要建立一個 framebuffer 物件( FBO )並像 OpenGL 中的任何其他物件一樣繫結它:

unsigned int FBO;
glGenFramebuffers(1, &FBO);
glBindFramebuffer(GL_FRAMEBUFFER, FBO);  

現在,你必須向幀緩衝區新增至少一個附件 (顏色,深度或模板)。附件是一個記憶體位置,充當幀緩衝區的緩衝區。它可以是紋理渲染緩衝物件。使用紋理的優點是你可以在後處理著色器中輕鬆使用此紋理。建立紋理與普通紋理類似:

unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, 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); 

widthheight 應與渲染視窗大小相同。紋理資料指標是 NULL,因為你只想分配記憶體而不用任何資料填充紋理。紋理已準備就緒,因此你可以將其實際附加到幀緩衝區:

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);  

你的幀緩衝區現在應該可以使用了,但你可能還想新增深度附件或深度和模板附件。如果你想將它們新增為紋理附件(並將它們用於某些處理),你可以建立上面的其他紋理。唯一的區別在於這些方面:

glTexImage2D(
    GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0, 
    GL_DEPTH_COMPONENT, GL_FLOAT, NULL
);

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture, 0);

或者如果你想在單個紋理中使用深度模板附件:

glTexImage2D(
    GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0, 
    GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL
);

glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texture, 0);

如果以後不想處理值,也可以使用 renderbuffer 而不是紋理作為深度和模板緩衝區的附件。 (將在另一個例子中解釋……)

你可以檢查幀緩衝是否已成功建立並完成且沒有任何錯誤:

if(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
    // do something...

最後不要忘記取消繫結幀緩衝區,以免意外渲染到它:

glBindFramebuffer(GL_FRAMEBUFFER, 0);  

範圍

通過使用引數 GL_MAX_COLOR_ATTACHMENTS,可以通過 OGL 函式 glGetIntegerv 確定可附加到單個幀緩衝區的最大顏色緩衝區數 :

GLint maxColAttchments = 0;
glGetIntegerv( GL_MAX_COLOR_ATTACHMENTS, &maxColAttchments );

使用幀緩衝

用法非常簡單。首先繫結幀緩衝區並將場景渲染到其中。但是你實際上還沒有看到任何東西,因為你的渲染緩衝區是不可見的。所以第二部分是將幀緩衝區渲染為全屏四邊形的紋理到螢幕上。你可以按原樣渲染它或執行一些後處理效果。

以下是全屏四邊形的頂點:

float vertices[] = {
//   positions     texture coordinates
    -1.0f,  1.0f,  0.0f, 1.0f,
    -1.0f, -1.0f,  0.0f, 0.0f,
     1.0f, -1.0f,  1.0f, 0.0f,

    -1.0f,  1.0f,  0.0f, 1.0f,
     1.0f, -1.0f,  1.0f, 0.0f,
     1.0f,  1.0f,  1.0f, 1.0f
};

你需要將它們儲存在 VBO 中或使用屬性指標進行渲染。你還需要一些基本著色器程式來渲染具有紋理的全屏四邊形。

頂點著色器:

in vec2 position;
in vec2 texCoords;

out vec2 TexCoords;

void main()
{
    gl_Position = vec4(position.x, position.y, 0.0, 1.0); 
    TexCoords = texCoords;
}  

片段著色器:

in vec2 TexCoords;
out vec4 color;

uniform sampler2D screenTexture;

void main()
{ 
    color = texture(screenTexture, TexCoords);
}

注意: 你可能需要調整你的 GLSL 版本的著色器。

現在你可以進行實際渲染。如上所述,首先是將場景渲染到你的 FBO 中。要做到這一點,你只需繫結你的 FBO,清除它並繪製場景:

glBindFramebuffer(GL_FRAMEBUFFER, FBO);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
// draw your scene here...   

注意:glClear 功能中,你應指定你正在使用的所有幀緩衝附件(在此示例中為顏色和深度附件)。

現在,你可以將 FBO 渲染為預設幀緩衝區上的全屏四邊形,以便你可以看到它。要做到這一點,你只需取消繫結你的 FBO 並渲染四邊形:

glBindFramebuffer(GL_FRAMEBUFFER, 0); // unbind your FBO to set the default framebuffer
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

shader.Use(); // shader program for rendering the quad  

glBindTexture(GL_TEXTURE_2D, texture); // color attachment texture
glBindBuffer(GL_ARRAY_BUFFER, VBO); // VBO of the quad
// You can also use VAO or attribute pointers instead of only VBO...
glDrawArrays(GL_TRIANGLES, 0, 6); 
glBindBuffer(GL_ARRAY_BUFFER, 0);

就這樣! 如果你已正確完成所有操作,則應該看到與之前相同的場景,但在全屏四邊形上渲染。視覺輸出與以前相同,但現在只需編輯片段著色器即可輕鬆新增後處理效果。 (我將在另一個例子中新增效果並在此處連結)