暂停时快速出现高斯模糊 [英] Fast Gaussian blur at pause

查看:148
本文介绍了暂停时快速出现高斯模糊的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

cocos2d-x 中,我需要实现快速的高斯模糊,这是它的外观(我刚刚在App Store上找到了一些已经做到了这种模糊的游戏,以统一的方式):

In cocos2d-x I need to implement fast gaussian blur and here is how it should looks like( I just found some game on the App Store with already done such blur, in unity):

因此,当用户暂停游戏时,淡入-淡出会很不错.

So, it's nice fadeIn-fadeOut blur when user pauses the game.

GPUImage已经具有我需要的快速模糊处理,但是我找不到针对cocos2d-x的解决方案.

GPUImage already has a fast blur I need, but I can't find solution for cocos2d-x.

v2代码何时为现在 斯威夫特(GPUImage v2)斯威夫特

v2 code when is now Swift(GPUImage v2) Swift

这是使用GPUImage2进行实时摄像机视图的结果-在iPod上进行了测试触摸5G,它可以在这台缓慢而又旧的设备上快速运行.

Here is result of live camera view using GPUImage2 - tested on iPod Touch 5G, and it works fast on this slow and old device.

即使在速度非常慢的设备(如iPod Touch 5G)上,GPUImage中的模糊效果也非常快.
寻找针对cocos2d-x的超快速高斯模糊解决方案.

Blur in GPUImage works very fast even on very slow devices like iPod Touch 5G.
Looking for solution with super fast Gaussian blur for cocos2d-x.

推荐答案

学习了"RENDERTEXTURE + BLUR" ,我提出了以下解决方案.

After studying "Post-Processing Effects in Cocos2d-X" and "RENDERTEXTURE + BLUR", I came along to the following solution.

在Cocos2s-X中实现后处理效果的常用方法是实现图层.场景是一层,后期处理是另一层,它使用场景层作为输入.使用此技术,后期处理可以操纵渲染的场景.

The common way to achieve post processing effects in Cocos2s-X is to implement layers. The scene is one layer, and a post process is another layer, which uses the scene layer as an input. With this technique, the post process can manipulate the rendered scene.

