處理opengl使用fbo貼圖時產生的鋸齒
阿新 • • 發佈:2019-01-08
背景描述:
在使用artools的矩陣進行貼圖的時候,發現在我自己的濾鏡裡貼圖會有明顯鋸齒。
學習了一下,這種鋸齒被稱作aliasing,反鋸齒被稱作anti-aliasing。
比較常用的方法是MSAA(Multisample Anti- Aliasing),該方法已經在opengl裡實現了,對於使用視窗的opengl程式來說,僅僅需要設定sample的數目,然後glEnable(GL_MULTISAMPLE)。而對於我使用離屏渲染fbo的情況來說,必須自己處理。
網上常見的方法大多使用的是renderbuffer來處理。我目前呼叫的流程不需要renderbuffer,我的流程僅僅需要opengl把圖draw到一個fbo裡,然後我通過glReadPixels讀取內容到cpu memory。最後我僅僅增加了一個multisample fbo和一個multisample texture來處理。粗略流程就是:讓draw的目的fbo繫結使用multi sample的texture,然後再把裡面的內容拷貝到普通的fbo用於輸出。
下面簡單解釋一下流程:
首先,初始化的時候標明使用multi sample,以及samples的個數。我這裡是通過離屏渲染,所以對應的是glXChooseFBConfig初始化的引數:
在 glewInit() 之後,我們可以通過下面的方法檢查是否設定生效了:static int visualAttribs[] = { GLX_SAMPLE_BUFFERS, 1, GLX_SAMPLES , 4 }; /* GLX Framebuffer Config */ fbConfigs = glXChooseFBConfig(s->display, DefaultScreen(s->display), visualAttribs, &numberOfFramebufferConfigurations);
GLint bufs, samplesaa;
glGetIntegerv(GL_SAMPLE_BUFFERS, &bufs);
glGetIntegerv(GL_SAMPLES, &samplesaa);
之後,設定使用multi sample
glEnable(GL_MULTISAMPLE);
接下來,我們需要建立multi sample的texture,用於多采樣。注意GL_TEXTURE_2D_MULTISAMPLE後面的引數4對應的是samples的個數,越多效果越好,速度越慢
然後建立fbo,繫結這個multi sample的texture。這裡和普通fbo繫結的區別是顯示宣告GL_TEXTURE_2D_MULTISAMPLEglGenTextures(3, p_fbo->tex_serial); for (int i=0; i<s->planes; i++) { int k = (!!i); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, p_fbo->tex_serial[i]); glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RED, p_fbo->width[k], p_fbo->height[k], GL_TRUE); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); }
glGenFramebuffers(3, s->fbo_render);
for (int i=0; i<s->planes; i++)
{
glBindFramebuffer(GL_FRAMEBUFFER, s->fbo_render[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, p_fbo->tex_serial[i], 0);
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
av_log(NULL, AV_LOG_ERROR, "Error(in %s): Couldn't create frame buffer", __FUNCTION__);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
接下來建立普通的texture和fbo,用於輸出(readpixel)資料
glGenTextures(3, p_fbo->tex_second);
for (int i=0; i<s->planes; i++)
{
int k = (!!i);
glBindTexture(GL_TEXTURE_2D, p_fbo->tex_second[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, p_fbo->width[k], p_fbo->height[k], 0, GL_RED, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_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);
}
glGenFramebuffers(3, s->fbo_second);
for (int i=0; i<s->planes; i++)
{
glBindFramebuffer(GL_FRAMEBUFFER, s->fbo_second[i]);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_fbo->tex_second[i], 0);
status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
av_log(NULL, AV_LOG_ERROR, "Error(in %s): Couldn't create frame buffer", __FUNCTION__);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
在畫圖的時候,我們首先繫結multi sample的fbo:fbo_render
glBindFramebuffer(GL_FRAMEBUFFER, s->fbo_render[i]);
glUseProgram(p_fbo->prg[k]->po);
/* source */
glActiveTexture(GL_TEXTURE0+i);
glBindTexture(GL_TEXTURE_2D, s->sub_texture[i]);
glUniform1i(p_fbo->prg[k]->texUniform, i);
glUniformMatrix4fv(p_fbo->prg[k]->posMVPMatrix, 1, GL_FALSE, (const GLfloat *)m2);
/* destination */
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glViewport(0, 0, p_fbo->width[k], p_fbo->height[k]);
/* draw to FBO */
glBindVertexArray(p_fbo->vao[k]);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_fbo->index_buffer[k]);
glDrawElements(GL_TRIANGLES, p_fbo->indices_count[k], GL_UNSIGNED_INT, NULL);
在draw完成之後,我們把裡面的內容考到普通的fbo:
// copy framebuffer from multi sample fbo to normal fbo
glBindFramebuffer(GL_READ_FRAMEBUFFER, s->fbo_render[i]);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, s->fbo_second[i]);
glBlitFramebuffer(0, 0, p_fbo->width[k], p_fbo->height[k], 0, 0, p_fbo->width[k], p_fbo->height[k], GL_COLOR_BUFFER_BIT, GL_NEAREST);
最後,我們從第二個fbo裡讀取資料給cpu的memory
for (int i=0; i<s->planes; i++)
{
GLubyte* src;
int k = (!!i);
glBindFramebuffer(GL_FRAMEBUFFER, s->fbo_second[i]);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glPixelStorei(GL_PACK_ALIGNMENT, 4); // TODO
glPixelStorei(GL_PACK_IMAGE_HEIGHT, p_fbo->height[k]); // image height in client memory
glPixelStorei(GL_PACK_ROW_LENGTH, out->linesize[i]); // pitch in client memory
glBindBuffer(GL_PIXEL_PACK_BUFFER, s->pbo_down[i]);
glReadPixels(0, 0, p_fbo->width[k], p_fbo->height[k], GL_RED, GL_UNSIGNED_BYTE, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
//glBindBuffer(GL_PIXEL_PACK_BUFFER, s->pbo_down[i]);
src = (GLubyte*)glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
av_assert0(src); // TODO
av_assert0(out->data[i]);
memcpy(out->data[i], src, out->linesize[i]*p_fbo->height[k]);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
}
下面是對比圖,第一張是沒有使用MSAA的貼圖,第二張是使用sample=4的貼圖。很明顯邊緣效果好很多。
更多更詳細的原理內容以及使用renderbuffer的解決方法請參考這些帖子:
http://www.tuicool.com/articles/I3Qfmmb
http://www.cnblogs.com/waytofall/p/3830527.html