暂停时快速高斯模糊[关闭]

2023-11-27

In cocos2d-x我需要实现快速高斯模糊,它应该是这样的(我刚刚在 App Store 上发现了一些游戏,已经统一完成了这样的模糊):

enter image description here

所以,这很好fadeIn-fadeOut当用户暂停游戏时模糊。

GPUImage 已经有我需要的快速模糊,但我找不到 cocos2d-x 的解决方案。

  • v1 代码(GPUImage v1)客观的 C

  • v2 代码 现在是什么时候 Swift(GPUImage v2) Swift

  • GPUImage-xC++版本

这是使用 GPUImage2 实时摄像头视图的结果- 在 iPod Touch 5G 上进行了测试,在这款缓慢且旧的设备上运行速度很快。

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


学习后《Cocos2d-X 中的后处理效果》 and “渲染纹理+模糊”,我提出了以下解决方案。

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

模糊算法在着色器中实现。在场景上应用模糊效果的常见方法是首先沿视口的 X 轴模糊,然后沿视口的 Y 轴进行第二次模糊(请参见着色器Lesson5)。这是一个可接受的近似值,可以显着提高性能。

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

// 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.

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

// 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,我试图让事情尽可能简单:

后处理.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;
};

后处理.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_WIDHT and u_blurStrength)。相反,我使用了光滑的埃尔米特插值与GLSL功能类似smoothstep:

模糊.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;
}

模糊片段

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).

See the preview:
preview


每个模糊半径都有单独的着色器

高斯模糊算法的高性能版本是以下网址中提出的解决方案:GPUImage-x。在此实现中,为每个模糊半径创建单独的模糊着色器。完整的源代码cocos2d-x演示实现可以在以下位置找到GitHub.该实现提供了2种变体,标准实现和优化实现,如链接中的实现,可以通过以下方式设置bool GPUimageBlur::m_optimized。该实现为每个半径从 0 到int GPUimageBlur::m_maxRadius和西格玛float GPUimageBlur::m_sigma.

See the preview:
preview


快速有限质量模糊

一个更强大的解决方案,但明显质量很低,将使用呈现在的着色器在移动 GPU 上优化高斯模糊。模糊不是动态的,只能打开或关闭:

update methode:

// 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 );

顶点着色器:

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:
enter image description here


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


具有两层的渐进式解决方案(帧缓冲区)

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

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 层都是不可见的。

