1. 程式人生 > >【Cocos2d-x】圖片描邊的一種比較好的shader實現方法

【Cocos2d-x】圖片描邊的一種比較好的shader實現方法

轉載: http://blog.csdn.net/u011281572/article/details/44999609

圖片描邊需求如下:

1. 可指定描邊寬度2. 可指定描邊顏色3. 可用於字型

圖片描邊我所知道的方式有以下幾種:

1. Cocos2d-x 3.x中,字型用FreeType庫,字型描邊可以用FreeType自帶的描邊功能,實際效果沒測過,但只能用於字型。

2. 用RenderTexture,方法大概是把原圖做一圈的偏移,渲染到同一張紋理上,他們相隔中心點的距離都是r,最後再把自己渲染到中間,核心程式碼大概這樣:

  1. rt->begin();  
  2. for(int i = 0; i < 360; i += 15)  
  3. {  
  4.    float rad = CC_DEGREES_TO_RADIANS(i);  
  5.    m_label->setPosition(ccp(  
  6.    textureSize.width * 0.5f + sin(rad) * r,  
  7.    textureSize.height * 0.5f + cos(rad) * r));  
  8.    m_label->visit();  
  9. }  
  10. m_label->setColor(col);  
  11. m_label->setBlendFunc(originalBlend);  
  12. m_label->setPosition(ccp(textureSize.width * 0.5f, textureSize.height * 0.5f));  
  13. m_label->visit();  
  14. rt->end();  

這種方法可以作為一個比較好的解決方案,但是我認為這種方式在生成描邊圖片時,需要繪製多個圖片,效率不是很好。

3. Cocos2d-x 3.x的ShaderTest有個描邊的例子,是用shader實現的,不過那個程式有些不友好:描邊寬度不是傳畫素進去,而是一個0~1的數字,是一個比例,就是說大的圖片描邊大,小的圖片描邊小,而且同一個圖片不同位置的描邊也寬窄不一(這在長比寬大很多的圖片尤其明顯),而且描邊的顏色也深淺不一。

以下我分享一種我認為比較好的描邊演算法

在片段著色器裡面,對於每個畫素:1. 如果它是不透明的畫素,則不管,維持原本顏色;2. 如果透明,是360度判斷它四周有沒有不透明的畫素,如果有,則把它設成描邊顏色,否則保持透明。

我為程式碼加了比較詳細的註釋,希望大家能理解微笑

stroke.fsh:描邊片段著色器

  1. varying vec4 v_fragmentColor; // vertex shader傳入,setColor設定的顏色
  2. varying vec2 v_texCoord; // 紋理座標
  3. uniform float outlineSize; // 描邊寬度,以畫素為單位
  4. uniform vec3 outlineColor; // 描邊顏色
  5. uniform vec2 textureSize; // 紋理大小(寬和高),為了計算周圍各點的紋理座標,必須傳入它,因為紋理座標範圍是0~1
  6. uniform vec3 foregroundColor; // 主要用於字型,可傳可不傳,不傳預設為白色
  7. // 判斷在這個角度上距離為outlineSize那一點是不是透明
  8. int getIsStrokeWithAngel(float angel)  
  9. {  
  10.     int stroke = 0;  
  11.     float rad = angel * 0.01745329252; // 這個浮點數是 pi / 180,角度轉弧度
  12.     float a = texture2D(CC_Texture0, vec2(v_texCoord.x + outlineSize * cos(rad) / textureSize.x, v_texCoord.y + outlineSize * sin(rad) / textureSize.y)).a; // 這句比較難懂,outlineSize * cos(rad)可以理解為在x軸上投影,除以textureSize.x是因為texture2D接收的是一個0~1的紋理座標,而不是畫素座標
  13.     if (a >= 0.5)// 我把alpha值大於0.5都視為不透明,小於0.5都視為透明
  14.     {  
  15.         stroke = 1;  
  16.     }  
  17.     return stroke;  
  18. }  
  19. void main()  
  20. {  
  21.     vec4 myC = texture2D(CC_Texture0, vec2(v_texCoord.x, v_texCoord.y)); // 正在處理的這個畫素點的顏色
  22.     myC.rgb *= foregroundColor;  
  23.     if (myC.a >= 0.5) // 不透明,不管,直接返回
  24.     {  
  25.         gl_FragColor = v_fragmentColor * myC;  
  26.         return;  
  27.     }  
  28.     // 這裡肯定有朋友會問,一個for迴圈就搞定啦,怎麼這麼麻煩!其實我一開始也是用for的,但後來在安卓某些機型(如小米4)會直接崩潰,查詢資料發現OpenGL es並不是很支援迴圈,while和for都不要用
  29.     int strokeCount = 0;  
  30.     strokeCount += getIsStrokeWithAngel(0.0);  
  31.     strokeCount += getIsStrokeWithAngel(30.0);  
  32.     strokeCount += getIsStrokeWithAngel(60.0);  
  33.     strokeCount += getIsStrokeWithAngel(90.0);  
  34.     strokeCount += getIsStrokeWithAngel(120.0);  
  35.     strokeCount += getIsStrokeWithAngel(150.0);  
  36.     strokeCount += getIsStrokeWithAngel(180.0);  
  37.     strokeCount += getIsStrokeWithAngel(210.0);  
  38.     strokeCount += getIsStrokeWithAngel(240.0);  
  39.     strokeCount += getIsStrokeWithAngel(270.0);  
  40.     strokeCount += getIsStrokeWithAngel(300.0);  
  41.     strokeCount += getIsStrokeWithAngel(330.0);  
  42.     if (strokeCount > 0) // 四周圍至少有一個點是不透明的,這個點要設成描邊顏色
  43.     {  
  44.         myC.rgb = outlineColor;  
  45.         myC.a = 1.0;  
  46.     }  
  47.     gl_FragColor = v_fragmentColor * myC;  
  48. }  


