最新文章
Cocos2d-x游戏开发实例详解7:对象释放时机
03-25 13:59
Cocos2d-x游戏开发实例详解6:自动释放池
03-25 13:55
Cocos2d-x游戏开发实例详解5:神奇的自动释放
03-25 13:49
Cocos2d-x游戏开发实例详解4:游戏主循环
03-25 13:44
Cocos2d-x游戏开发实例详解3:无限滚动地图
03-25 13:37
Cocos2d-x游戏开发实例详解2:开始菜单续
03-25 13:32
cocos2dx 描边原理
如果不考虑效率,我们可以通过以下三种方式实现 Cocos2d-x 的描边效果:
- 利用
CCLabelTTF制作文字描边和阴影效果。 - 利用
CCRenderTexture渲染纹理的方式生成带有描边效果的文字。 - 利用 Shader 实现,通过 Cocos2d-x 中
CCGLProgram类与 OpenGL 绘图机制中的着色器交互来达成。
本文将主要探讨前两种方法,从原理上来说,这两种方式都是利用多个 CCLabelTTF 的重叠来实现的,只不过第二种方式是使用 CCLabelTTF 绘制出一个带有描边效果的文字图片。
利用 CCLabelTTF 制作文字描边和阴影效果
文字描边原理
该方法的原理是创建 4 个用于描边的 CCLabelTTF 和 1 个正文主体 CCLabelTTF。首先绘制描边的 label,将其颜色设置为所需的描边色,并分别在目标位置的上下左右 4 个方向进行偏移,最后覆盖上正文主体 label。
以下是实现文字描边的代码:
/*
* string 文本
* fontName 文本字体类型
* fontSize 文本大小
* color3 文本颜色
* lineWidth 所描边的宽度
*/
CCLabelTTF* HelloWorld::textAddStroke(const char* string, const char* fontName, float fontSize, const ccColor3B &color3, float lineWidth)
{
/* 正文 CCLabelTTF */
CCLabelTTF* center = CCLabelTTF::create(string, fontName, fontSize);
center->setColor(color3);
/* 描边 CCLabelTTF 上 */
CCLabelTTF* up = CCLabelTTF::create(string, fontName, fontSize);
up->setColor(ccBLACK);
up->setPosition(ccp(center->getContentSize().width * 0.5, center->getContentSize().height * 0.5 + lineWidth));
center->addChild(up, -1);
/* 描边 CCLabelTTF 下 */
CCLabelTTF* down = CCLabelTTF::create(string, fontName, fontSize);
down->setColor(ccBLACK);
down->setPosition(ccp(center->getContentSize().width * 0.5, center->getContentSize().height * 0.5 - lineWidth));
center->addChild(down, -1);
/* 描边 CCLabelTTF 左 */
CCLabelTTF* left = CCLabelTTF::create(string, fontName, fontSize);
left->setColor(ccBLACK);
left->setPosition(ccp(center->getContentSize().width * 0.5 - lineWidth, center->getContentSize().height * 0.5));
center->addChild(left, -1);
/* 描边 CCLabelTTF 右 */
CCLabelTTF* right = CCLabelTTF::create(string, fontName, fontSize);
right->setColor(ccBLACK);
right->setPosition(ccp(center->getContentSize().width * 0.5 + lineWidth, center->getContentSize().height * 0.5));
center->addChild(right, -1);
return center;
}
文字阴影效果
阴影效果的实现更为简单,只需要两个 label,即一个用于阴影的 label 和一个正文 label。只需设置阴影 label 的颜色、透明度和位置偏移即可。
以下是实现文字阴影的代码:
/*
* string 文本
* fontName 文本字体类型
* fontSize 文本大小
* color3 文本颜色
* shadowSize 阴影大小
* shadowOpacity 阴影透明度
*/
CCLabelTTF* HelloWorld::textAddShadow(const char* string, const char* fontName, float fontSize, const ccColor3B &color3, float shadowSize, float shadowOpacity)
{
/* 阴影 label */
CCLabelTTF* shadow = CCLabelTTF::create(string, fontName, fontSize);
shadow->setColor(ccBLACK);
shadow->setOpacity(shadowOpacity);
/* 正文 label */
CCLabelTTF* center = CCLabelTTF::create(string, fontName, fontSize);
center->setColor(color3);
center->setPosition(ccp(shadow->getContentSize().width * 0.5 - shadowSize, shadow->getContentSize().height * 0.5 + shadowSize));
shadow->addChild(center);
return shadow;
}
需要注意的是,具体创建的 label 数量并非固定。创建的 label 越多,描边效果越粗越亮,但效率会越低。例如,创建 40 个描边用的 label,4 个方向各 10 个,且互相之间有微小偏移不完全重叠,这样的效果是 label 更加粗且亮,阴影效果同理。
利用 CCRenderTexture 渲染纹理的方式生成带有描边效果的文字
OpenGL 颜色混合机制
很多人对 OpenGL 的绘图着色机制不太了解,但这里仅涉及到颜色混合的知识。在绘图时,OpenGL 会取出源颜色和目标颜色,并分别乘以一个系数(源颜色乘以的系数称为“源因子”,目标颜色乘以的系数称为“目标因子”),然后进行运算得到新的颜色值,并按照新的颜色进行绘制。
例如,在 RGB 格式中,原来的颜色是 (255, 0, 0)(红色),目标颜色是 (0, 255, 0)(绿色),若系数为 {1, 0},运算方式为颜色叠加,则新的颜色为 (255, 0, 0) * 1 + (0, 255, 0) * 0 = (255, 0, 0)(红色),即完全不使用目标颜色的值,相当于颜色未改变。在 {1, 0} 的混合机制中,源因子为 1,目标因子为 0。
Cocos2d-x 渲染混色
在 Cocos2d-x 中,可渲染的节点都是 CCNode 的子类,如精灵、场景、文本等。CCNode 有渲染混色的函数 glBlendFunc() 和 setBlendFunc(),操作的是 ccBlendFunc。ccBlendFunc 有两个调节变量,例如 ccBlendFunc func = {GL_SRC_ALPHA, GL_ONE},这两个调节变量就是前面所说的系数:
GL_SRC_ALPHA:表示使用源颜色的 alpha 值作为源因子。GL_ONE:表示使用 1.0 作为因子,实际上相当于完全使用该颜色参与混合运算。
这类参数有很多,可跟踪引擎代码内部查看。
CCRenderTexture 生成描边字体的原理
CCRenderTexture 可理解为一张纹理画布,我们可以在这张画布上进行绘制,绘制完成后即可生成一张纹理。具体做法是,在画布上使用一个 label 在不同位置不断绘制,类似于第一种方法,在目标位置的 360 个方向绘制多个 label 并使其重叠,最后通过画布生成带有描边文字的纹理图,再用这张纹理图生成一个精灵放到场景中。
以下是实现代码:
CCTexture2D* FlyBloodLabel::createStrokeTexture(const char* value, float strokeValue, ccColor3B color)
{
// float fontSize = m_fontSize - 2 * strokeSize;
/* 创建一个 CCLabelTTF,含有期望字体样式,作为画笔 */
CCLabelTTF *label = CCLabelTTF::create(value, "Arial", EFFECT_LABEL_FONT_SIZE);
/* 通过 label 的大小来设置最终生成的纹理图片的大小,strokeValue 为描边字体的偏移量,影响粗细 */
CCSize textureSize = label->getContentSize();
textureSize.width += 2 * strokeValue;
textureSize.height += 2 * strokeValue;
/* 监测 OpenGl 的错误状态 */
glGetError();
/* 创建一张纹理画布 */
CCRenderTexture *rt = CCRenderTexture::create(textureSize.width, textureSize.height);
if (!rt)
{
CCLog("create render texture failed !!!!");
addChild(label);
return 0;
}
/* 设置描边的颜色 */
label->setColor(color);
/*
* 拿到源文字的混色机制,存储以备恢复,并设置新的目标混色机制
* 混色机制设为:源颜色透明度(影响亮度)和目标颜色(影响颜色)
*/
ccBlendFunc originalBlend = label->getBlendFunc();
ccBlendFunc func = { GL_SRC_ALPHA, GL_ONE };
label->setBlendFunc(func);
/* 这是自定义的一些调整,倾斜了一点 */
label->setAnchorPoint(ccp(0.5, 0.5));
label->setRotationX(15);
/* 张开画布,开始绘画 */
rt->begin();
for (int i = 0; i < 360; i += 5) // 每变化 5 度绘制一张
{
float r = CC_DEGREES_TO_RADIANS(i); // 度数格式的转换
label->setPosition(ccp(textureSize.width * 0.5f + sin(r) * strokeValue, textureSize.height * 0.5f + cos(r) * strokeValue));
/* CCRenderTexture 的用法,在 begin 和 end 之间 visit 的纹理,都会画在 CCRenderTexture 里面 */
label->visit(); // 画了一次该 label
}
/* 恢复原始的 label 并绘制在最上层 */
label->setColor(ccWHITE);
label->setBlendFunc(originalBlend);
label->setPosition(ccp(textureSize.width * 0.5f, textureSize.height * 0.5f));
label->visit();
/* 在画布上绘制结束,此时会生成一张纹理 */
rt->end();
/* 取出生成的纹理,添加抗锯齿打磨,并返回 */
CCTexture2D *texture = rt->getSprite()->getTexture();
texture->setAntiAliasTexParameters(); // setAliasTexParameters();
return texture;
}