可以通过网盘下载gif看看动态效果
http://pan.baidu.com/s/1jGoRER0
也可以看看移植前的stage3D版本的效果
http://www.dreamfairy.cn/blog/index.php/2014/05/10/create-a-god-ray-effect-in-stage3d.html
又一个月没写博客了,先吐槽下,怎么广告这么多。。。 每天都要删,插件都顶不住了。
然后再吐槽下之前某网友好像是叫as3er的看了我的god ray教程,然后自己在away3d中实现了但是缺少了遮挡这个god ray中最体现效果的功能。 这先不说,这位同学看就看了,理解了就好了嘛,反而不满我不发源码~ 我也不知道该说啥 摊手. 国内的开源社区条件是一直很恶劣的,以前我也完整开源,现在只能有选择的开源了,我还没有那么伟大到不用考虑薪水问题,而无私贡献。之前廖成在 Particle 做的 幻影和热流效果就不开源,我做了详细的讲解但也不开源,没有必要跟他人起矛盾,但我觉得对于有经验的程序员这些讲解已经足够了。
回归正题,cocos2dx纯粹的代码堆积,毫无美感,这是用了一段时间的体会,API的不稳定也是大忌,有点赞同云风大叔的想法了。 最近要在 CCSprite 上做各种效果,以网络上的教程来说,只能创建多个 自定义CCSprite, 每个特定滤镜,最后不断切换。。。 对于从AS毕业的我来说,太坑了。
于是打算实现带滤镜接口的CCSprite,我没有一部分程序员的毛病,比如重复造轮子,触控的程序员估计大部分都有这毛病,看看cocos2dx中大量的重复功能函数库。 于是找到了 zrong 同学写的 CCFilteredSprite github 上在这里
https://github.com/zrong/cocos2d-x-filters 而且这货在 quick 和 cocos2dx 3.x 中貌似继承了。 由于我不想装win8, 所以用不了 3.x , 因为是开发弹幕游戏,担心lua顶不住,不能用quick. 又因为项目是用 cocos2dx 2.2.4 搞的,这个库不完全兼容,所以只能做修改或者重写了。
我试着重现AS3 的 多个滤镜,但是 cocos2dx 在 2.x 版本对于 renderTexture 的封装。。。无力吐槽, 要实现仅能大改了,于是放弃了这个想法,还是单个滤镜吧。
接下来上代码
核心类 只有2个 CCSpriteWithFilter.cpp 和 CCFilter.cpp
所有的滤镜都是继承自 CCFilter.cpp
那么从头开始
首先创建头文件 CCSpriteWithFilter.h
[cc language=”c++”]
/******************************************************************
Copyright (c) 2014, dreamfairy.cn
All rights reserved
创建日期: 2014年6月26日 16时33分
文件名称: CCSpriteWidthFilter.h
说 明:
当前版本: 1.00
作 者: 苍白的茧
概 述:
*******************************************************************/
#ifndef __CCspriteWithFilter__
#define __CCspriteWithFilter__
#include “cocos2d.h”
#include “filter/CCFilter.h”
class CCFilter;
USING_NS_CC;
class CCSpriteWidthFilter : public CCSprite
{
public:
CCSpriteWidthFilter();
~CCSpriteWidthFilter();
static CCSpriteWidthFilter* create();
static CCSpriteWidthFilter* create(const char* $pszFileName);
static CCSpriteWidthFilter* create(const char* $pszFileName, const CCRect& $rect);
static CCSpriteWidthFilter* createWithTexture(CCTexture2D* $pTexture);
static CCSpriteWidthFilter* createWithTexture(CCTexture2D* $pTexture, const CCRect& rect);
static CCSpriteWidthFilter* createWithSpriteFrame(CCSpriteFrame* $pSpriteFrame);
static CCSpriteWidthFilter* createWithSpriteFrameName(const char* $pszSpriteFrameName);
virtual void draw(void);
virtual CCFilter* getFilter(unsigned int index = 0);
virtual void setFilter(CCFilter* filter);
virtual CCArray* getFilters();
virtual void setFilters(CCArray* filters);
virtual void clearFilter();
protected:
virtual void drawFilter();
virtual bool updateFilters();
CCArray* m_filters;
};
#endif
[/cc]
本质上只是增加几个自定义方法 setFilter, setFilters, clearFilter, drawFiler 功能就是将滤镜丢进去,或者清除 没啥好讲解的
然后是实现部分 CCSpriteWidthFilter.cpp
[cc lang=”c++”]
#include “CCSpriteWidthFilter.h”
#include “ccMacros.h”
void CCSpriteWidthFilter::draw()
{
CCObject* obj;
CCARRAY_FOREACH(m_filters,obj)
{
CCFilter* filter = static_cast
CC_PROFILER_START_CATEGORY(kCCProfilerCategorySprite, “CCSprite – draw”);
CCAssert(!m_pobBatchNode, “If CCSprite is being rendered by CCSpriteBatchNode, CCSprite#draw SHOULD NOT be called”);
this->setShaderProgram(filter->getProgram());
do
{
ccGLEnable(m_eGLServerState);
CCAssert(getShaderProgram(), “No shader program set for this node”);
{
getShaderProgram()->use();
getShaderProgram()->setUniformsForBuiltins();
}
} while (0);
//useProgram后调用draw
filter->draw();
ccGLBlendFunc( m_sBlendFunc.src, m_sBlendFunc.dst );
ccGLBindTexture2D(m_pobTexture->getName());
ccGLEnableVertexAttribs( kCCVertexAttribFlag_PosColorTex );
#define kQuadSize sizeof(m_sQuad.bl)
#ifdef EMSCRIPTEN
long offset = 0;
setGLBufferData(&m_sQuad, 4 * kQuadSize, 0);
#else
long offset = (long)&m_sQuad;
#endif // EMSCRIPTEN
// vertex
int diff = offsetof( ccV3F_C4B_T2F, vertices);
glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, kQuadSize, (void*) (offset + diff));
// texCoods
diff = offsetof( ccV3F_C4B_T2F, texCoords);
glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, kQuadSize, (void*)(offset + diff));
// color
diff = offsetof( ccV3F_C4B_T2F, colors);
glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, kQuadSize, (void*)(offset + diff));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
CHECK_GL_ERROR_DEBUG();
#if CC_SPRITE_DEBUG_DRAW == 1
// draw bounding box
CCPoint vertices[4]={
ccp(m_sQuad.tl.vertices.x,m_sQuad.tl.vertices.y),
ccp(m_sQuad.bl.vertices.x,m_sQuad.bl.vertices.y),
ccp(m_sQuad.br.vertices.x,m_sQuad.br.vertices.y),
ccp(m_sQuad.tr.vertices.x,m_sQuad.tr.vertices.y),
};
ccDrawPoly(vertices, 4, true);
#elif CC_SPRITE_DEBUG_DRAW == 2
// draw texture box
CCSize s = this->getTextureRect().size;
CCPoint offsetPix = this->getOffsetPosition();
CCPoint vertices[4] = {
ccp(offsetPix.x,offsetPix.y), ccp(offsetPix.x+s.width,offsetPix.y),
ccp(offsetPix.x+s.width,offsetPix.y+s.height), ccp(offsetPix.x,offsetPix.y+s.height)
};
ccDrawPoly(vertices, 4, true);
#endif // CC_SPRITE_DEBUG_DRAW
CC_INCREMENT_GL_DRAWS(1);
CC_PROFILER_STOP_CATEGORY(kCCProfilerCategorySprite, “CCSprite – draw”);
}
}
void CCSpriteWidthFilter::drawFilter()
{
if(m_filters && m_filters->count() == 1)
{
static_cast
}
}
//以push的方式增加filter
void CCSpriteWidthFilter::setFilter( CCFilter* filter )
{
if(m_filters->indexOfObject(filter) == UINT_MAX)
{
m_filters->addObject(filter);
filter->initSprite(this);
}
}
void CCSpriteWidthFilter::clearFilter()
{
//todo
}
bool CCSpriteWidthFilter::updateFilters()
{
return true;
}
CCFilter* CCSpriteWidthFilter::getFilter( unsigned int index /*= 0*/ )
{
return static_cast
}
CCSpriteWidthFilter* CCSpriteWidthFilter::create()
{
CCSpriteWidthFilter* sprite = new CCSpriteWidthFilter();
if(sprite && sprite->init())
{
sprite->autorelease();
return sprite;
}
CC_SAFE_DELETE(sprite);
return NULL;
}
CCSpriteWidthFilter* CCSpriteWidthFilter::create(const char* $pszFileName)
{
CCSpriteWidthFilter* sprite = new CCSpriteWidthFilter();
if (sprite && sprite->initWithFile($pszFileName))
{
sprite->autorelease();
return sprite;
}else{
CC_SAFE_DELETE(sprite);
return NULL;
}
}
CCSpriteWidthFilter* CCSpriteWidthFilter::create(const char* $pszFileName, const CCRect& $rect)
{
CCSpriteWidthFilter *sprite = new CCSpriteWidthFilter();
if (sprite && sprite->initWithFile($pszFileName, $rect))
{
sprite->autorelease();
return sprite;
}
CC_SAFE_DELETE(sprite);
return NULL;
}
CCSpriteWidthFilter* CCSpriteWidthFilter::createWithTexture(CCTexture2D* $pTexture)
{
CCSpriteWidthFilter *sprite = new CCSpriteWidthFilter();
if (sprite && sprite->initWithTexture($pTexture))
{
sprite->autorelease();
return sprite;
}
CC_SAFE_DELETE(sprite);
return NULL;
}
CCSpriteWidthFilter* CCSpriteWidthFilter::createWithTexture(CCTexture2D* $pTexture, const CCRect& rect)
{
CCSpriteWidthFilter *sprite = new CCSpriteWidthFilter();
if (sprite && sprite->initWithTexture($pTexture, rect))
{
sprite->autorelease();
return sprite;
}
CC_SAFE_DELETE(sprite);
return NULL;
}
CCSpriteWidthFilter* CCSpriteWidthFilter::createWithSpriteFrame(CCSpriteFrame* $pSpriteFrame)
{
CCSpriteWidthFilter *sprite = new CCSpriteWidthFilter();
if ($pSpriteFrame && sprite)
{
if ($pSpriteFrame->isRotated())
{
CCSprite* __sp = CCSprite::createWithSpriteFrame($pSpriteFrame);
CCSize __size = __sp->getContentSize();
__sp->setAnchorPoint(ccp(0,0));
__sp->setPosition(ccp(0,0));
CCRenderTexture* __rTex = CCRenderTexture::create(__size.width, __size.height);
__rTex->begin();
__sp->visit();
__rTex->end();
CCTexture2D* __newTex = new CCTexture2D();
__newTex->initWithImage(__rTex->newCCImage(true));
__newTex->autorelease();
sprite->initWithTexture(__newTex);
//CCLOG(“==== CCFilteredSprite::initWithTexture, rotated true texture wh(%f,%f)”, __newTex->getContentSize().width, __newTex->getContentSize().height);
}
else
{
sprite->initWithSpriteFrame($pSpriteFrame);
}
sprite->autorelease();
return sprite;
}
CC_SAFE_DELETE(sprite);
return NULL;
}
CCSpriteWidthFilter* CCSpriteWidthFilter::createWithSpriteFrameName(const char* $pszSpriteFrameName)
{
CCSpriteFrame *pFrame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName($pszSpriteFrameName);
#if COCOS2D_DEBUG > 0
char msg[256] = { 0 };
sprintf(msg, “Invalid spriteFrameName: %s”, $pszSpriteFrameName);
CCAssert(pFrame != NULL, msg);
#endif
return createWithSpriteFrame(pFrame);
}
CCSpriteWidthFilter::CCSpriteWidthFilter()
{
m_filters = CCArray::create();
m_filters->retain();
}
CCSpriteWidthFilter::~CCSpriteWidthFilter()
{
m_filters->release();
//CC_SAFE_DELETE(m_filters);
}
void CCSpriteWidthFilter::setFilters( CCArray* filters )
{
CCObject* obj;
CCARRAY_FOREACH(filters, obj)
{
CCFilter* addFilter = static_cast
addFilter->initSprite(this);
}
m_filters->addObjectsFromArray(filters);
}
CCArray* CCSpriteWidthFilter::getFilters()
{
return m_filters;
}
[/cc]
这里重点是 draw() 函数
与 GLProgram 通信(传递参数) 的前提是 GLProgram.use()
而 CCSprite 原本的 draw()函数中 用宏 CC_NODE_DRAW_SETUP 进行了封装,并使用了默认 GLProgram, 我对于我们的 SpriteWithFilter来说,其 GLProgram 位于滤镜中,因此 draw()函数要重写
[cc lang=”c++”]
this->setShaderProgram(filter->getProgram());
do
{
ccGLEnable(m_eGLServerState);
CCAssert(getShaderProgram(), “No shader program set for this node”);
{
getShaderProgram()->use();
getShaderProgram()->setUniformsForBuiltins();
}
} while (0);
//useProgram后调用draw
filter->draw();
[/cc]
在手动切换到滤镜的program后,调用滤镜的draw ,给滤镜一个函数来传递滤镜需要的参数
之后是 CCFilter.h
[cc lang=”c++”]
#ifndef __CCFILTER__
#define __CCFILTER__
#include “cocos2d.h”
#include “CCGL.h”
#include “CCSpriteWidthFilter.h”
class CCSpriteWidthFilter;
USING_NS_CC;
class CCFilter : public CCObject
{
public:
CCFilter();
~CCFilter();
virtual void initSprite(CCSpriteWidthFilter* sprite);
virtual void draw();
CCGLProgram* getProgram();
const char* shaderName;
protected:
CCGLProgram* m_program;
void initProgram();
virtual CCGLProgram* loadShader();
virtual void setAttributes(CCGLProgram* gl);
virtual void setUniforms(CCGLProgram* gl);
CCSpriteWidthFilter* m_sprite;
};
#endif
[/cc]
initSprite 让滤镜持有这个宿主对象,原本是打算扩展RTT的,后来放弃啦
CCFilter.cpp
[cc lang=”c++”]
#include “CCFilter.h”
CCFilter::CCFilter()
: shaderName(NULL),
m_program(NULL)
{
}
CCFilter::~CCFilter()
{
}
void CCFilter::initSprite(CCSpriteWidthFilter* sprite)
{
}
void CCFilter::draw()
{
}
CCGLProgram* CCFilter::getProgram()
{
return m_program;
}
void CCFilter::initProgram()
{
CCGLProgram* program = CCShaderCache::sharedShaderCache()->programForKey(this->shaderName);
if(!program)
{
program = loadShader();
this->setAttributes(program);
CHECK_GL_ERROR_DEBUG();
program->link();
CHECK_GL_ERROR_DEBUG();
program->updateUniforms();
CHECK_GL_ERROR_DEBUG();
CCShaderCache::sharedShaderCache()->addProgram(program, this->shaderName);
program->release();
}
if(!m_program)
{
CCLOG(“initProgram id:%d”,program->getProgram());
m_program = program;
m_program->retain();
}
}
CCGLProgram* CCFilter::loadShader()
{
return NULL;
}
void CCFilter::setAttributes(CCGLProgram* gl)
{
}
void CCFilter::setUniforms(CCGLProgram* gl)
{
}
[/cc]
函数都留空,丢给子类去实现它
使用方法很简单,像这样
[cc lang=”c++”]
CCGodRayFilter* filter = CCGodRayFilter::create();
CCSpriteWidthFilter* sprite = CCSpriteWidthFilter::create(“HelloWorld.png”);
sprite->setFilter(filter);
//sprite->setAnchorPoint(ccp(0,0));
[/cc]
这个项目我会传到git上,目前只有几个预制滤镜
灰度,闪烁,颜色闪烁 和 上帝之光。 zrong 的 git 上有 饱和度,模糊等滤镜, 有空会考虑“山寨过来“, 其他滤镜在有空的时候会后续加入,比如之前写过的水波和阴影体还有HDR