1. 程式人生 > >【複習筆記】 cocos2d-x 2.x 渲染特效實現 五 節點樹的模糊效果

【複習筆記】 cocos2d-x 2.x 渲染特效實現 五 節點樹的模糊效果

到目前為止,我們實現的模糊都是在單張貼圖上的,只是針對當前這一張貼圖對其模糊,因而當圖片中的內容就在圖片邊緣的時候,其模糊在圖片邊緣是被“截斷”的,因此,我們需要一個比原貼圖大,但又包含原本顏色資訊的圖來做模糊。另外,有時候我們需要的模糊並不只是在一張貼圖上,可能當某個單位需要被模糊時,它是由諸多節點和諸多貼圖經過各自的變換組合出來的。因此,我們需要擴大到對一個節點進行模糊,這樣,上述的兩個問題就都可以解決了~

在上一篇中,我們為了對貼圖做縱向的二次高斯模糊處理,使用了FBO技術。運用同樣的思路,我們這次使用該技術,先得到一張整個節點樹渲染之後的貼圖,之後再對該貼圖做模糊,便可得到整個節點樹模糊之後的效果啦~當然,為了更好的渲染效率考慮,針對節點樹的模糊就用簡單模糊即可,否則,如果用高斯模糊,就要先做兩次FBO渲染,總共三次渲染才可以得到最終的效果。

在cocos中,visit()中會對節點做基於父節點的變換,然後在其中呼叫draw()渲染,所以我們需要在整個節點visit前,就讓該節點樹原原本本的渲染到我們建立的貼圖中,而不是直接渲染到系統的螢幕緩衝區。所以我們要重寫visit函式。因為自身節點的渲染也是其中的一部分,所以,draw()函式不需要重寫,繼續使用CCSprite的draw即可~

重寫後的visit函式如下:

void CCEffectSprite::visit()
{
	float blursDis[2] = {_pixelSpan/_screenBufferSize.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);
	if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
		CCLOG("glCheckFramebufferStatus ERROR! 0x%04x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
	}
	
    
    // ************ first draw ******************
	CCNode::visit();
    // ******************************************
    
    
    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);
	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, _texture);
    ccGLEnableVertexAttribs(kCCVertexAttribFlag_PosColorTex);

	// uniforms
	glUniform2f(_uniforms[kUniformBlurDis], blursDis[0], blursDis[1]);
	
    // attributes
	ccV3F_C4B_T2F_Quad tempQuad;
	memset(&tempQuad, 0, sizeof(tempQuad));
	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);
	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);
	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);
}
該函式中,_pixelSpan = 2.0f,即取樣間隔為2個畫素。渲染節點的時候直接呼叫CCNode的visit函式,原封不動的進行之前就要做的渲染操作,以確保渲然到紋理之後,結果完全相同。可見CCNode::visit()做了一次原本就要做的節點樹渲染操作,而得到渲染好的紋理之後,只是再多做了一次模糊的渲染操作。所以,只比之前正常渲染多做了一次渲染,就得到了整個節點樹的模糊渲染效果,價效比還是很高的~

除了原本該節點的一個貼圖(吉他)之外,給該節點新增兩個子節點(四葉草和蘋果)之後的效果圖如下: