欢迎光临散文网 会员登陆 & 注册

Cocos 实现图像描边shader

2022-05-21 12:19 作者:Nimanggi  | 我要投稿

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:


const char* 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);  



应该预先计算好sin和cos值,无需每次计算。优化后的 stroke.fsh 如下:

varying vec4 v_fragmentColor;  
varying vec2 v_texCoord;  
uniform float outlineSize;  
uniform vec3 outlineColor;  
uniform vec2 textureSize;  
uniform vec3 foregroundColor;  
  
const float cosArray[12] = {1, 0.866, 0.5, 0, -0.5, -0.866, -0.1, -0.866, -0.5, 0, 0.5, 0.866};  
const float 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;  
}  


Cocos 实现图像描边shader的评论 (共 条)

分享到微博请遵守国家法律