在cocos2dx 3.x 中实现带颜色滤镜的Sprite

来源:互联网 发布:php如何写短信接口 编辑:程序博客网 时间:2024/04/27 15:08
一.目的

cocos2dx做项目时经常会碰到要对图片进行变色的需求,最常用的就是变灰了,就要让按钮变灰来表示当前的状态是不可点的。 但是cocos2dx的Sprite中是没有这个变灰支持的。那么,就要我们自己动手来扩展实现一个。我们让这个带变色功能的Sprite叫做FilterSprite。这个FilterSprite扩展了Sprite的功能:可以方便地变换颜色。

二.原理

对图片进行颜色变换,就是对图片上的每个像素进行变换。要实现这个,要新创建一个fragmentShader,这个fragmentShader 比sprite的那个fragmentShader多了一个颜色变换矩阵。shader会让图片上每个像素与颜色变换矩阵进行相乘,输出新的像素值。

这个shader是这样的:

#ifdef GL_ES 
precision mediump float; 
#endif
uniform sampler2D u_texture; 
varying vec2 v_texCoord; 
varying vec4 v_fragmentColor; 
uniform mat4 fiterMat; 
void main(void) 

   vec4 value = v_fragmentColor*texture2D(u_texture, v_texCoord); 
   gl_FragColor = fiterMat*value; 
};
从shader上我们看到,“filterMat” 就是所谓的颜色变换矩阵,仅仅在原来像素输出前用它处理了一下:与待输出像素相乘。 这个shader是opengl层级的,要应用到coco2dx引擎中,我们要着手实现cocos2dx的FilterSprite类了。

三.实现

实现这个FilterSprite注意几个要点:

引擎中一个shader对应一个GLProgram,所以这个带颜色滤镜的shader(称为filterShader)对应一个GLProgram(称为filterProgram)对象,在实际使用时,是用对GLProgram进行了封装的GLProgramState(称为filterProgramState)对象,FilterSprite对象的_glProgramState要设置成filterProgramState对象,在源码中FilterSprite的initWithTexture进行这个filterShader和filterProgram的关联。
在渲染时要将滤镜传递给shader程序,在源码中就是在onDraw回调时调用
        glProgramState->setUniformMat4( "fiterMat",m_uSpriteFilter)
四.使用

使用起来非常简单,只需要设置一个颜色矩阵,例如,如果要变灰就设置一个灰度矩阵,如果要恢复原貌就设置一个单位矩阵。

    Sprite *_sprite1;
    _sprite1 = FilterSprite::create("Images/background3.png");

    GLfloat  filterMat[16]= {
            0.3f,  0.3f,  0.3f,  0.0f,
            0.59f, 0.59f, 0.59f, 0.59f,
            0.11f, 0.11f, 0.11f, 0.0f,
            0.0f,  0.0f,  0.0f,  1.0f,
    };

    dynamic_cast<FilterSprite*>(_sprite1)->setFilterMat(filterMat);
五.源码

FilterSprite.h:

    /****************************************************************************

     FilterSpirte.h

     Created by LiaoYanXuan  on 14-10-21.
     ****************************************************************************/

    #ifndef __FilterSpirte_h
    #define __FilterSpirte_h

    #include "cocos2d.h"

    USING_NS_CC;


    class FilterSprite : public Sprite{

    public:

        FilterSprite();
        virtual ~FilterSprite();

        static FilterSprite* create();
        static FilterSprite* create(const std::string& filename);
        static FilterSprite* create(const std::string& filename, const Rect& rect);


        static FilterSprite* createWithTexture(Texture2D *pTexture);
        static FilterSprite* createWithTexture(Texture2D *pTexture, const Rect& rect, bool rotated=false);
        static FilterSprite* createWithSpriteFrame(SpriteFrame *pSpriteFrame);
        static FilterSprite* createWithSpriteFrameName(const std::string& spriteFrameName);

        bool initWithTexture(Texture2D* pTexture, const Rect& tRect);
        virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override;
        void onDraw(const Mat4 &transform, uint32_t flags);
        void setFilterMat(cocos2d::Mat4 matrixArray);
        //to-do 提供一个设置滤镜的方法
    protected:
        CustomCommand _customCommand;
    private:
        cocos2d::Mat4   m_uSpriteFilter;
    };

    #endif
