原理:对于所有透明的像素点,遍历该像素点周围的所有像素点,当有任意一个像素点非透明时,就将该像素点置为描边颜色。
PS.在网上读到一位前辈写的方法是“遍历所有不透明的像素点四周,当有透明像素点时,将该像素点设置为描边颜色”(思路相反),这样的做法会有些缺憾,结尾会放出比较图。
local vert = [[
attribute vec4 a_position;
attribute vec2 a_texCoord;
attribute vec4 a_color;
#ifdef GL_ES
varying lowp vec4 v_fragmentColor;
varying mediump vec2 v_texCoord;
#else
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
#endif
void main()
{
gl_Position = CC_PMatrix * a_position;
v_fragmentColor = a_color;
v_texCoord = a_texCoord;
}
]]
local frag = [[
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
uniform vec2 my_size; // 纹理大小,纹理坐标范围为0-1
void main(void)
{
vec2 unit = 1.0 / my_size.xy; // 单位坐标
float step = 30.0; // 30度
float width = 2.5; // 描边宽度
float a_limit = 0.4; // 透明度限制,低于该值即视为透明
vec4 border = vec4(1.0,1.0,1.0,1.0);// 边框颜色
vec4 color1 = texture2D(CC_Texture0, v_texCoord);
gl_FragColor = color1;
if(color1.a >= a_limit)
{
return; // 非透明像素点不做处理
}
// 遍历四周所有像素点
for(float i = 0.0; i < 360.0; i += step)
{
// 当前角度的偏移坐标
vec2 offset = vec2(cos(i) * unit.x, sin(i) * unit.y) * width;
// 当前像素点偏移坐标下的像素点
vec4 color2 = texture2D(CC_Texture0, v_texCoord + offset);
if(color2.a >= a_limit)
{
gl_FragColor = border; // 不透明则将当前像素点设为边框
return;
}
}
}
]]
-- 1.创建glProgram
local glProgram = cc.GLProgram:createWithByteArrays(vert, frag)
-- 2.获取glProgramState
local glProgramState = cc.GLProgramState:getOrCreateWithGLProgram(glProgram)
-- 3.设置属性值
local size = self.blur:getTexture():getContentSizeInPixels()
glProgramState:setUniformVec2("my_size", cc.p(size.width, size.height))
self.blur:setGLProgram(glProgram)
self.blur:setGLProgramState(glProgramState)
当前效果:
相反效果:
添加渐变效果
原理很简单,对于当前像素点,一层一层进行遍历即可
void main(void)
{
vec2 unit = 1.0 / my_size.xy; // 单位坐标
float step = 30.0; // 30度
float width = 5.5; // 描边宽度
float width_step = 0.5; // 描边宽度_步长
float a_limit = 0.4; // 透明度限制,低于该值即视为透明
vec4 border = vec4(1.0,1.0,1.0,1.0);// 边框颜色
vec4 color1 = texture2D(CC_Texture0, v_texCoord);
gl_FragColor = color1;
if(color1.a >= a_limit)
{
return; // bu透明像素点不做处理
}
// 一层一层遍历四周所有像素点
for(float w = 0.0; w < width; w += width_step)
{
for(float i = 0.0; i < 360.0; i += step)
{
// 当前角度的偏移坐标
vec2 offset = vec2(cos(i) * unit.x, sin(i) * unit.y) * w;
// 当前像素点偏移坐标下的像素点
vec4 color2 = texture2D(CC_Texture0, v_texCoord + offset);
if(color2.a >= a_limit)
{
gl_FragColor = border * w / width; // 不透明则将当前像素点设为边框
return;
}
}
}
}
一层一层的遍历像素点明显效率极低,时间复杂度为(12*n)^m = n^m(n为透明像素点,m为遍历层数),一种解决方法:是将两个循环的颠倒过来,先找到非透明像素,然后在逐步判断两个像素点间的下一个非透明像素的位置。时间复杂度为12*n*m = n*m,提高了一些效率。
void main(void)
{
float a_limit = 0.1; // 透明度限制,低于该值即视为透明
vec4 color1 = texture2D(CC_Texture0, v_texCoord);
gl_FragColor = color1;
if(color1.a >= a_limit)
{
return; // bu透明像素点不做处理
}
vec2 unit = 1.0 / my_size.xy; // 单位坐标
float step = 30.0; // 30度
float width = 5.0; // 描边宽度
float width_step = 0.5; // 描边宽度_步长
vec4 border = vec4(1.0,1.0,1.0,1.0);// 边框颜色
// 遍历四周所有像素点
for(float i = 0.0; i < 360.0; i += step)
{
// 当前角度的偏移坐标
vec2 offset = vec2(cos(i) * unit.x, sin(i) * unit.y);
// 当前像素点偏移坐标下的像素点
vec4 color2 = texture2D(CC_Texture0, v_texCoord + offset * width);
if(color2.a >= a_limit)
{
for(float w = 0.0; w <= width; w += width_step)
{
vec4 color3 = texture2D(CC_Texture0, v_texCoord + offset * w);
if (color3.a >= a_limit)
{
gl_FragColor = border * w / width; // 不透明则将当前像素点设为边框
return;
}
}
}
}
}
效果稍有些差异
由于这种方式并没有选择最优点来计算描边宽度,因此描边效果并不好,我们应该找到透明像素点周围距离最近的非透明像素点。
void main(void)
{
float a_limit = 0.1; // 透明度限制,低于该值即视为透明
vec4 color1 = texture2D(CC_Texture0, v_texCoord);
gl_FragColor = color1;
if(color1.a >= a_limit)
{
return; // bu透明像素点不做处理
}
vec2 unit = 1.0 / my_size.xy; // 单位坐标
float step = 30.0; // 30度
float width = 5.0; // 描边宽度
float width_step = 0.5; // 描边宽度_步长
vec4 border = vec4(1.0,1.0,1.0,1.0);// 边框颜色
// 遍历四周所有像素点
float min_dis = 5.0; // 最小距离
float sum = 0.0; // 周围满足条件的非透明像素点个数
for(float i = 0.0; i < 360.0; i += step)
{
// 当前角度的偏移坐标
vec2 offset = vec2(cos(i) * unit.x, sin(i) * unit.y);
// 当前像素点偏移坐标下的像素点
vec4 color2 = texture2D(CC_Texture0, v_texCoord + offset * width);
if(color2.a >= a_limit)
{
for(float w = 0.0; w <= width; w += width_step)
{
vec4 color3 = texture2D(CC_Texture0, v_texCoord + offset * w);
if (color3.a >= a_limit && w <= min_dis)
{
min_dis = w;
sum += 1.0;
}
}
}
}
if(sum > 0.0)
{
gl_FragColor = border * min_dis / width; // 不透明则将当前像素点设为边框
}
}
效果好了一些
---------------------
作者:Emperor_Dandy
来源:CSDN
原文:https://blog.csdn.net/Register_man/article/details/78033003
版权声明:本文为博主原创文章,转载请附上博文链接!