由于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.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);
}
用这个shader描边描出来的效果在2dx上的表现一般,描的很浅,下面是普通白字和白字描红边的对比效果图。
这是在Ios上描边效果,Android的效果也差不多。
描边的代码在这里下载:http://download.csdn.net/download/oktears/8403783
本文由CC原创总结,如需转载请注明出处:http://blog.csdn.net/oktears/article/details/43200757