描边原理:在片段着色器里面,对于每个像素:1. 如果它是不透明的像素,则不管,维持原本颜色;2. 如果透明,是360度判断它四周有没有不透明的像素,如果有,则把它设成描边颜色,否则保持透明。
顶点着色器 Outline.vert
attribute vec4 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_color;
//varying 顶点shader和片段shader之间相互传递的参数。
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
void main()
{
gl_Position = CC_PMatrix * a_position;
v_fragmentColor = a_color;
v_texCoord = a_texCoord;
}
片段着色器Outline.frag
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;
}
//如果是透明像素,就判断这个点周围12个方向,每个方向距离当前像素距离是outlineSize的像素点是否透明,
//只要有一个是非透明像素,就把当前像素点设为描边的颜色,并设置成非透明。
int strokeCount = 0;
for (int i = 0; i < 12; i++)
{
strokeCount += getIsStrokeWithAngel(i * 30);
}
if (strokeCount > 0) // 四周围至少有一个点是不透明的,这个点要设成描边颜色
{
myC.rgb = outlineColor;
myC.a = 1.0;
}
gl_FragColor = v_fragmentColor * myC;
}
先判断当前像素是否透明,如果不透明则直接返回。如果是透明像素,就判断这个点周围12个方向,每个方向距离当前像素距离是outlineSize的像素点是否透明,只要有一个是非透明像素,就把当前像素点设为描边的颜色,并设置成非透明。
使用示例
//精灵
auto splash = Sprite::create("grossini.png");
splash->setPosition(100, 100);
this->addChild(splash, 1);
splash->setScale(3);
//vert:顶点着色器 frag:片段着色器
auto glprogram = GLProgram::createWithFilenames("Shaders/test/Outline.vert", "Shaders/test/Outline.frag");
auto glprogramstate = GLProgramState::getOrCreateWithGLProgram(glprogram);
splash->setGLProgramState(glprogramstate);
//设置属性
glprogramstate->setUniformFloat("outlineSize", 3); // 描边宽度
//描边颜色
Color3B outlineColor(0, 255, 0);
glprogramstate->setUniformVec3("outlineColor", Vec3(outlineColor.r / 255.0f, outlineColor.g / 255.0f, outlineColor.b / 255.0f));
//图片纹理大小
glprogramstate->setUniformVec2("textureSize", Vec2(splash->getContentSize().width, splash->getContentSize().height));
效果