模糊算法是在着色器中实现的.在场景上应用模糊效果的常见方法是先沿视口的X轴模糊,然后沿视口Y轴第二遍(请参见

The blur algorithm is implemented in a shader. A common way to apply a blur effect on a scene is to blur first along he X-axis of the viewport and in an second pass along the Y-axis of the viewport (see ShaderLesson5). This is an acceptable approximations, which gives a massive gain of performance.

这意味着,在Cocos2s-X中我们需要2个后期处理层.因此,我们需要3层,一层用于场景,两层用于后期处理:

This means, that we need 2 post process layers in Cocos2s-X. So wee need 3 layers, one for the scene and 2 for the post processes:

// scene (game) layer
m_gameLayer = Layer::create();
this->addChild(m_gameLayer, 0);

// blur X layer
m_blurX_PostProcessLayer = PostProcess::create("shader/blur.vert", "shader/blur.frag");
m_blurX_PostProcessLayer->setAnchorPoint(Point::ZERO);
m_blurX_PostProcessLayer->setPosition(Point::ZERO);
this->addChild(m_blurX_PostProcessLayer, 1);

// blur y layer
m_blurY_PostProcessLayer = PostProcess::create("shader/blur.vert", "shader/blur.frag");
m_blurY_PostProcessLayer->setAnchorPoint(Point::ZERO);
m_blurY_PostProcessLayer->setPosition(Point::ZERO);
this->addChild(m_blurY_PostProcessLayer, 2);

请注意,必须将场景的精灵和资源添加到m_gameLayer.

Note, the sprites and resources of the scene have to be added to m_gameLayer.

updated方法中,后期处理必须应用到场景中(稍后我将描述制服的设置):

In the updated methode, the post processes have to be apllied to the scene (I'll describe the setup of the uniforms later):

// blur in X direction

cocos2d::GLProgramState &blurXstate = m_blurX_PostProcessLayer->ProgramState();
blurXstate.setUniformVec2( "u_blurOffset", Vec2( 1.0f/visibleSize.width, 0.0 ) ); 
blurXstate.setUniformFloat( "u_blurStrength", (float)blurStrength );

m_blurX_PostProcessLayer->draw(m_gameLayer);

// blur in Y direction

cocos2d::GLProgramState &blurYstate = m_blurY_PostProcessLayer->ProgramState();
blurYstate.setUniformVec2( "u_blurOffset", Vec2( 0.0, 1.0f/visibleSize.height ) );
blurYstate.setUniformFloat( "u_blurStrength", (float)blurStrength );

m_blurY_PostProcessLayer->draw(m_blurX_PostProcessLayer);


为了管理后期流程,我实现了一个类PostProcess,在其中我尝试使事情尽可能简单:


For the management of the post process I implemented a class PostProcess, where I tried to keep things as simple as possible:

PostProcess.hpp

#include <string>
#include "cocos2d.h"

class PostProcess : public cocos2d::Layer
{
private:
    PostProcess(void) {}
    virtual ~PostProcess() {}
public:
    static PostProcess* create(const std::string& vertexShaderFile, const std::string& fragmentShaderFile);
    virtual bool init(const std::string& vertexShaderFile, const std::string& fragmentShaderFile);
    void draw(cocos2d::Layer* layer);
    cocos2d::GLProgram      & Program( void )      { return *_program; }
    cocos2d::GLProgramState & ProgramState( void ) { return *_progState; }
private:
    cocos2d::GLProgram       *_program;
    cocos2d::GLProgramState  *_progState;
    cocos2d::RenderTexture   *_renderTexture;
    cocos2d::Sprite          *_sprite;
};

PostProcess.cpp

#include "PostProcess.hpp"

using namespace cocos2d;

bool PostProcess::init(const std::string& vertexShaderFile, const std::string& fragmentShaderFile)
{
    if (!Layer::init()) {
        return false;
    }

    _program = GLProgram::createWithFilenames(vertexShaderFile, fragmentShaderFile);
    _program->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_COLOR, GLProgram::VERTEX_ATTRIB_POSITION);
    _program->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_POSITION, GLProgram::VERTEX_ATTRIB_COLOR);
    _program->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD, GLProgram::VERTEX_ATTRIB_TEX_COORD);
    _program->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD1, GLProgram::VERTEX_ATTRIB_TEX_COORD1);
    _program->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD2, GLProgram::VERTEX_ATTRIB_TEX_COORD2);
    _program->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_TEX_COORD3, GLProgram::VERTEX_ATTRIB_TEX_COORD3);
    _program->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_NORMAL, GLProgram::VERTEX_ATTRIB_NORMAL);
    _program->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_BLEND_WEIGHT, GLProgram::VERTEX_ATTRIB_BLEND_WEIGHT);
    _program->bindAttribLocation(GLProgram::ATTRIBUTE_NAME_BLEND_INDEX, GLProgram::VERTEX_ATTRIB_BLEND_INDEX);
    _program->link();

    _progState = GLProgramState::getOrCreateWithGLProgram(_program);

    _program->updateUniforms();

    auto visibleSize = Director::getInstance()->getVisibleSize();

    _renderTexture = RenderTexture::create(visibleSize.width, visibleSize.height);
    _renderTexture->retain();

    _sprite = Sprite::createWithTexture(_renderTexture->getSprite()->getTexture());
    _sprite->setTextureRect(Rect(0, 0, _sprite->getTexture()->getContentSize().width,
    _sprite->getTexture()->getContentSize().height));
    _sprite->setAnchorPoint(Point::ZERO);
    _sprite->setPosition(Point::ZERO);
    _sprite->setFlippedY(true);
    _sprite->setGLProgram(_program);
    _sprite->setGLProgramState(_progState);
    this->addChild(_sprite);

    return true;
}

void PostProcess::draw(cocos2d::Layer* layer)
{
    _renderTexture->beginWithClear(0.0f, 0.0f, 0.0f, 0.0f);
    layer->visit();
    _renderTexture->end();
}

PostProcess* PostProcess::create(const std::string& vertexShaderFile, const std::string& fragmentShaderFile)
{
    auto p = new (std::nothrow) PostProcess();
    if (p && p->init(vertexShaderFile, fragmentShaderFile)) {
        p->autorelease();
        return p;
    }
    delete p;
    return nullptr;
}


着色器需要一个unifor,其中包含模糊算法(u_blurOffset)的偏移量.这是第一次模糊通过的沿X轴的2个像素之间的距离,以及第二次模糊通过的沿Y轴的2个像素之间的距离.
模糊效果的强度由均匀变量(u_blurStrength)设置.其中0.0表示模糊关闭,而1.0表示最大模糊.最大模糊效果由值MAX_BLUR_WIDHT定义,该值定义在每个方向上观察的纹理像素的范围.因此,这或多或少是模糊半径.如果增加该值,则模糊效果会增加,这会降低性能.如果减小该值,则模糊效果将减小,但是您将赢得性能.性能与MAX_BLUR_WIDHT的值之间的关系值得庆幸的是呈线性关系(而不是二次关系),因为实现了大约2遍. 我决定避免预先计算高斯权重并将其传递给着色器(高斯权重取决于MAX_BLUR_WIDHTu_blurStrength).相反,我使用了平滑的
Hermite内插,类似于GLSL函数


