cocos2d-x 2.x版本文字描邊研究01_使用shader描邊
阿新 • • 發佈:2019-01-31
由於Cocos2d-x 2.x版本對描邊支援的不好,3.X的基於Freetype的字型檔的描邊效果還是不錯的,但專案用的是舊版本引擎,又需要用到描邊字,最近也研究了幾種描邊的方法,想分享一下。
在網上找了很多種描邊的方式,各有優劣,有的描邊效果很不錯,而有的效果稍差但繪製效率更高。這篇文章講解其中一種基於Shader的描邊方法。
“雲風”大哥的ejoy2d引擎中提供了一種效率很高的描邊演算法,使用ejoy2d進行描邊時,有不錯的效果,但是我整合到了cocos2dx中,描邊效果不太明顯,可能是與生成的紋理有關係,如果對描邊的效果要求不高,可以考慮用一下這個描邊效果。
描邊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, const std::string& fontName, float fontSize, const ccColor3B& textColor, const ccColor3B& strokeColor, 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, const std::string& fontName, float fontSize, const ccColor3B& textColor, const ccColor3B& strokeColor, 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, const std::string& fontName, float fontSize, const ccColor3B& textColor, const ccColor3B& strokeColor, 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, 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"); long long int endTime = getNowTime(); long long int druation = endTime - startTime; CCLOG("drawTime %lld", druation); }
我在每一次繪製都列印了一下繪製消耗的時間,單位是毫秒,基本都近似於0~1毫秒,所以繪製效率還是很高的。
用這個shader描邊描出來的效果在2dx上的表現一般,描的很淺,下面是普通白字和白字描紅邊的對比效果圖。
這是在Ios上描邊效果,Android的效果也差不多。