1. 程式人生 > >【複習筆記】 cocos2d-x 2.x 渲染特效實現 四 高斯模糊效果二

【複習筆記】 cocos2d-x 2.x 渲染特效實現 四 高斯模糊效果二

在上文末,我們已經完成了高斯模糊橫向上的模糊效果,而且提到,縱向模糊要在橫向模糊的結果上進行,所以為了得到橫向模糊的貼圖,我們使用離屏渲染。顧名思義,現在渲染的目的地不是螢幕了~一般狀況下,gl是直接把渲染好的紋理繪製到螢幕緩衝區的,進而直接顯示在螢幕上。但是現在,我們要把它繪製到一張紋理中,這樣,就可以像操作一張普通的紋理一樣,對它進行再次加工了~這裡我們用到FBO技術。

FBO即Frame Buffer Object,幀快取物件,這使得我們可以建立一個由gl控制的幀快取。通常情況下,我們只繪製到作業系統提供的幀快取。

渲染到紋理的大致步驟如下:

1.建立一個空的texture

2.建立FBO

3.繫結texture到FBO上

4.使用自己建立的FBO作為當前渲染的幀快取

4.draw~

具體程式碼如下:

void CCEffectSprite::drawGaussianBlur()
{
	
	CCEGLView *eglView = CCDirector::sharedDirector()->getOpenGLView();
	float zoomScale = eglView->getFrameZoomFactor();
	CCSize frameSize = eglView->getFrameSize();
	CCSize screenBufferSize = CCSizeMake(frameSize.width*zoomScale, frameSize.height*zoomScale);
	//CCLOG("frameSize : %f, %f", frameSize.width, frameSize.height);

	float pixelSpan = 0.5f;
	float blursDis[2] = {pixelSpan/getTextureRect().size.width, pixelSpan/screenBufferSize.height};
	GLint size = sizeof(ccV3F_C4B_T2F);
    long data = (long)&m_sQuad;

	/**/
    // gen a empty texture
    glGenTextures(1, &_texture);
    glBindTexture(GL_TEXTURE_2D, _texture);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, screenBufferSize.width, screenBufferSize.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);
    
    // gen a off-screen framebuffer
    GLint oldFBO;
    glGetIntegerv(GL_RENDERBUFFER_BINDING, &oldFBO);
    glGenFramebuffers(1, &_framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
	
    
    // ************ first draw ******************
    CC_NODE_DRAW_SETUP();
    ccGLBlendFunc(m_sBlendFunc.src, m_sBlendFunc.dst);
    // ccGLBindTexture2D(m_pobTexture->getName());
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, m_pobTexture->getName());
    ccGLEnableVertexAttribs(kCCVertexAttribFlag_PosColorTex);
    
    // uniforms
    glUniform2f(_uniforms[kUniformBlurDis], blursDis[0], 0.00f);
    
    // attributes
    glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, size, (GLvoid *)data);
    glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, size, (GLvoid *)(data + sizeof(ccVertex3F)));
    glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, size, (GLvoid *)(data + sizeof(ccVertex3F) + sizeof(ccColor4B)));
    
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    // ******************************************
    
    
    glBindFramebuffer(GL_FRAMEBUFFER, oldFBO);
    
    
    // ************** second draw *****************
	// reset stack
	kmGLMatrixMode(KM_GL_MODELVIEW);
	kmGLPushMatrix();
	kmGLLoadIdentity();
	kmGLMatrixMode(KM_GL_PROJECTION);
	kmGLPushMatrix();
	kmGLLoadIdentity();
	glViewport(0, 0, screenBufferSize.width, screenBufferSize.height);
	
	CC_NODE_DRAW_SETUP();
	ccGLBlendFunc(m_sBlendFunc.src, m_sBlendFunc.dst);
	// ccGLBindTexture2D(m_pobTexture->getName());
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, _texture);
    ccGLEnableVertexAttribs(kCCVertexAttribFlag_PosColorTex);

	// uniforms
	glUniform2f(_uniforms[kUniformBlurDis], 0.00f, blursDis[1]);
	
    // attributes
	// do not add any other render info, just do another direction blur:
	ccV3F_C4B_T2F_Quad tempQuad;
	memset(&tempQuad, 0, sizeof(tempQuad));
	// vertices are located in the 4 corners of the whole viewport.
	tempQuad.tl.vertices = vertex3(-1, 1, -1);
	tempQuad.bl.vertices = vertex3(-1, -1, -1);
	tempQuad.tr.vertices = vertex3(1, 1, -1);
	tempQuad.br.vertices = vertex3(1, -1, -1);
	// color is black which won't change any color info.
	tempQuad.tl.colors = ccc4(255, 255, 255, 255);
	tempQuad.bl.colors = ccc4(255, 255, 255, 255);
	tempQuad.tr.colors = ccc4(255, 255, 255, 255);
	tempQuad.br.colors = ccc4(255, 255, 255, 255);
	// UV y-coords is also fliped just like what CCSprite did in above step.
	tempQuad.tl.texCoords = tex2(0, 1);
	tempQuad.bl.texCoords = tex2(0, 0);
	tempQuad.tr.texCoords = tex2(1, 1);
	tempQuad.br.texCoords = tex2(1, 0);
	data = (long)&tempQuad;
    glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, size, (GLvoid *)data);
    glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, size, (GLvoid *)(data + sizeof(ccVertex3F)));
    glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, size, (GLvoid *)(data + sizeof(ccVertex3F) + sizeof(ccColor4B)));
    
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

	// restore stack
	kmGLMatrixMode(KM_GL_PROJECTION);
	kmGLPopMatrix();
	kmGLMatrixMode(KM_GL_MODELVIEW);
	kmGLPopMatrix();
	CCDirector::sharedDirector()->setViewport();
    // **************************************
    
	/**/
    glDeleteTextures(1, &_texture);
    glDeleteFramebuffers(1, &_framebuffer);
	
}

上述程式碼中值得一提的幾點是:

1.由於我們自己建立的幀快取是為了模擬另一個“螢幕”而存在的,所以FBO中繫結的texture和viewport都使用了系統幀快取的大小,所以當第一次渲染結束後,可以得到一張和螢幕大小相同的貼圖,且和直接渲染的到螢幕之後,與螢幕上顯示的畫素資訊完全相同。在第二次渲染中,因為原貼圖大小已經變化,所以不能使用正常渲染的MVP變換,而是直接把與螢幕大小相同的貼圖直接繪製出來即可。在cocos中,使用了第三方的3D數學庫kazmath來儲存各級節點的變換矩陣,所以,在第二次渲染的時候,在MV矩陣和P矩陣棧上都壓入一個單位矩陣,來保證不做任何變換,且紋理座標也根據cocos的習慣,在縱向上做了翻轉。

2.因為兩次渲染的貼圖大小是不一樣的,為了橫向和縱向都用相同的取樣標準去做高斯模糊,blurDis變數分別用原貼圖的寬和生成貼圖的高做了計算。

最終效果:

可以看到,相比於之前簡單取樣的模糊,高斯模糊有更好的模糊效果~