Cocos2d-x v3.2 例程中的 SpriteBlur

SpriteBlur类是cocos2d-x例程中的一个类,用于将一个精灵模糊。
由于项目需要模糊场景中的精灵,于是笔者把该类提取出来,未做任何修改用到自己的工程中。
实际应用发现奇怪的现象:
cocos2d-x_v3.0中提供的SpriteBlur,精灵模糊后基本是正常的,边缘的地方偶尔会闪出黑边。
cocos2d-x_v3.2中提供的SpriteBlur,精灵模糊后原本是透明像素的部分变成了黑色,即便模糊半径设置为0(即不模糊)也一样。

请看下图

左侧是v3.2的效果,采样数设置的是3,模糊半径10。
右侧是v3.0的效果,采样数不能设置,  模糊半径10。

检查 SpriteBlur.hSpriteBlur.cpp 中的代码,没有关于像素绘制的部分,而是将这部分工作交给了着色器“example_Blur.fsh

......
void main(void)
{
    vec3 col = blur(v_texCoord);
    gl_FragColor = vec4(col, 1.0) * v_fragmentColor;
}

OK,问题就在给gl_FragColor赋值这一句,vec4(col, 1.0),强制把alpha值设置成了1,所以透明像素会"黑"。做如下修改:

gl_FragColor = vec4(col, texture2D(CC_Texture0, v_texCoord).a) * v_fragmentColor;

即将alpha值设置为当前纹理坐标处的alpha值,运行得到下面的效果:

Oz,头像边缘出现了锯齿和白边,为啥会这样呢?修改:

gl_FragColor = v_fragmentColor;

运行,头像的图片显示成了全白的长方形,白边也许就是因此而来。再看example_Blur.fsh中的blur函数,其中为每一个点的rgb值都进行了加权操作,于是笔者想,如果将alpha值也进行加权操作会怎么样呢?修改example_Blur.fsh:

……
vec4 blur(vec2);
void main(void)
{
    vec4 col = blur(v_texCoord);
    gl_FragColor = col * v_fragmentColor;
}

vec4 blur(vec2 p)
{
    if (blurRadius > 0.0 && sampleNum > 1.0)
    {
        vec4 col = vec4(0);
        vec2 unit = 1.0 / resolution.xy;
        float r = blurRadius;
        float sampleStep = r / sampleNum;
        float count = 0.0;
        for(float x = -r; x < r; x += sampleStep)
        {
            for(float y = -r; y < r; y += sampleStep)
            {
                float weight = (r - abs(x)) * (r - abs(y));
                col += texture2D(CC_Texture0, p + vec2(x * unit.x, y * unit.y)) * weight;
                count += weight;
            }
        }
        return col / count;
    }
    return texture2D(CC_Texture0, p);
}

运行,得到下面的结果:

可以看到,效果与3.0中的效果基本一致了。
但是,还是不太理解,两种修改方法的效果怎么会不一样?高斯模糊应该只是重新分布rgb颜色值吧,怎么alpha也要进行加权操作才能正常?
另外,无论是3.2还是3.0,模糊以后,在任务头顶有一道明显的黑边,不知是何原因,有待探究。

最后附上完整的代码跟着色器文件:

#ifndef __prjHeads__SpriteBlur__
#define __prjHeads__SpriteBlur__

#include "cocos2d.h"

class SpriteBlur : public cocos2d::Sprite
{
public:
    ~SpriteBlur();
    bool initWithTexture(cocos2d::Texture2D* texture, const cocos2d::Rect&  rect);
    void initGLProgram();
    static SpriteBlur* create(const char *pszFileName);
    void setBlurRadius(float radius);
    void setBlurSampleNum(float num);

protected:
    float _blurRadius;
    float _blurSampleNum;
};
#endif /* defined(__prjHeads__SpriteBlur__) */
#include "SpriteBlur.h"

USING_NS_CC;

SpriteBlur::~SpriteBlur()
{
}

SpriteBlur* SpriteBlur::create(const char *pszFileName)
{
    SpriteBlur* pRet = new SpriteBlur();
    if (pRet && pRet->initWithFile(pszFileName))
    {
        pRet->autorelease();
    }
    else
    {
        CC_SAFE_DELETE(pRet);
    }
    return pRet;
}

bool SpriteBlur::initWithTexture(Texture2D* texture, const Rect& rect)
{
    _blurRadius = 0;
    if( Sprite::initWithTexture(texture, rect) )
    {
#if CC_ENABLE_CACHE_TEXTURE_DATA
        auto listener = EventListenerCustom::create(EVENT_RENDERER_RECREATED, [this](EventCustom* event){
            setGLProgram(nullptr);
            initGLProgram();
        });
        _eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
#endif
        initGLProgram();
        return true;
    }
    return false;
}

void SpriteBlur::initGLProgram()
{
    GLchar * fragSource = (GLchar*) String::createWithContentsOfFile(
                                            FileUtils::getInstance()->fullPathForFilename("SpriteBlur.fsh").c_str()
                                                                     )->getCString();
    auto program = GLProgram::createWithByteArrays(ccPositionTextureColor_noMVP_vert, fragSource);
    auto glProgramState = GLProgramState::getOrCreateWithGLProgram(program);
    setGLProgramState(glProgramState);
    auto size = getTexture()->getContentSizeInPixels();
    getGLProgramState()->setUniformVec2("resolution", size);
    getGLProgramState()->setUniformFloat("blurRadius", _blurRadius);
    getGLProgramState()->setUniformFloat("sampleNum", 7.0f);
}

void SpriteBlur::setBlurRadius(float radius)
{
    _blurRadius = radius;
    getGLProgramState()->setUniformFloat("blurRadius", _blurRadius);
}

void SpriteBlur::setBlurSampleNum(float num)
{
    _blurSampleNum = num;
    getGLProgramState()->setUniformFloat("sampleNum", _blurSampleNum);
}
#ifdef GL_ES
precision mediump float;
#endif

varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
uniform vec2 resolution;
uniform float blurRadius;
uniform float sampleNum;

vec4 blur(vec2);

void main(void)
{
    vec4 col = blur(v_texCoord);
    gl_FragColor = col * v_fragmentColor;
}

vec4 blur(vec2 p)
{
    if (blurRadius > 0.0 && sampleNum > 1.0)
    {
        vec4 col = vec4(0);
        vec2 unit = 1.0 / resolution.xy;
        float r = blurRadius;
        float sampleStep = r / sampleNum;
        float count = 0.0;
        for(float x = -r; x < r; x += sampleStep)
        {
            for(float y = -r; y < r; y += sampleStep)
            {
                float weight = (r - abs(x)) * (r - abs(y));
                col += texture2D(CC_Texture0, p + vec2(x * unit.x, y * unit.y)) * weight;
                count += weight;
            }
        }
        return col / count;
    }
    return texture2D(CC_Texture0, p);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值