The shader needs a unifor which contains the offset for the blur algorithm (u_blurOffset). This is the distance between 2 pixels along the X-axis for the first blur pass and the distance between 2 texels along the Y-axis for the second blur pass.
The strength of the blur effect is setup by the uniform variable (u_blurStrength). Where 0.0 means that blurring is off and 1.0 means maximum blurring. The maximum blur effect is defined by the value of MAX_BLUR_WIDHT, which defines the range of the texels wich are looked on in each direction. So this is more or less the blur radius. If you increase the value, the blur effect will increase, at the disadvantage of a loss of performance. If you decrease the value the blur effect will decrease, but you will winn performance. The relation between performance and the value of MAX_BLUR_WIDHT is thankfully linear (and not quadratic), because of the approximated 2 pass implementation.
I decided to avoid pre calculating gauss weights and passing them to the shader (the gauss weights would depend on MAX_BLUR_WIDHT and u_blurStrength). Instead I used a smooth Hermite interpolation similar to the GLSL function smoothstep:

blur.vert

attribute vec4 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_color;

varying vec4 v_fragmentColor;
varying vec2 v_texCoord;

void main()
{
    gl_Position     = CC_MVPMatrix * a_position;
    v_fragmentColor = a_color;
    v_texCoord      = a_texCoord;
}

blur.frag

varying vec4 v_fragmentColor;
varying vec2 v_texCoord;

uniform vec2  u_blurOffset;
uniform float u_blurStrength;

#define MAX_BLUR_WIDHT 10

void main()
{
    vec4 color   = texture2D(CC_Texture0, v_texCoord);

    float blurWidth = u_blurStrength * float(MAX_BLUR_WIDHT);
    vec4 blurColor  = vec4(color.rgb, 1.0);
    for (int i = 1; i <= MAX_BLUR_WIDHT; ++ i)
    {
        if ( float(i) >= blurWidth )
            break;

        float weight = 1.0 - float(i) / blurWidth;
        weight = weight * weight * (3.0 - 2.0 * weight); // smoothstep

        vec4 sampleColor1 = texture2D(CC_Texture0, v_texCoord + u_blurOffset * float(i));
        vec4 sampleColor2 = texture2D(CC_Texture0, v_texCoord - u_blurOffset * float(i));
        blurColor += vec4(sampleColor1.rgb + sampleColor2.rgb, 2.0) * weight; 
    }

    gl_FragColor = vec4(blurColor.rgb / blurColor.w, color.a);
}


完整的C ++和GLSL源代码可以在
GitHub (可以通过bool HelloWorld::m_blurFast = false激活该实现).


The full C++ and GLSL source code can be found on GitHub (The implementation can be activated by bool HelloWorld::m_blurFast = false).

查看预览:

See the preview:


高斯模糊算法的高性能版本是

A high performance version of an gaussian blur algorithm is the solution presented at GPUImage-x. In this implementation a separated blur shader for each blur radius is created. The source code of the full cocos2d-x demo implementation can be found at GitHub.The implementation provides 2 variants, the standard implementation and the optimized implementation, like the implementation in the link, which can be set up by bool GPUimageBlur::m_optimized. The implementation generates a shader for each radius from 0 to int GPUimageBlur::m_maxRadius and a sigma float GPUimageBlur::m_sigma.

查看预览:

See the preview:


一种更强大的解决方案,但具有明显非常低的质量,将使用

A much more powerful solution, but with obvious very low quality, would be to use the shader presented at Optimizing Gaussian blurs on a mobile GPU. The blurring is not dynamic and can only be switched on or off:

update方法:

// blur pass 1
cocos2d::GLProgramState &blurPass1state = m_blurPass1_PostProcessLayer->ProgramState();
blurPass1state.setUniformVec2( "u_blurOffset", Vec2( blurStrength/visibleSize.width, blurStrength/visibleSize.height ) );
m_gameLayer->setVisible( true );
m_blurPass1_PostProcessLayer->draw(m_gameLayer);
m_gameLayer->setVisible( false );

