原理:对于所有透明的像素点,遍历该像素点周围的所有像素点,当有任意一个像素点非透明时,就将该像素点置为描边颜色。
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; // 不透明则将当前像素点设为边框
- }
- }
效果好了一些
|
请发表评论