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;      // 不透明则将当前像素点设为边框            }        }


效果好了一些

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 华为荣耀畅玩5x卡顿怎么办 淘宝买家收到货后恶意退款怎么办 手机淘宝申请退款后不想退了怎么办 买房交首付时的收据发票掉了怎么办 苹果商城消费提示问题忘记了怎么办 psd文件超过2g不能存储怎么办 手机拍的照片做微信头像太大怎么办 上传的照片在等待中传不上去怎么办 淘宝购物车里的图片模糊怎么办 天猫超市一箱饮料少两瓶怎么办 网上卖一件代发顾客要退货怎么办 京东买东西卖家拒绝发货怎么办 淘宝不小心退款给买家了怎么办 不小心智能清理了淘宝物流怎么办 移动卡绑定了太多东西换联通怎么办 淘宝上卖出的东西快递弄丢了怎么办 京东第三方不确认收货怎么办? 天猫评价被判定为广告怎么办 天猫一个订单用卷分单退货怎么办 天猫对已付款成功后自动退款怎么办 拼多多新人红包减价卖家怎么办 魔力宝贝手机版注册人数已满怎么办 买家投诉虚假签收淘宝卖家该怎么办 手机淘宝商家老打骚扰电话怎么办 手机上查询详单忘记服务密码怎么办 淘宝上的东西买过就找不到了怎么办 刚刚开的淘宝企业店没有流量怎么办 有图片怎么在淘宝找不到商品怎么办 白色高跟鞋鞋面磨脏了怎么办弄干净 我跟鞋后跟磨烂的脚怎么办? 内衣买回来有一股刺鼻的味道怎么办 裁剪袖子开口偏了1公分怎么办 对于这乱扔垃圾不听劝者该怎么办 感觉被南通蒲公英店铺骗了怎么办 6个月宝宝吃米粉不吃奶怎么办 十个月的宝宝不吃辅食怎么办 5个月宝宝拉肚子有泡沫怎么办 生完孩子后皮肤暗黄怎么办 开服装店批发服装的吊牌怎么办 天虹的鞋一天就坏了怎么办 车被钥匙划了露底漆了怎么办