幀緩衝的基礎知識
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);
width
和 height
應與渲染視窗大小相同。紋理資料指標是 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);
就這樣! 如果你已正確完成所有操作,則應該看到與之前相同的場景,但在全屏四邊形上渲染。視覺輸出與以前相同,但現在只需編輯片段著色器即可輕鬆新增後處理效果。 (我將在另一個例子中新增效果並在此處連結)