【Cocos2d-x】圖片描邊的一種比較好的shader實現方法
轉載: http://blog.csdn.net/u011281572/article/details/44999609
圖片描邊需求如下:
1. 可指定描邊寬度2. 可指定描邊顏色3. 可用於字型
圖片描邊我所知道的方式有以下幾種:
1. Cocos2d-x 3.x中,字型用FreeType庫,字型描邊可以用FreeType自帶的描邊功能,實際效果沒測過,但只能用於字型。
2. 用RenderTexture,方法大概是把原圖做一圈的偏移,渲染到同一張紋理上,他們相隔中心點的距離都是r,最後再把自己渲染到中間,核心程式碼大概這樣:
- rt->begin();
-
for(int i = 0; i < 360; i += 15)
- {
- float rad = CC_DEGREES_TO_RADIANS(i);
- m_label->setPosition(ccp(
- textureSize.width * 0.5f + sin(rad) * r,
- textureSize.height * 0.5f + cos(rad) * r));
- m_label->visit();
- }
- m_label->setColor(col);
- m_label->setBlendFunc(originalBlend);
-
m_label->setPosition(ccp(textureSize.width * 0.5f, textureSize.height * 0.5f));
- m_label->visit();
- rt->end();
這種方法可以作為一個比較好的解決方案,但是我認為這種方式在生成描邊圖片時,需要繪製多個圖片,效率不是很好。
3. Cocos2d-x 3.x的ShaderTest有個描邊的例子,是用shader實現的,不過那個程式有些不友好:描邊寬度不是傳畫素進去,而是一個0~1的數字,是一個比例,就是說大的圖片描邊大,小的圖片描邊小,而且同一個圖片不同位置的描邊也寬窄不一(這在長比寬大很多的圖片尤其明顯),而且描邊的顏色也深淺不一。以下我分享一種我認為比較好的描邊演算法:
在片段著色器裡面,對於每個畫素:1. 如果它是不透明的畫素,則不管,維持原本顏色;2. 如果透明,是360度判斷它四周有沒有不透明的畫素,如果有,則把它設成描邊顏色,否則保持透明。
我為程式碼加了比較詳細的註釋,希望大家能理解
stroke.fsh:描邊片段著色器
- varying vec4 v_fragmentColor; // vertex shader傳入,setColor設定的顏色
- varying vec2 v_texCoord; // 紋理座標
- uniform float outlineSize; // 描邊寬度,以畫素為單位
- uniform vec3 outlineColor; // 描邊顏色
- uniform vec2 textureSize; // 紋理大小(寬和高),為了計算周圍各點的紋理座標,必須傳入它,因為紋理座標範圍是0~1
- uniform vec3 foregroundColor; // 主要用於字型,可傳可不傳,不傳預設為白色
- // 判斷在這個角度上距離為outlineSize那一點是不是透明
- int getIsStrokeWithAngel(float angel)
- {
- int stroke = 0;
- float rad = angel * 0.01745329252; // 這個浮點數是 pi / 180,角度轉弧度
- 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的紋理座標,而不是畫素座標
- if (a >= 0.5)// 我把alpha值大於0.5都視為不透明,小於0.5都視為透明
- {
- stroke = 1;
- }
- return stroke;
- }
- void main()
- {
- vec4 myC = texture2D(CC_Texture0, vec2(v_texCoord.x, v_texCoord.y)); // 正在處理的這個畫素點的顏色
- myC.rgb *= foregroundColor;
- if (myC.a >= 0.5) // 不透明,不管,直接返回
- {
- gl_FragColor = v_fragmentColor * myC;
- return;
- }
- // 這裡肯定有朋友會問,一個for迴圈就搞定啦,怎麼這麼麻煩!其實我一開始也是用for的,但後來在安卓某些機型(如小米4)會直接崩潰,查詢資料發現OpenGL es並不是很支援迴圈,while和for都不要用
- int strokeCount = 0;
- strokeCount += getIsStrokeWithAngel(0.0);
- strokeCount += getIsStrokeWithAngel(30.0);
- strokeCount += getIsStrokeWithAngel(60.0);
- strokeCount += getIsStrokeWithAngel(90.0);
- strokeCount += getIsStrokeWithAngel(120.0);
- strokeCount += getIsStrokeWithAngel(150.0);
- strokeCount += getIsStrokeWithAngel(180.0);
- strokeCount += getIsStrokeWithAngel(210.0);
- strokeCount += getIsStrokeWithAngel(240.0);
- strokeCount += getIsStrokeWithAngel(270.0);
- strokeCount += getIsStrokeWithAngel(300.0);
- strokeCount += getIsStrokeWithAngel(330.0);
- if (strokeCount > 0) // 四周圍至少有一個點是不透明的,這個點要設成描邊顏色
- {
- myC.rgb = outlineColor;
- myC.a = 1.0;
- }
- gl_FragColor = v_fragmentColor * myC;
- }
我的utilShader.cpp:
- constchar* shaderNameStroke = "ShjyShader_Stroke";
- namespace utilShader
- {
- // 傳入描邊寬度(畫素為單位),描邊顏色,圖片大小,獲得GLProgramState
- cocos2d::GLProgramState* getStrokeProgramState( float outlineSize, cocos2d::Color3B outlineColor, cocos2d::Size textureSize, cocos2d::Color3B foregroundColor/* = cocos2d::Color3B::WHITE*/ )
- {
- auto glprogram = GLProgramCache::getInstance()->getGLProgram(shaderNameStroke);
- if (!glprogram)
- {
- std::string fragmentSource = FileUtils::getInstance()->getStringFromFile(FileUtils::getInstance()->fullPathForFilename("shaders/stroke.fsh"));
- glprogram = GLProgram::createWithByteArrays(ccPositionTextureColor_noMVP_vert, fragmentSource.c_str());
- GLProgramCache::getInstance()->addGLProgram(glprogram, shaderNameStroke);
- }
- auto glprogramState = GLProgramState::create(glprogram);
- glprogramState->setUniformFloat("outlineSize", outlineSize);
- glprogramState->setUniformVec3("outlineColor", Vec3(outlineColor.r / 255.0f, outlineColor.g / 255.0f, outlineColor.b / 255.0f));
- glprogramState->setUniformVec2("textureSize", Vec2(textureSize.width, textureSize.height));
- glprogramState->setUniformVec3("foregroundColor", Vec3(foregroundColor.r / 255.0f, foregroundColor.g / 255.0f, foregroundColor.b / 255.0f));
- return glprogramState;
- }
- }
呼叫的地方:
- Sprite* spr = Sprite::create("elephant1_Diffuse.png");
- spr->setPosition(200, 200);
- spr->setGLProgramState(utilShader::getStrokeProgramState(5, Color3B::GREEN, spr->getContentSize()));
- this->addChild(spr, 1);
效果:
效果還算是比較好的,經測試,此演算法在安卓多個機型上也表現良好。
這樣一套完整的描邊演算法就完成了,如果描述有不當之處,或有更優方法,歡迎吐槽。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------
謝謝complex_ok的吐槽,應該預先計算好sin和cos值,無需每次計算。優化後的 stroke.fsh 如下:
- varying vec4 v_fragmentColor;
- varying vec2 v_texCoord;
- uniform float outlineSize;
- uniform vec3 outlineColor;
- uniform vec2 textureSize;
- uniform vec3 foregroundColor;
- constfloat cosArray[12] = {1, 0.866, 0.5, 0, -0.5, -0.866, -0.1, -0.866, -0.5, 0, 0.5, 0.866};
- constfloat sinArray[12] = {0, 0.5, 0.866, 1, 0.866, 0.5, 0, -0.5, -0.866, -1, -0.866, -0.5};
- int getIsStrokeWithAngelIndex(int index)
- {
- int stroke = 0;
- float a = texture2D(CC_Texture0, vec2(v_texCoord.x + outlineSize * cosArray[index] / textureSize.x, v_texCoord.y + outlineSize * sinArray[index] / textureSize.y)).a;
- if (a >= 0.5)
- {
- stroke = 1;
- }
- return stroke;
- }
- void main()
- {
- vec4 myC = texture2D(CC_Texture0, vec2(v_texCoord.x, v_texCoord.y));
- myC.rgb *= foregroundColor;
- if (myC.a >= 0.5)
- {
- gl_FragColor = v_fragmentColor * myC;
- return;
- }
- int strokeCount = 0;
- strokeCount += getIsStrokeWithAngelIndex(0);
- strokeCount += getIsStrokeWithAngelIndex(1);
- strokeCount += getIsStrokeWithAngelIndex(2);
- strokeCount += getIsStrokeWithAngelIndex(3);
- strokeCount += getIsStrokeWithAngelIndex(4);
- strokeCount += getIsStrokeWithAngelIndex(5);
- strokeCount += getIsStrokeWithAngelIndex(6);
- strokeCount += getIsStrokeWithAngelIndex(7);
- strokeCount += getIsStrokeWithAngelIndex(8);
- strokeCount += getIsStrokeWithAngelIndex(9);
- strokeCount += getIsStrokeWithAngelIndex(10);
- strokeCount += getIsStrokeWithAngelIndex(11);
- bool stroke = false;
- if (strokeCount > 0)
- {
- stroke = true;
- }
- if (stroke)
- {
- myC.rgb = outlineColor;
- myC.a = 1.0;
- }
- gl_FragColor = v_fragmentColor * myC;
- }
相關推薦
【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-x】cocos2d-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