FilterSprite.cpp:

    /****************************************************************************
     FilterSpirte.h

     Created by LiaoYanXuan  on 14-10-21.
     ****************************************************************************/
    #include "FilterSprite.h"

    FilterSprite::FilterSprite(void)
    {
        m_uSpriteFilter=Mat4::IDENTITY;
    }

    FilterSprite::~FilterSprite()
    {

    }

    FilterSprite* FilterSprite::create()
    {
        FilterSprite *sprite = new (std::nothrow) FilterSprite();
        if (sprite && sprite->init())
        {
            sprite->autorelease();
            return sprite;
        }
        CC_SAFE_DELETE(sprite);
        return nullptr;
    }

    FilterSprite* FilterSprite::create(const std::string& filename)
    {
        FilterSprite *sprite = new (std::nothrow) FilterSprite();
        if (sprite && sprite->initWithFile(filename))
        {
            sprite->autorelease();
            return sprite;
        }
        CC_SAFE_DELETE(sprite);
        return nullptr;
    }

    FilterSprite* FilterSprite::create(const std::string& filename, const Rect& rect)
    {
        FilterSprite *sprite = new (std::nothrow) FilterSprite();
        if (sprite && sprite->initWithFile(filename, rect))
        {
            sprite->autorelease();
            return sprite;
        }
        CC_SAFE_DELETE(sprite);
        return nullptr;
    }

    FilterSprite* FilterSprite::createWithTexture(Texture2D *pTexture)
    {
        FilterSprite *sprite = new (std::nothrow) FilterSprite();
        Rect rect = Rect::ZERO;
        rect.size = pTexture->getContentSize();
        if (sprite && sprite->initWithTexture(pTexture,rect))
        {
            sprite->autorelease();
            return sprite;
        }
        CC_SAFE_DELETE(sprite);
        return nullptr;
    }

    FilterSprite* FilterSprite::createWithTexture(Texture2D *texture, const Rect& rect, bool rotated)
    {
        FilterSprite *sprite = new (std::nothrow) FilterSprite();
        if (sprite && sprite->initWithTexture(texture, rect))
        {
            sprite->autorelease();
            return sprite;
        }
        CC_SAFE_DELETE(sprite);
        return nullptr;
    }

    FilterSprite* FilterSprite::createWithSpriteFrame(SpriteFrame *spriteFrame)
    {
        FilterSprite *sprite = new (std::nothrow) FilterSprite();
        if (sprite && spriteFrame && sprite->initWithSpriteFrame(spriteFrame))
        {
            sprite->autorelease();
            return sprite;
        }
        CC_SAFE_DELETE(sprite);
        return nullptr;
    }

    FilterSprite* FilterSprite::createWithSpriteFrameName(const std::string& spriteFrameName)
    {
        SpriteFrame *frame = SpriteFrameCache::getInstance()->getSpriteFrameByName(spriteFrameName);

    #if COCOS2D_DEBUG > 0
        char msg[256] = {0};
        sprintf(msg, "Invalid spriteFrameName: %s", spriteFrameName.c_str());
        CCASSERT(frame != nullptr, msg);
    #endif

        return createWithSpriteFrame(frame);
    }

    bool FilterSprite::initWithTexture(Texture2D* pTexture, const Rect& tRect){
        do{
            CC_BREAK_IF(!Sprite::initWithTexture(pTexture, tRect));

            GLchar* pszFragSource =
                "#ifdef GL_ES \n \
                precision mediump float; \n \
                #endif \n \
                uniform sampler2D u_texture; \n \
                varying vec2 v_texCoord; \n \
                varying vec4 v_fragmentColor; \n \
                uniform mat4 fiterMat; \n \
                void main(void) \n \
                { \n \
                vec4 value = v_fragmentColor*texture2D(u_texture, v_texCoord); \n \
                gl_FragColor = fiterMat*value; \n \
                }";

             auto glprogram = GLProgram::createWithByteArrays(ccPositionTextureColor_vert, pszFragSource);
             auto glprogramstate = GLProgramState::getOrCreateWithGLProgram(glprogram);
             setGLProgramState(glprogramstate);

             CHECK_GL_ERROR_DEBUG();

             return true;
        } while (0);
        return false;
    }

    void  FilterSprite::setFilterMat(cocos2d::Mat4 matrixArray)
    {
        m_uSpriteFilter=matrixArray;
    }

    void FilterSprite::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
    {
        _customCommand.init(_globalZOrder);
        _customCommand.func = CC_CALLBACK_0(FilterSprite::onDraw, this, transform, flags);
        renderer->addCommand(&_customCommand);
    }

    void FilterSprite::onDraw(const Mat4 &transform, uint32_t flags)
    {
         auto glProgramState = getGLProgramState();
         glProgramState->setUniformMat4("fiterMat",m_uSpriteFilter);
         glProgramState->apply(transform);

        GL::blendFunc( _blendFunc.src, _blendFunc.dst );

        GL::bindTexture2D( _texture->getName() );
        GL::enableVertexAttribs( GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX );

    #define kQuadSize sizeof(_quad.bl)
        size_t offset = (size_t)&_quad;

        // vertex
        int diff = offsetof( V3F_C4B_T2F, vertices);
        glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 3, GL_FLOAT, GL_FALSE, kQuadSize, (void*) (offset + diff));

        // texCoods
        diff = offsetof( V3F_C4B_T2F, texCoords);
        glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORD, 2, GL_FLOAT, GL_FALSE, kQuadSize, (void*)(offset + diff));

        // color
        diff = offsetof( V3F_C4B_T2F, colors);
        glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (void*)(offset + diff));

        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

        CHECK_GL_ERROR_DEBUG();
        CC_INCREMENT_GL_DRAWN_BATCHES_AND_VERTICES(1,4);
    } 
0 0
原创粉丝点击