// blur pass 2
cocos2d::GLProgramState &blurPass2state = m_blurPass2_PostProcessLayer->ProgramState();
blurPass2state.setUniformVec2( "u_blurOffset", Vec2( blurStrength/visibleSize.width, -blurStrength/visibleSize.height ) );
m_blurPass1_PostProcessLayer->setVisible( true );
m_blurPass2_PostProcessLayer->draw(m_blurPass1_PostProcessLayer);
m_blurPass1_PostProcessLayer->setVisible( false );

Vetex着色器:

attribute vec4 a_position;
attribute vec2 a_texCoord;

varying vec2 blurCoordinates[5];

uniform vec2  u_blurOffset;

void main()
{
    gl_Position     = CC_MVPMatrix * a_position;

    blurCoordinates[0] = a_texCoord.xy;
    blurCoordinates[1] = a_texCoord.xy + u_blurOffset * 1.407333;
    blurCoordinates[2] = a_texCoord.xy - u_blurOffset * 1.407333;
    blurCoordinates[3] = a_texCoord.xy + u_blurOffset * 3.294215;
    blurCoordinates[4] = a_texCoord.xy - u_blurOffset * 3.294215;
}

片段着色器

varying vec2 blurCoordinates[5];

uniform float u_blurStrength;

void main()
{
    vec4 sum = vec4(0.0);
    sum += texture2D(CC_Texture0, blurCoordinates[0]) * 0.204164;
    sum += texture2D(CC_Texture0, blurCoordinates[1]) * 0.304005;
    sum += texture2D(CC_Texture0, blurCoordinates[2]) * 0.304005;
    sum += texture2D(CC_Texture0, blurCoordinates[3]) * 0.093913;
    sum += texture2D(CC_Texture0, blurCoordinates[4]) * 0.093913;
    gl_FragColor = sum;
}

查看预览:

See the preview:


完整的C ++和GLSL源代码可以在
GitHub (可以通过bool HelloWorld::m_blurFast切换实现.)


The full C++ and GLSL source code can be found on GitHub (The implementation can be switched by bool HelloWorld::m_blurFast).


此解决方案的想法是对场景进行平滑,渐进,高质量的模糊处理.为此,需要一种弱但快速且高质量的模糊算法.模糊精灵不会被删除,它将被存储以用于游戏引擎的下一次刷新,并用作下一个模糊步骤的来源.这意味着较弱的模糊精灵,再次变得模糊,因此比最后一个模糊得多.这是一个渐进过程,最终会产生强烈且精确的模糊精灵.
要设置此过程,需要3个层,即游戏层和2个模糊层(偶数和奇数).

The idea of this solution is, to do a smooth, progressive, high quality blur of the scene. For this a weak, but fast and high quality blur algorithm is need. A blurry sprite is not deleted, it will be stored for the next refresh of the game engine and is used as source for the next blurring step. This means the weak blurry sprite, again gets blurry and so it is a little bit more blurry than the last one. This is a progressive process which end in a strong and exact blurred sprite.
To set up this process 3 layers are of need, the game layer and 2 blur layers (even and odd).

m_gameLayer = Layer::create();
m_gameLayer->setVisible( false );
this->addChild(m_gameLayer, 0);

// blur layer even
m_blur_PostProcessLayerEven = PostProcess::create("shader/blur_fast2.vert", "shader/blur_fast2.frag");
m_blur_PostProcessLayerEven->setVisible( false );
m_blur_PostProcessLayerEven->setAnchorPoint(Point::ZERO);
m_blur_PostProcessLayerEven->setPosition(Point::ZERO);
this->addChild(m_blur_PostProcessLayerEven, 1);

// blur layer odd
m_blur_PostProcessLayerOdd = PostProcess::create("shader/blur_fast2.vert", "shader/blur_fast2.frag");
m_blur_PostProcessLayerOdd->setVisible( false );
m_blur_PostProcessLayerOdd->setAnchorPoint(Point::ZERO);
m_blur_PostProcessLayerOdd->setPosition(Point::ZERO);
this->addChild(m_blur_PostProcessLayerOdd, 1);

请注意,最初所有3层都是不可见的.

Note, that initially all 3 layers are invisible.

