Cocos2d-x shader學習3: sprite描邊(Outline)

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的像素點是否透明,只要有一個是非透明像素,就把當前像素點設爲描邊的顏色,並設置成非透明。
效果以下:
outlineorm

相關文章
相關標籤/搜索