我的utilShader.cpp:

  1. constchar* shaderNameStroke = "ShjyShader_Stroke";  
  2. namespace utilShader  
  3. {  
  4. // 傳入描邊寬度(畫素為單位),描邊顏色,圖片大小,獲得GLProgramState
  5.     cocos2d::GLProgramState* getStrokeProgramState( float outlineSize, cocos2d::Color3B outlineColor, cocos2d::Size textureSize, cocos2d::Color3B foregroundColor/* = cocos2d::Color3B::WHITE*/ )  
  6.     {  
  7.         auto glprogram = GLProgramCache::getInstance()->getGLProgram(shaderNameStroke);  
  8.         if (!glprogram)  
  9.         {  
  10.             std::string fragmentSource = FileUtils::getInstance()->getStringFromFile(FileUtils::getInstance()->fullPathForFilename("shaders/stroke.fsh"));  
  11.             glprogram = GLProgram::createWithByteArrays(ccPositionTextureColor_noMVP_vert, fragmentSource.c_str());  
  12.             GLProgramCache::getInstance()->addGLProgram(glprogram, shaderNameStroke);  
  13.         }  
  14.         auto glprogramState = GLProgramState::create(glprogram);  
  15.         glprogramState->setUniformFloat("outlineSize", outlineSize);  
  16.         glprogramState->setUniformVec3("outlineColor", Vec3(outlineColor.r / 255.0f, outlineColor.g / 255.0f, outlineColor.b / 255.0f));  
  17.         glprogramState->setUniformVec2("textureSize", Vec2(textureSize.width, textureSize.height));  
  18.         glprogramState->setUniformVec3("foregroundColor", Vec3(foregroundColor.r / 255.0f, foregroundColor.g / 255.0f, foregroundColor.b / 255.0f));  
  19.         return glprogramState;  
  20.     }  
  21. }  



呼叫的地方:
  1. Sprite* spr = Sprite::create("elephant1_Diffuse.png");  
  2. spr->setPosition(200, 200);  
  3. spr->setGLProgramState(utilShader::getStrokeProgramState(5, Color3B::GREEN, spr->getContentSize()));  
  4. this->addChild(spr, 1);  

效果:


效果還算是比較好的,經測試,此演算法在安卓多個機型上也表現良好。

這樣一套完整的描邊演算法就完成了,如果描述有不當之處,或有更優方法,歡迎吐槽。

-------------------------------------------------------------------------------------------------------------------------------------------------------------------

