转载:http://blog.csdn.net/u011281572/article/details/44999609
图片描边需求如下:
1. 可指定描边宽度 2. 可指定描边颜色3. 可用于字体
图片描边我所知道的方式有以下几种:
1. Cocos2d-x 3.x中,字体用FreeType库,字体描边可以用FreeType自带的描边功能,实际效果没测过,但只能用于字体。
2. 用RenderTexture,方法大概是把原图做一圈的偏移,渲染到同一张纹理上,他们相隔中心点的距离都是r,最后再把自己渲染到中间,核心代码大概这样:
[cpp] view plain copy rt->begin(); for(inti=0;i<360;i+=15) { floatrad=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();这种方法可以作为一个比较好的解决方案,但是我认为这种方式在生成描边图片时,需要绘制多个图片,效率不是很好。
以下我分享一种我认为比较好的描边算法:
在片段着色器里面,对于每个像素:1. 如果它是不透明的像素,则不管,维持原本颜色;2. 如果透明,是360度判断它四周有没有不透明的像素,如果有,则把它设成描边颜色,否则保持透明。
我为代码加了比较详细的注释,希望大家能理解
stroke.fsh:描边片段着色器
copy varyingvec4v_fragmentcolor;//vertexshader传入,setcolor设置的颜色 varyingvec2v_texCoord;//纹理坐标 uniformfloatoutlinesize;//描边宽度,以像素为单位 uniformvec3outlinecolor;//描边颜色 uniformvec2textureSize;//纹理大小(宽和高),为了计算周围各点的纹理坐标,必须传入它,因为纹理坐标范围是0~1 uniformvec3foregroundcolor;//主要用于字体,可传可不传,不传默认为白色 //判断在这个角度上距离为outlinesize那一点是不是透明 intgetIsstrokeWithAngel(floatangel) intstroke=0; floatrad=angel*0.01745329252;//这个浮点数是pi/180,角度转弧度 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的纹理坐标,而不是像素坐标 if(a>=0.5)//我把Alpha值大于0.5都视为不透明,小于0.5都视为透明 { stroke=1; } returnstroke; voIDmain() vec4myC=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)会直接崩溃,查找资料发现OpenGLes并不是很支持循环,while和for都不要用 intstrokeCount=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; }
我的utilShader.cpp:
copy constchar*shadernamestroke="ShjyShader_stroke"; namespaceutilShader //传入描边宽度(像素为单位),描边颜色,图片大小,获得GLProgramState cocos2d::GLProgramState*getstrokeProgramState(floatoutlinesize,cocos2d::color3Boutlinecolor,cocos2d::SizetextureSize,cocos2d::color3Bforegroundcolor/*=cocos2d::color3B::WHITE*/) autoglprogram=GLProgramCache::getInstance()->getGLProgram(shadernamestroke); if(!glprogram) std::stringfragmentSource=fileUtils::getInstance()->getStringFromfile(fileUtils::getInstance()->fullPathForfilename("shaders/stroke.fsh")); glprogram=GLProgram::createWithByteArrays(ccpositionTexturecolor_noMVP_vert,fragmentSource.c_str()); GLProgramCache::getInstance()->addGLProgram(glprogram,shadernamestroke); autoglprogramState=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)); returnglprogramState;
调用的地方:
copy 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 如下:
copy varyingvec4v_fragmentcolor; varyingvec2v_texCoord; floatoutlinesize; uniformvec3outlinecolor; uniformvec2textureSize; uniformvec3foregroundcolor; floatcosArray[12]={1,0.866,0.5,-0.5,-0.866,-0.1,0.866}; floatsinArray[12]={0,1,-1,-0.5}; intgetIsstrokeWithAngelindex(intindex) intstroke=0; if(a>=0.5) returnstroke; voIDmain() vec4myC=texture2D(CC_Texture0,v_texCoord.y)); myC.rgb*=foregroundcolor; if(myC.a>=0.5) gl_Fragcolor=v_fragmentcolor*myC; return; intstrokeCount=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); boolstroke=false; if(strokeCount>0) stroke=true; if(stroke) myC.rgb=outlinecolor; myC.a=1.0; } 总结
以上是内存溢出为你收集整理的【Cocos2d-x】图片描边的一种比较好的shader实现方法全部内容,希望文章能够帮你解决【Cocos2d-x】图片描边的一种比较好的shader实现方法所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)