在update`方法中,一层设置为 visible 状态.如果没有模糊,则游戏层可见.一旦开始模糊,便会使用模糊着色器将游戏层渲染到 even 层.游戏层变为不可见,而 even 层变为可见.在下一个循环中,使用模糊着色器将 even 层渲染为 odd 层. even 层变为不可见,而 odd 层变为可见.这个过程一直持续到模糊停止为止.同时,高质量的场景变得越来越模糊. 如果必须再次显示原始场景,则必须将游戏层设置为可见,并且必须将 even odd 层设置为不可见.

In the update` method one layer is set to state visible. If there is no blurring, then the game layer is visible. Once blurring starts, the game layer is rendered to the even layer, with the blur shader. The game layer becomes invisible and the even layer becomes visible. In the next cycle the even layer is rendered to the odd layer, with the blur shader. The even layer becomes invisible and the odd layer becomes visible. This process continues till blurring is stopped. Meanwhile, the scene becomes blurred stronger and stronger, at high quality. If the original scene has to show again, then the game layer has be set to visible and the even and odd layer have to be set invisible.

update方法:

bool even = (m_blurTick % 2) == 0;
if ( m_blur )
{
    cocos2d::GLProgramState &blurFaststate1 = m_blur_PostProcessLayerEven->ProgramState();
    blurFaststate1.setUniformVec2( "u_texelOffset", Vec2( 1.0f/visibleSize.width, 1.0f/visibleSize.height ) );
    cocos2d::GLProgramState &blurFaststate2 = m_blur_PostProcessLayerOdd->ProgramState();
    blurFaststate2.setUniformVec2( "u_texelOffset", Vec2( -1.0f/visibleSize.width, -1.0f/visibleSize.height ) );

    if ( m_blurTick == 0 )
    {
        m_gameLayer->setVisible( true );
        m_blur_PostProcessLayerEven->draw(m_gameLayer);
    }
    else if ( even )
    {
      m_blur_PostProcessLayerEven->draw(m_blur_PostProcessLayerOdd);
    }
    else
    {
      m_blur_PostProcessLayerOdd->draw(m_blur_PostProcessLayerEven);
    }
    ++m_blurTick;
}
else
  m_blurTick = 0; 

m_gameLayer->setVisible( !m_blur );
m_blur_PostProcessLayerEven->setVisible( m_blur && even );
m_blur_PostProcessLayerOdd->setVisible( m_blur && !even );

着色器是一种简单而精确的3 * 3模糊着色器:

The shader is a simple and exact 3*3 blur shader:

Vetex着色器:

attribute vec4 a_position;
attribute vec2 a_texCoord;

varying vec2 blurCoordinates[9];

uniform vec2 u_texelOffset;

void main()
{
    gl_Position     = CC_MVPMatrix * a_position;

    blurCoordinates[0] = a_texCoord.st + vec2( 0.0,  0.0) * u_texelOffset.st;
    blurCoordinates[1] = a_texCoord.st + vec2(+1.0,  0.0) * u_texelOffset.st;
    blurCoordinates[2] = a_texCoord.st + vec2(-1.0,  0.0) * u_texelOffset.st;
    blurCoordinates[3] = a_texCoord.st + vec2( 0.0, +1.0) * u_texelOffset.st;
    blurCoordinates[4] = a_texCoord.st + vec2( 0.0, -1.0) * u_texelOffset.st;
    blurCoordinates[5] = a_texCoord.st + vec2(-1.0, -1.0) * u_texelOffset.st;
    blurCoordinates[6] = a_texCoord.st + vec2(+1.0, -1.0) * u_texelOffset.st;
    blurCoordinates[7] = a_texCoord.st + vec2(-1.0, +1.0) * u_texelOffset.st;
    blurCoordinates[8] = a_texCoord.st + vec2(+1.0, +1.0) * u_texelOffset.st;
}

片段着色器:

varying vec2 blurCoordinates[9];

void main()
{
    vec4 sum = vec4(0.0);
    sum += texture2D(CC_Texture0, blurCoordinates[0]) * 4.0;
    sum += texture2D(CC_Texture0, blurCoordinates[1]) * 2.0;
    sum += texture2D(CC_Texture0, blurCoordinates[2]) * 2.0;
    sum += texture2D(CC_Texture0, blurCoordinates[3]) * 2.0;
    sum += texture2D(CC_Texture0, blurCoordinates[4]) * 2.0;
    sum += texture2D(CC_Texture0, blurCoordinates[5]) * 1.0;
    sum += texture2D(CC_Texture0, blurCoordinates[6]) * 1.0;
    sum += texture2D(CC_Texture0, blurCoordinates[7]) * 1.0;
    sum += texture2D(CC_Texture0, blurCoordinates[8]) * 1.0;
    sum /= 16.0; 
    gl_FragColor = sum;
}


同样,完整的C ++和GLSL源代码可以在中找到GitHub .


Again, the full C++ and GLSL source code can be found on GitHub.

查看预览:

See the preview:

这篇关于暂停时快速出现高斯模糊的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