謝謝complex_ok的吐槽,應該預先計算好sin和cos值,無需每次計算。優化後的 stroke.fsh 如下:

  1. varying vec4 v_fragmentColor;  
  2. varying vec2 v_texCoord;  
  3. uniform float outlineSize;  
  4. uniform vec3 outlineColor;  
  5. uniform vec2 textureSize;  
  6. uniform vec3 foregroundColor;  
  7. constfloat cosArray[12] = {1, 0.866, 0.5, 0, -0.5, -0.866, -0.1, -0.866, -0.5, 0, 0.5, 0.866};  
  8. constfloat sinArray[12] = {0, 0.5, 0.866, 1, 0.866, 0.5, 0, -0.5, -0.866, -1, -0.866, -0.5};  
  9. int getIsStrokeWithAngelIndex(int index)  
  10. {  
  11.     int stroke = 0;  
  12.     float a = texture2D(CC_Texture0, vec2(v_texCoord.x + outlineSize * cosArray[index] / textureSize.x, v_texCoord.y + outlineSize * sinArray[index] / textureSize.y)).a;  
  13.     if (a >= 0.5)  
  14.     {  
  15.         stroke = 1;  
  16.     }  
  17.     return stroke;  
  18. }  
  19. void main()  
  20. {  
  21.     vec4 myC = texture2D(CC_Texture0, vec2(v_texCoord.x, v_texCoord.y));  
  22.     myC.rgb *= foregroundColor;  
  23.     if (myC.a >= 0.5)  
  24.     {  
  25.         gl_FragColor = v_fragmentColor * myC;  
  26.         return;  
  27.     }  
  28.     int strokeCount = 0;  
  29.     strokeCount += getIsStrokeWithAngelIndex(0);  
  30.     strokeCount += getIsStrokeWithAngelIndex(1);  
  31.     strokeCount += getIsStrokeWithAngelIndex(2);  
  32.     strokeCount += getIsStrokeWithAngelIndex(3);  
  33.     strokeCount += getIsStrokeWithAngelIndex(4);  
  34.     strokeCount += getIsStrokeWithAngelIndex(5);  
  35.     strokeCount += getIsStrokeWithAngelIndex(6);  
  36.     strokeCount += getIsStrokeWithAngelIndex(7);  
  37.     strokeCount += getIsStrokeWithAngelIndex(8);  
  38.     strokeCount += getIsStrokeWithAngelIndex(9);  
  39.     strokeCount += getIsStrokeWithAngelIndex(10);  
  40.     strokeCount += getIsStrokeWithAngelIndex(11);  
  41.     bool stroke = false;  
  42.     if (strokeCount > 0)  
  43.     {  
  44.         stroke = true;  
  45.     }  
  46.     if (stroke)  
  47.     {  
  48.         myC.rgb = outlineColor;  
  49.         myC.a = 1.0;  
  50.     }  
  51.     gl_FragColor = v_fragmentColor * myC;  
  52. }  

相關推薦

Cocos2d-x圖片比較shader實現方法

轉載: http://blog.csdn.net/u011281572/article/details/44999609 圖片描邊需求如下: 1. 可指定描邊寬度2. 可指定描邊顏色3. 可用於字型 圖片描邊我所知道的方式有以下幾種: 1. Cocos2d-x 3.x中,

Cocos2d-x 3.0 基礎系列 各類回調函數寫法匯總

void ont white fort instance gles dir ner mdi 一、button回調 1. Lambda 表達式,C++11 Lambda 賦予了Cocos2d-x 3.0創建回調函數的靈活性。 auto it

Cocos2d-x坐標系和圖層

