在Cocos2d-x v3.x 中实现带颜色滤镜的Sprite

一、目的

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


二、原理

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


这个shader是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
#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回调时调用:

1
glProgramState->setUniformMat4(  "fiterMat" ,m_uSpriteFilter)

        

四、使用

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

1
2
3
4
5
6
7
8
9
10
11
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
     /****************************************************************************
 
      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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
  
     /****************************************************************************
      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);
     }


来源网址:http://www.cocoachina.com/bbs/read.php?tid-238457.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值