【复习笔记】 cocos2d-x 2.x 渲染特效实现 四 高斯模糊效果二

来源:互联网 发布:js object 循环 编辑:程序博客网 时间:2024/05/13 01:19

在上文末,我们已经完成了高斯模糊横向上的模糊效果,而且提到,纵向模糊要在横向模糊的结果上进行,所以为了得到横向模糊的贴图,我们使用离屏渲染。顾名思义,现在渲染的目的地不是屏幕了~一般状况下,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 stackkmGLMatrixMode(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);// uniformsglUniform2f(_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 stackkmGLMatrixMode(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变量分别用原贴图的宽和生成贴图的高做了计算。

最终效果:

可以看到,相比于之前简单采样的模糊,高斯模糊有更好的模糊效果~

0 0