pac 多說 pan art world 地圖 分辨 play instance 在Cocos2D-X中,存在四種坐標系: 1、OpenGL坐標系:該坐標系原點在屏幕左下角。x軸向右,y軸向上。這也就是cocos2dx中用到的坐標系所以沒啥好說的。 2、屏幕坐標系(UI

Cocos2d-x截圖分享功能

Cocos2d-x截圖實現 #include "cocos2d.h" USING_NS_CC; // 設定紋理寬、高、畫素質量 CCRenderTexture* tx = CCRenderTexture::create(CCDirector::sharedDirec

cocos2d-x 精靈新增效果

學習cocos2d-x 以來一直對裡面的shader部分感興趣,今天正好花了點時間來研究一下精靈的描邊效果。 主要參考了子龍山人大神的TestCpp裡面例子並結合自己的理解,實現相對比較簡單。主要是根據引擎內部自帶的shader相關程式碼來實現的。 好了廢話不多說看程式碼:

Cocos2D-X 初窺門徑(1) 製作一個動態的精靈

原理: Cocos2D中有個導演控制整個遊戲流程,導演將場景新增到螢幕上,場景中有各種各樣的演員。 先通過顯示一張圖片來看看Cocos2D遊戲的流程: AppDelegate.cpp bool AppDelegate::applicationDidFinishLaunch

Cocos2d-x之編譯so檔案出錯

現在用的是cocos2d-x3.6版本,這個版本新建的工程第一次在eclipse上跑的時候,會出現找不到Cocos2d-x在Android平臺的Java類,錯誤如圖: 這些類位於:<遊戲工程路

Cocos2D-X 初窺門徑(5)CCAction:動作

//移動到,1s, pRole->runAction(CCMoveTo::create(1,ccp(300,300))); //跳躍到,1s,高度50,分為5步 pRole->runAction(CCJumpTo::create(1,ccp(300,300),50,5)); //放

Cocos2d-x物理引擎使用入門

相關概念 什麼是物理引擎? 科學模型:科學研究中對事物的合理簡化。 物理引擎是一個計算機程式模擬牛頓力學模型,使用質量、速度、摩擦力和空氣阻力等變數。 可以用來預測這種不同情況下的效果。它主要用在科學模擬和電子遊戲中。 一般,物理引擎只負責物理計算,而不進行畫面渲染。

Cocos2D-X 初窺門徑(10)解決中文亂碼

用資原始檔儲存字串,格式UTF-8,這樣讀取xml檔案,然後顯示就不會亂碼了。 這樣做還有一個好處就是方便以後國際化。 xml檔案: <dict> <key>Hello</key> <string>Hi~可以正常顯示中

cocos2d-x建構函式與初始化

出自圖書《製作自己的捕魚達人》 在cocos2d-x中建立物件的方法與C++開發者的習慣不同,在C++中,我們只需要呼叫類的建構函式即可建立一個物件,即可直接建立一個棧上的值物件,        也可以使用new操作符建立一個指標,指向堆上的物件,而在cocos2d-

Cocos2D-X 初窺門徑(2) 場景的切換及特效

//CCTransitionJumpZoom::transitionWithDuration(t, s);//跳躍式,本場景先會縮小,然後跳躍進來  //CCTransitionFade::transitionWithDuration(t, s);//淡出淡入,原場景

cocos2d-xcocos2d-x 3.2 在 NDK10下編譯出錯的解決方法

最近使用cocos2d-x 3.2 在NDK10 版本下打包APK,出現了一個特別奇怪的錯誤。 1. /Users/minggo/SourceCode/cocos2d-x/build/../cocos/./3d/CCBundleReader.cpp:94:

HLSDK系列怎麽增加新實體

sta fun class 使用 pen 關聯 creat bsp cnblogs 你平常肯定接觸到很多比如 info_player_start hostage info_target 之類的實體,這裏就解釋一下怎麽創建一種新的實體。 首先建立一個新的 .h 文件(當然你寫

問底伍藝:基於Rsync演算法的資料庫備份方案設計

根據容災備份系統對備份類別的要求程度,資料庫備份系統可以分為資料級備份和應用級備份。資料備份是指建立一個異地的資料備份系統,該系統是對原本地系統關鍵應用資料實時複製。當出現故障時,可由異地資料系統迅速恢復本地資料從而保證業務的連續性。應用級備份比資料備份層次更高,即在異地建

學習筆記65~百度看到的比較的Android原始碼學習路徑

我幹了3年Android sdk開發,覺得到了瓶勁沒法更進一步,於是花了一年多點時間,大概摸到點門徑。根據前輩的經驗,Android底層完全入門需要兩年。    先說下我的入門過程:    第零步,下載原始碼,我下的4.2的,框架層原始碼10G,核心2G多,ctags給框架層建的標籤檔案都有600M,當時

opencv學習矩陣CvMat的兩宣告和初始化方法

double a[9]={1,2,3;4,5,6;7,8,9} //方式一:直接宣告 CvMat mat_01; //矩陣變數 mat_01 = cvMat(3,3,CV_64FC1

Qt之自定義搜索框——QLineEdit裏增加一個Layout,還不影響正常輸入文字(好像是比較通吃的方法

too 步驟 set box 文本 csdn sub void 鼠標 簡述 關於搜索框,大家都經常接觸。例如:瀏覽器搜索、Windows資源管理器搜索等。 當然,這些對於Qt實現來說毫無壓力,只要思路清晰,分分鐘搞定。 方案一:調用QLineEdit現

快速卷積實現方法

本文基於論文Fast Algorithms for Convolutional Neural Networks,並且整理了相關知識點。 我們首先了解一下傅立葉變換: 對於連續型傅立葉變換的頻域核時域轉換公式: 對於離散的傅立葉變換: 關於傅立葉的理解 可以看這篇文章

稀疏矩陣的實現方法

本文簡單描述了一種稀疏矩陣的實現方式,並與一般矩陣的實現方式做了效能和空間上的對比 矩陣一般以二維陣列的方式實現,程式碼來看大概是這個樣子: // C# public class Matrix { // methods // elements