Cocos2d-lua 初识shader之四:描边
来源:互联网 发布:nginx 禁止外网访问 编辑:程序博客网 时间:2024/05/18 03:44
原理:对于所有透明的像素点,遍历该像素点周围的所有像素点,当有任意一个像素点非透明时,就将该像素点置为描边颜色。
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; // 不透明则将当前像素点设为边框 } }
效果好了一些
阅读全文
0 0
- Cocos2d-lua 初识shader之四:描边
- Cocos2d-lua 初识shader之二:传值
- Cocos2d-lua 初识shader之三:模糊
- Cocos2d-lua 初识shader之五:定时器
- Cocos2d-lua 初识shader之一:置灰
- cocos2d lua使用 shader
- Lua初识之值类型-四
- Cocos2d-x lua shader使用
- lua 函数初识(四)
- Lua初识之全局变量
- Unity之Shader初识 - 一
- Cocos2d-Lua之函数
- Cocos2d-Lua之赋值
- Cocos2d-Lua之table
- Cocos2d-Lua之string
- Cocos2d-Lua之随机数
- Lua初识之LuaStdio使用方法
- Lua初识之输入输出-三
- 软件开发中会用到的图
- 大家一起学爬虫(一)
- obj + mtl 格式说明
- python线程安全队列用法
- 怎么看地图?还是要一起看看地图是怎么画出来的!
- Cocos2d-lua 初识shader之四:描边
- 各种重启
- Matlab中查看内存环境的方法
- 元素出栈、入栈顺序的合法性
- 多线程
- python-PIL
- java POI excel导出,并合并单元格设置宽度高度
- 《将博客搬至CSDN》
- x264学习笔记(一)