由于Cocos2d-x 2.x版本对描边支持的不好,3.X的基于Freetype的字库的描边效果还是不错的,但项目用的是旧版本引擎,又需要用到描边字,最近也研究了几种描边的方法,想分享一下。
在网上找了很多种描边的方式,各有优劣,有的描边效果很不错,而有的效果稍差但绘制效率更高。这篇文章讲解其中一种基于Shader的描边方法。
“云风”大哥的ejoy2d引擎中提供了一种效率很高的描边算法,使用ejoy2d进行描边时,有不错的效果,但是我集成到了cocos2dx中,描边效果不太明显,可能是与生成的纹理有关系,如果对描边的效果要求不高,可以考虑用一下这个描边效果。
描边的原理参考云风的博客,http://blog.codingnow.com/2013/09/edge_font.html
参考的ejoy2d的描边shader,可以去github上查看源码,https://github.com/ejoy/ejoy2d
描边shader如下:
/* * LICENSE ??? */ #ifdef GL_ES precision highp float; #endif uniform sampler2D u_texture; varying vec2 v_texCoord; varying vec4 v_fragmentColor; uniform vec4 u_effectColor; void main() { float c = texture2D(u_texture,v_texCoord).w; float alpha = clamp(c,0.0,0.5) * 2.0; float color = (clamp(c,0.5,1.0) - 0.5) * 2.0; gl_FragColor.xyz = (v_fragmentColor.xyz + u_effectColor.xyz) * color; gl_FragColor.w = alpha; gl_FragColor *= v_fragmentColor.w; gl_FragColor *= v_fragmentColor.w; }
我封装了一个StrokeLabel类来对cocos2dx的CCLabelTTF进行了绘制的修改,增加了使用自有的shader进行着色的逻辑。
// // StrokeLabel.h // StrokeLabel_01 // // Created by cc on 15/1/27. // // #ifndef __StrokeLabel_01__StrokeLabel__ #define __StrokeLabel_01__StrokeLabel__ #include "CCLabelTTFLoader.h" USING_NS_CC; class StrokeLabel : public CCLabelTTF { private: //描边宽度 float m_strokeSize; //描边效果颜色 ccColor4B m_effectColor; ccColor4F m_effectColorF; //文本颜色 ccColor4F m_textColorF; ccColor4B m_textColor; //描边颜色值,传入shader的全局变量 GLuint m_uniformEffectColor; public: #pragma mark <构造 && 析构> StrokeLabel(); ~StrokeLabel(); #pragma mark <创建 && 初始化> /** * 创建描边字 * * @param content 文本内容 * @param fontName 字体 * @param fontSize 字号 * @param textColor 文本颜色 * @param strokeColor 描边颜色 * @param strokeSize 描边宽度 * * @return 描边字 */ static StrokeLabel* createWithAttribute(const std::string& content,const std::string& fontName,float fontSize,const ccColor3B& textColor,const ccColor3B& strokeColor,float strokeSize); /** * 初始化描边字 * * @param content 文本内容 * @param fontName 字体 * @param fontSize 字号 * @param textColor 文本颜色 * @param strokeColor 描边颜色 * @param strokeSize 描边宽度 * * @return true: 初始化成功 false: 初始化失败 */ bool initWithAttribute(const std::string& content,float strokeSize); /** * 更新Shader程序 */ void updateShaderProgram(); /** * 绘制 */ virtual void draw(); }; #endif /* defined(__StrokeLabel_01__StrokeLabel__) */
// // StrokeLabel.cpp // StrokeLabel_01 // // Created by cc on 15/1/27. // // #include "StrokeLabel.h" StrokeLabel::StrokeLabel() : m_strokeSize(0),m_uniformEffectColor(0) { } StrokeLabel::~StrokeLabel() { } long long int getNowTime() { struct timeval tv; gettimeofday(&tv,NULL); long long int nowTime = ((long long int)tv.tv_sec) * 1000 + tv.tv_usec / 1000; return nowTime; } /** * 创建描边字 * * @param content 文本内容 * @param fontName 字体 * @param fontSize 字号 * @param textColor 文本颜色 * @param strokeColor 描边颜色 * @param strokeSize 描边宽度 * * @return 描边字 */ StrokeLabel* StrokeLabel::createWithAttribute(const std::string& content,float strokeSize) { StrokeLabel *pRet = new StrokeLabel(); if (pRet && pRet->initWithAttribute(content,fontName,fontSize,textColor,strokeColor,strokeSize)) { pRet->autorelease(); return pRet; } CC_SAFE_DELETE(pRet); return NULL; } /** * 初始化描边字 * * @param content 文本内容 * @param fontName 字体 * @param fontSize 字号 * @param textColor 文本颜色 * @param strokeColor 描边颜色 * @param strokeSize 描边宽度 * * @return true: 初始化成功 false: 初始化失败 */ bool StrokeLabel::initWithAttribute(const std::string& content,float strokeSize) { if (!CCLabelTTF::initWithString(content.c_str(),fontName.c_str(),fontSize)) { return false; } m_textColor = ccc4(textColor.r,textColor.g,textColor.b,255); m_textColorF.r = m_textColor.r / 255.0f; m_textColorF.g = m_textColor.g / 255.0f; m_textColorF.b = m_textColor.b / 255.0f; m_textColorF.a = m_textColor.a / 255.0f; m_effectColor = ccc4(strokeColor.r,strokeColor.g,strokeColor.b,255);; m_effectColorF.r = m_effectColor.r / 255.0f; m_effectColorF.g = m_effectColor.g / 255.0f; m_effectColorF.b = m_effectColor.b / 255.0f; m_effectColorF.a = m_effectColor.a / 255.0f; this->setColor(textColor); this->updateShaderProgram(); return true; } /** * 更新Shader程序 */ void StrokeLabel::updateShaderProgram() { const GLchar* fragmentSource = (GLchar*)CCString::createWithContentsOfFile(CCFileUtils::sharedFileUtils()->fullPathForFilename("Label_outline1.frag").c_str())->getCString(); CCGLProgram* pProgram = new CCGLProgram(); pProgram->initWithVertexShaderByteArray(ccPositionTextureColor_vert,fragmentSource); setShaderProgram(pProgram); pProgram->release(); CHECK_GL_ERROR_DEBUG(); getShaderProgram()->addAttribute(kCCAttributeNamePosition,kCCVertexAttrib_Position); getShaderProgram()->addAttribute(kCCAttributeNameColor,kCCVertexAttrib_Color); getShaderProgram()->addAttribute(kCCAttributeNameTexCoord,kCCVertexAttrib_TexCoords); CHECK_GL_ERROR_DEBUG(); getShaderProgram()->link(); CHECK_GL_ERROR_DEBUG(); getShaderProgram()->updateUniforms(); m_uniformEffectColor = glGetUniformLocation(getShaderProgram()->getProgram(),"u_effectColor"); } void StrokeLabel::draw() { long long int startTime = getNowTime(); CC_PROFILER_START_CATEGORY(kCCProfilerCategorySprite,"CCSprite - draw"); CCAssert(!m_pobBatchNode,"If CCSprite is being rendered by CCSpriteBatchNode,CCSprite#draw SHOULD NOT be called"); CC_NODE_DRAW_SETUP(); ccGLBlendFunc( m_sBlendFunc.src,m_sBlendFunc.dst ); getShaderProgram()->use(); getShaderProgram()->setUniformLocationWith4f(m_uniformEffectColor,m_effectColorF.r,m_effectColorF.g,m_effectColorF.b,m_effectColorF.a); getShaderProgram()->setUniformsForBuiltins(); 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,(void*)(offset + diff)); // color diff = offsetof( ccV3F_C4B_T2F,colors); glVertexAttribPointer(kCCVertexAttrib_Color,4,GL_UNSIGNED_BYTE,GL_TRUE,(void*)(offset + diff)); glDrawArrays(GL_TRIANGLE_STRIP,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,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+s.height),ccp(offsetPix.x,offsetPix.y+s.height) }; ccDrawPoly(vertices,true); #endif // CC_SPRITE_DEBUG_DRAW CC_INCREMENT_GL_DRAWS(1); CC_PROFILER_STOP_CATEGORY(kCCProfilerCategorySprite,"CCSprite - draw"); long long int endTime = getNowTime(); long long int druation = endTime - startTime; CCLOG("drawTime %lld",druation); }
我在每一次绘制都打印了一下绘制消耗的时间,单位是毫秒,基本都近似于0~1毫秒,所以绘制效率还是很高的。
用这个shader描边描出来的效果在2dx上的表现一般,描的很浅,下面是普通白字和白字描红边的对比效果图。
描边的代码在这里下载:http://download.csdn.net/download/oktears/8403783
本文由CC原创总结,如需转载请注明出处:http://blog.csdn.net/oktears/article/details/43200757
原文链接:/cocos2dx/344650.html