在 update` 方法中,一层被设置为状态visible。如果没有模糊,则游戏层是可见的。一旦模糊开始,游戏层就会被渲染到even层,带有模糊着色器。游戏层变得不可见并且even图层变得可见。在下一个周期中even层被渲染到odd层,带有模糊着色器。这even层变得不可见并且odd图层变得可见。这个过程一直持续到模糊停止为止。与此同时,场景变得越来越模糊,而且质量很高。 如果必须再次显示原始场景,则游戏图层已设置为可见,并且even and odd图层必须设置为不可见。

update methode:

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 模糊着色器:

顶点着色器:

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.

See the preview:
enter image description here

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

暂停时快速高斯模糊[关闭] 的相关文章

  • 具有不同大小结构的结构数组的 malloc()

    如果每个结构都包含一个大小不同的字符串数组 那么如何正确地 malloc 一个结构数组 因此每个结构可能有不同的大小 并且不可能 realloc 结构体数量 sizeof 结构体名称 after malloc 初始大小 sizeof 结构名
  • clang 格式换行符在错误的位置

    给出以下代码行 get abc manager get platform status abc platform status sw update status fill update status actions allowed stat
  • 将字节数组转换为托管结构

    更新 这个问题的答案帮助我编写了开源项目GitHub 上的 AlicanC 现代战争 2 工具 https github com AlicanC AlicanC s Modern Warfare 2 Tool 你可以看到我是如何阅读这些数据
  • C 程序从连接到系统的 USB 设备读取数据

    我正在尝试从连接到系统 USB 端口的 USB 设备 例如随身碟 获取数据 在这里 我可以打开设备文件并读取一些随机原始数据 但我想获取像 minicom teraterm 这样的数据 请让我知道我可以使用哪些方法和库来成功完成此操作以及如
  • 如何尝试/捕获所有异常

    我正在完成由其他人启动的 UWP 应用程序 该应用程序经常崩溃 我总是陷入困境应用程序 at if global System Diagnostics Debugger IsAttached global System Diagnostic
  • 如何创建用于 QML 的通用对象模型?

    我想知道是否有任何宏或方法如何将 Qt 模型注册为 QObject 的属性 例如 我有AnimalModel http doc qt io qt 5 qtquick modelviewsdata cppmodels html qabstra
  • 对 boost 库的依赖项没有完整路径

    我已经成功构建了动态库 依赖于使用自定义前缀构建和安装的 boost 库 b2 install prefix PREFIX 然而 当我跑步时otool L在我的库中 我得到如下输出 libboost regex dylib compatib
  • 无法解析远程名称 - webclient

    我面临这个错误 The remote name could not be resolved russgates85 001 site1 smarterasp net 当我请求使用 Web 客户端读取 html 内容时 出现错误 下面是我的代
  • 将带有 glut 的点击坐标添加到向量链接列表中

    我想创建一个向量链接列表 并在 GLUT 库的帮助下获取点击的位置并将它们附加到链接列表中 这些是我写的结构 typedef struct vector int x int y Vector typedef struct VectorLis
  • 从 Code::Blocks 运行程序时出现空白控制台窗口 [重复]

    这个问题在这里已经有答案了 当我尝试在 Code Blocks 中构建并运行新程序时 控制台窗口弹出空白 我必须单击退出按钮才能停止它 它对我尝试过的任何新项目 包括 Hello world 都执行此操作 奇怪的是 它对于我拥有的任何旧项目
  • 2D morton 码编码/解码 64 位

    如何将给定 x y 的莫顿代码 z 顺序 编码 解码为 32 位无符号整数 生成 64 位莫顿代码 反之亦然 我确实有 xy2d 和 d2xy 但仅适用于 16 位宽的坐标 产生 32 位莫顿数 在网上查了很多 但没有找到 请帮忙 如果您可
  • OpenCV 2.4.3 中的阴影去除

    我正在使用 OpenCV 2 4 3 最新版本 使用内置的视频流检测前景GMG http docs opencv org modules gpu doc video html highlight gmg gpu 3a 3aGMG GPU算法
  • WPF。如何从另一个窗口隐藏/显示主窗口

    我有两个窗口 MainWindow 和 Login 显示登录的按钮位于主窗口 this Hide Login li new Login li Show 登录窗口上有一个检查密码的按钮 如果密码正确 我如何显示主窗口 将参数传递给 MainW
  • 0-1背包算法

    以下 0 1 背包问题是否可解 浮动 正值和 浮动 权重 可以是正数或负数 背包的 浮动 容量 gt 0 我平均有 这是一个相对简单的二进制程序 我建议用蛮力进行修剪 如果任何时候你超过了允许的重量 你不需要尝试其他物品的组合 你可以丢弃整
  • 从 Delphi 调用 C# dll

    我用单一方法编写了 Net 3 5 dll 由Delphi exe调用 不幸的是它不起作用 步骤 1 使用以下代码创建 C 3 5 dll public class MyDllClass public static int MyDllMet
  • 如何引用解决方案之外的项目?

    我有一个 Visual Studio C 解决方案 其中包含一些项目 其中一个项目需要引用另一个不属于解决方案的项目 一开始我引用了dll
  • 在 C++17 中使用 成员的链接错误

    我在 Ubuntu 16 04 上使用 gcc 7 2 并且需要使用 C 17 中的新文件系统库 尽管确实有一个名为experimental filesystem的库 但我无法使用它的任何成员 例如 当我尝试编译此文件时 include
  • 为什么文件更新时“如果较新则复制”不复制文件?

    我在 Visual Studio Express 中有一个解决方案 如下所示 The LogicSchemaC 中的类 将在运行时解析指定的 XML 文件 以下是在main的方法Program cs LogicSchema ls new L
  • 带有私有设置器的 EFCore Base 实体模型属性 - 迁移奇怪的行为

    实体模型继承的类内的私有设置器似乎会导致 EFCore 迁移出现奇怪的问题 考虑以下示例 其中有多个类 Bar and Baz 继承自Foo 跑步时Add Migration多次命令 添加 删除private修饰符 生成的模式在多个方面都是
  • 如何使用 C# 以低分辨率形式提供高分辨率图像

    尝试使用 300dpi tif 图像在网络上显示 目前 当用户上传图像时 我正在动态创建缩略图 如果创建的页面引用宽度为 500x500px 的高分辨率图像 我可以使用相同的功能即时转换为 gif jpg 吗 将创建的 jpg 的即将分辨率

随机推荐