Cocos2d-x 3.x的label使用了freetype字體引擎(http://www.freetype.org/),能夠很輕鬆的實現描邊和陰影效果。因此本篇文章只針對於sprite
來實現描邊效果。html
官方demo中描邊shader沒有看懂,看效果好像是有點問題,透明的部分變成了黑色。做者也沒有怎麼解釋,直接丟了一個網址出來(http://www.idevgames.com/forums/thread-3010.html),看樣子是參考了這個帖子。ide
後來從網上別人的博客中找到了一遍關於描邊shader的文章,這篇文章用的方法跟我想的差很少,優勢是很容易理解,缺點是相對於官方demo給的描邊shader效率上差了點。原文地址:http://blog.csdn.net/u011281572/article/details/44999609。字體
原文的代碼考慮了label的描邊,這個對於如今的cocos3.x版原本說有點多餘,我就對原文的代碼作了些改動,去掉了label描邊的那塊兒代碼,有些邏輯也作了一些改變,使得更容易理解一些。.net
下面是我改動後的代碼:code
varying vec4 v_fragmentColor; // vertex shader傳入,setColor設置的顏色 varying vec2 v_texCoord; // 紋理座標 uniform float outlineSize; // 描邊寬度,以像素爲單位 uniform vec3 outlineColor; // 描邊顏色 uniform vec2 textureSize; // 紋理大小(寬和高),爲了計算周圍各點的紋理座標,必須傳入它,由於紋理座標範圍是0~1 // 判斷在這個角度上距離爲outlineSize那一點是否是透明 int getIsStrokeWithAngel(float angel) { int stroke = 0; float rad = angel * 0.01745329252; // 這個浮點數是 pi / 180,角度轉弧度 vec2 unit = 1.0 / textureSize.xy;//單位座標 vec2 offset = vec2(outlineSize * cos(rad) * unit.x, outlineSize * sin(rad) * unit.y); //偏移量 float a = texture2D(CC_Texture0, v_texCoord + offset).a; if (a >= 0.5)// 我把alpha值大於0.5都視爲不透明,小於0.5都視爲透明 { stroke = 1; } return stroke; } void main() { vec4 myC = texture2D(CC_Texture0, v_texCoord); // 正在處理的這個像素點的顏色 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; }
大體的邏輯是:
先判斷當前像素是否透明,若是不透明則直接返回。若是是透明像素,就判斷這個點周圍12個方向,每一個方向距離當前像素距離是outlineSize的像素點是否透明,只要有一個是非透明像素,就把當前像素點設爲描邊的顏色,並設置成非透明。
效果以下:
orm