轉載地址:http://www.taikr.com/group/2/thread/1606
關於cocos2dx 字體描邊的實現,不考慮效果和效率的話,是有三種方式:
① 利用CCLabelTTF制作文字描邊和陰影效果
② 利用CCRenderTexture渲染文理的方式生成帶有描邊效果的文字
③ 利用shader來實現,使用cocos2dx中CCGLProgram類與OpenGl繪圖機制中的着色器交互來實現
第三種方式我沒試過,不過基於shader的強大特效功能,實現起來是沒問題的,后面我還會寫一篇關於shader來實現改變紋理顏色做特殊效果的文章。現在我們主要研究一下前兩種。前兩種方式從原理來說都是利用多個CCLabelTTF的重疊來做的,只不過第二種方式是使用CCLabelTTF繪制了一個帶有描邊效果的文字圖片。
利用CCLabelTTF制作文字描邊和陰影效果
網上大量博客都講了這種方式,寫的很好,這里給一個鏈接:http://www.taikr.com/
原理就是創建了4個描邊用的CCLabelTTF和一個正文主體CCLabelTTF,先畫描邊的label,顏色設置成要用的描邊色,分別在目標位置出做上下左右4個方向偏移,然后在蓋上我們的正文主體label即可。
【cpp】 view plaincopy在CODE上查看代碼片派生到我的代碼片
/*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->setPosition(ccp(center->getContentSize().width*0.5-lineWidth, center->getContentSize().height*0.5));
left->setColor(ccBLACK);
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;
} <span style="font-family: verdana, Arial, Helvetica, sans-serif;"> </span>
陰影效果就更簡單了,只需要兩個label,一個陰影用的label和正文label,只需要設置陰影label:顏色、透明度和位置偏移即可。
【cpp】 view plaincopy在CODE上查看代碼片派生到我的代碼片
/*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;
} <span style="font-family: verdana, Arial, Helvetica, sans-serif;"> </span>
這里提到一點,就是具體要創建幾個label是不一定的,創建的越多,效果是描邊越粗越亮但是效率越低,比如創建了40個描邊用的label,4個方向各10個,互相之間有微小的偏移不完全重疊,這樣的效果是label更加的粗且亮,陰影效果等同。
利用CCRenderTexture渲染文理的方式生成帶有描邊效果的文字
這種方式難點在於,很多人不了解OpenGl的繪圖着色機制,不過也不要緊,這里只用到了一點着色知識,就是顏色的混合,這個大多數的人都知道,紅色和綠色混合會變成黃色,黑色和任何顏色混合還是黑色(0*任何數=0),任何顏色和白色的混合不改變顏色。
繪圖時,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}的混合機制。
好了,那么我們來了解一下cocos2dx的渲染是怎么做的。
在cocos2dx中的可渲染的節點都是CCNode的子類,如精靈、場景、文本,CCNode有一個渲染混色的函數glBlendFunc()和setBlendFunc(),操作的是ccBlendFunc,ccBlendFunc有兩個調節變量,ccBlendFunc func = { GL_SRC_ALPHA, GL_ONE},這個調節變量就是前面說的系數:
GL_SRC_ALPHA:表示使用源顏色的alpha值來作為源因子。
GL_ONE: 表示使用1.0作為因子,實際上相當於完全的使用了這種顏色參與混合運算。
這類參數有很多,自己可以跟蹤引擎代碼內部去看。
再介紹一下,CCRenderTexture生成描邊字體的原理,CCRenderTexture可以理解為一張紋理畫布,我們可以再這張畫布上塗鴉,當我們收工的時候,這張畫就算畫好了,即生成一張紋理。因此,我們是在畫布上,用一個label在不同的位置不斷的去畫,類似第一種方式,我們在畫布上繪制label的時候,在目標位置處的360個方向畫多個label,重疊,最后通過畫布就生成帶有描邊文字的一張紋理圖,然后用這張紋理圖生成一個精靈放到場景里,完畢。
下面先把代碼貼上來:
【cpp】 view plaincopy在CODE上查看代碼片派生到我的代碼片
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;
}
除此之外,還有一種方式就是Shader,這個還在研究,不過它的特效功能着實強大,以后逐步學習。