【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(inti=0;i<360;i+=15)
  3. {
  4. floatrad=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:描边片段着色器

copy

    varyingvec4v_fragmentColor;//vertexshader传入,setColor设置的颜色
  1. varyingvec2v_texCoord;//纹理坐标
  2. uniformfloatoutlineSize;//描边宽度,以像素为单位
  3. uniformvec3outlineColor;//描边颜色
  4. uniformvec2textureSize;//纹理大小(宽和高),为了计算周围各点的纹理坐标,必须传入它,因为纹理坐标范围是0~1
  5. uniformvec3foregroundColor;//主要用于字体,可传可不传,不传默认为白色
  6. //判断在这个角度上距离为outlineSize那一点是不是透明
  7. intgetIsStrokeWithAngel(floatangel)
  8. intstroke=0;
  9. floatrad=angel*0.01745329252;//这个浮点数是pi/180,角度转弧度
  10. floata=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的纹理坐标,而不是像素坐标
  11. if(a>=0.5)//我把alpha值大于0.5都视为不透明,小于0.5都视为透明
  12. {
  13. stroke=1;
  14. }
  15. returnstroke;
  16. voidmain()
  17. vec4myC=texture2D(CC_Texture0,vec2(v_texCoord.x,v_texCoord.y));//正在处理的这个像素点的颜色
  18. myC.rgb*=foregroundColor;
  19. if(myC.a>=0.5)//不透明,不管,直接返回
  20. gl_FragColor=v_fragmentColor*myC;
  21. return;
  22. //这里肯定有朋友会问,一个for循环就搞定啦,怎么这么麻烦!其实我一开始也是用for的,但后来在安卓某些机型(如小米4)会直接崩溃,查找资料发现OpenGLes并不是很支持循环,while和for都不要用
  23. intstrokeCount=0;
  24. strokeCount+=getIsStrokeWithAngel(0.0);
  25. strokeCount+=getIsStrokeWithAngel(30.0);
  26. strokeCount+=getIsStrokeWithAngel(60.0);
  27. strokeCount+=getIsStrokeWithAngel(90.0);
  28. strokeCount+=getIsStrokeWithAngel(120.0);
  29. strokeCount+=getIsStrokeWithAngel(150.0);
  30. strokeCount+=getIsStrokeWithAngel(180.0);
  31. strokeCount+=getIsStrokeWithAngel(210.0);
  32. strokeCount+=getIsStrokeWithAngel(240.0);
  33. strokeCount+=getIsStrokeWithAngel(270.0);
  34. strokeCount+=getIsStrokeWithAngel(300.0);
  35. strokeCount+=getIsStrokeWithAngel(330.0);
  36. if(strokeCount>0)//四周围至少有一个点是不透明的,这个点要设成描边颜色
  37. myC.rgb=outlineColor;
  38. myC.a=1.0;
  39. }


我的utilShader.cpp:

copy

    constchar*shaderNameStroke="ShjyShader_Stroke";
  1. namespaceutilShader
  2. //传入描边宽度(像素为单位),描边颜色,图片大小,获得GLProgramState
  3. cocos2d::GLProgramState*getStrokeProgramState(floatoutlineSize,cocos2d::Color3BoutlineColor,cocos2d::SizetextureSize,cocos2d::Color3BforegroundColor/*=cocos2d::Color3B::WHITE*/)
  4. autoglprogram=GLProgramCache::getInstance()->getGLProgram(shaderNameStroke);
  5. if(!glprogram)
  6. std::stringfragmentSource=FileUtils::getInstance()->getStringFromFile(FileUtils::getInstance()->fullPathForFilename("shaders/stroke.fsh"));
  7. glprogram=GLProgram::createWithByteArrays(ccPositionTextureColor_noMVP_vert,fragmentSource.c_str());
  8. GLProgramCache::getInstance()->addGLProgram(glprogram,shaderNameStroke);
  9. autoglprogramState=GLProgramState::create(glprogram);
  10. glprogramState->setUniformFloat("outlineSize",outlineSize);
  11. glprogramState->setUniformVec3("outlineColor",Vec3(outlineColor.r/255.0f,outlineColor.g/255.0f,outlineColor.b/255.0f));
  12. glprogramState->setUniformVec2("textureSize",Vec2(textureSize.width,textureSize.height));
  13. glprogramState->setUniformVec3("foregroundColor",Vec3(foregroundColor.r/255.0f,foregroundColor.g/255.0f,foregroundColor.b/255.0f));
  14. returnglprogramState;

  15. 调用的地方:

    copy

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

效果

效果还算是比较好的,经测试,此算法在安卓多个机型上也表现良好。

这样一套完整的描边算法就完成了,如果描述有不当之处,或有更优方法,欢迎吐槽。

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

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

copy

    varyingvec4v_fragmentColor;
  1. varyingvec2v_texCoord;
  2. floatoutlineSize;
  3. uniformvec3outlineColor;
  4. uniformvec2textureSize;
  5. uniformvec3foregroundColor;
  6. floatcosArray[12]={1,0.866,0.5,-0.5,-0.866,-0.1,0.866};
  7. floatsinArray[12]={0,1,-1,-0.5};
  8. intgetIsStrokeWithAngelIndex(intindex)
  9. intstroke=0;
  10. if(a>=0.5)
  11. returnstroke;
  12. voidmain()
  13. vec4myC=texture2D(CC_Texture0,v_texCoord.y));
  14. myC.rgb*=foregroundColor;
  15. if(myC.a>=0.5)
  16. gl_FragColor=v_fragmentColor*myC;
  17. return;
  18. intstrokeCount=0;
  19. strokeCount+=getIsStrokeWithAngelIndex(0);
  20. strokeCount+=getIsStrokeWithAngelIndex(1);
  21. strokeCount+=getIsStrokeWithAngelIndex(2);
  22. strokeCount+=getIsStrokeWithAngelIndex(3);
  23. strokeCount+=getIsStrokeWithAngelIndex(4);
  24. strokeCount+=getIsStrokeWithAngelIndex(5);
  25. strokeCount+=getIsStrokeWithAngelIndex(6);
  26. strokeCount+=getIsStrokeWithAngelIndex(7);
  27. strokeCount+=getIsStrokeWithAngelIndex(8);
  28. strokeCount+=getIsStrokeWithAngelIndex(9);
  29. strokeCount+=getIsStrokeWithAngelIndex(10);
  30. strokeCount+=getIsStrokeWithAngelIndex(11);
  31. boolstroke=false;
  32. if(strokeCount>0)
  33. stroke=true;
  34. if(stroke)
  35. myC.rgb=outlineColor;
  36. myC.a=1.0;
  37. }

猜你在找的Cocos2d-x相关文章