美文网首页cocos2dxcocos2d
基于cocos2d-lua的shader入门玩转

基于cocos2d-lua的shader入门玩转

作者: babybus_break | 来源:发表于2016-03-28 17:56 被阅读4285次

    四年没写过作文了有点小激动呢

    我这里讲的shader是什么呢,通俗讲就是是获得图片的像素和操作图片的像素
    比如我的原本颜色是这样的:


    wo.png
    void main()
    {
        vec4 mycolor =  v_fragmentColor*texture2D(CC_Texture0, v_texCoord);
        //我的颜色.rgb就是这张图片的每个像素点的值,我的颜色.a就是这张图的透明度
        gl_FragColor =  mycolor;
    }
    

    那么cocos2d把我自己变成灰色是怎样呢?在ccShader_UI_Gray.frag文件下它是这么写的

    void main()
    {
        vec4 mycolor =  v_fragmentColor*texture2D(CC_Texture0, v_texCoord);
        //我的颜色向量点乘vec3(0.21,0.71,0.07),还我本来的透明度值
        float gray = dot(mycolor.rgb, vec3(0.2126, 0.7152, 0.0722));
        gl_FragColor =  vec4(gray);
        gl_FragColor.a = mycolor.a;
    }   
    

    效果是这样的


    wo2.png

    好吧这是在模拟器上截的图,放了个背景就当衬托,Shader能干的事太多了,接下来我要引发图战了


    wo3.png
    这张是显示包围盒,平时碰撞检测的时候虽然心里有一个概念,但是还是不如这种直接把透明度<0.1的颜色设为0.5看的直接
        vec4 mycolor =  v_fragmentColor*texture2D(CC_Texture0, v_texCoord);
        if (mycolor.a < 0.1 )
        {
            mycolor = vec4(0.5);
        }
        gl_FragColor = mycolor;
    
    wo4.png
    vec4 mycolor =  v_fragmentColor*texture2D(CC_Texture0, v_texCoord);
        float average = mycolor.r + mycolor.g + mycolor.b;
        if (average < 1.4)
        {
            gl_FragColor = mycolor; 
        }
        else 
        {
            gl_FragColor = vec4(0.);
        }
    

    把我这张图片的rgb值相加,分值低于1.4的正常显示,高于的部分.rgb和透明度.a设为0.0,则出现上面的效果,怎么样像不像做了一个面具
    我们换张图片来操作吧,换张美女图片


    02.png

    做demo或者示例就改拿网上看到的漂亮图片来操作嘛,这样就算随意修修改改也是一张美女图片

    换个美女图片标题肯定要响亮些

    03.jpg

    我做了什么,我只是把美女的轮廓用浮雕凹显出来而已,好的现在开始进入Shader的基本介绍篇:

    #ifdef GL_ES
    precision mediump float;
    #endif
    uniform float PosX;
    uniform float PosY;
    uniform float waterwid;
    uniform float deepwid;
    //PosX是我传入的触控点X位置,PosY是触控点Y位置  
    uniform sampler2D CC_Texture0;
    varying vec2 v_texCoord;
    varying vec4 v_fragmentColor;
    void main() {
        vec2 onePixel = vec2(waterwid/ 48.0, deepwid/ 32.0);
        vec4 color;
        color.rgb = vec3(0.5);
        color -= texture2D(CC_Texture0, v_texCoord - onePixel) * PosX;
        color += texture2D(CC_Texture0, v_texCoord + onePixel) * PosY;
        color.rgb = vec3((color.r + color.g + color.b) / 3.0);
        gl_FragColor = vec4(color.rgb, 1.);
    }
    

    这里的PosX和PosY是我从外部传进来的触控X和Y的点,当然值是被我换算成01,这样的值不管在什么分辨率情况下能保证我们写的代码是通用的,waterwid和deepwid就是下面的两个滑动条了滑动范围也是在01之间,创造shader的Program代码lua:

    function M.Shader_Normal_Init(vs,fs)
        local program = GlUtils:createProgram(vs,fs)
        //顶点着色器和片段着色器
        GlUtils:programRetain(program)
        GlUtils:programAddAttribute(program, kCCAttributeNamePosition, kCCVertexAttrib_Position)
        GlUtils:programAddAttribute(program, kCCAttributeNameTexCoord, kCCVertexAttrib_TexCoords)
        GlUtils:programAddAttribute(program, kCCAttributeNameColor, kCCVertexAttrib_Color)
        //传入我这张图片的位置.纹理.颜色信息给gl这个进程
        GlUtils:programLink(program)
        //实时更新
        GlUtils:programUpdateUniforms(program)
        GlUtils:programUse(program)
        GlUtils:checkErrorDebug()
        return program
    end
    -- 创造浮雕
    function M.CreateRelief(node)
        local program = M.Shader_Normal_Init("bingxue/normal.vs", "bingxue/bins.fs")
        -- 默认水坑在中心点,即Posx=0.5,Posy=0.5
        local amountLocx = GlUtils:programGetUniformLocationForName(program, "PosX")
        GlUtils:programSetUniformLocationWith1f(program, amountLocx, 480.0/960.0)
        local amountLocy = GlUtils:programGetUniformLocationForName(program, "PosY")
        GlUtils:programSetUniformLocationWith1f(program, amountLocy, 320.0/640.0)
        node:setShaderProgram(program)
    end
    

    这时候我话锋一转开始介绍起了shader最基本的一些参数

    Shader常常自带的一些函数

    shader在初始化的时候必然有如下几个默认值的引入
    "uniform mat4 CC_PMatrix;\n"
    "uniform mat4 CC_MVMatrix;\n"
    "uniform mat4 CC_MVPMatrix;\n"
    "uniform vec4 CC_Time;\n"
    "uniform vec4 CC_SinTime;\n"
    "uniform vec4 CC_CosTime;\n"
    "uniform vec4 CC_Random01;\n"
    "uniform sampler2D CC_Texture0;\n"
    "uniform sampler2D CC_Texture1;\n"
    "uniform sampler2D CC_Texture2;\n"
    "uniform sampler2D CC_Texture3;\n"
    

    -- 其中CC_PMatrix表示传入的是所用图片的原始纹理,即你缩放2倍后的图在shader里也是按照原图纹理来操作的,并且坐标原点在左下角
    -- CC_MVPMatrix是我常用的,我们变换后是什么样传入就是什么样,坐标也是传入时候的值
    -- CC_Time.x是每一帧刷新自增0.01 .y是每帧自增0.1 框架默认是定时刷新,所以大部分情况下有这就没必要在创建shader还在外面开定时器
    我常用的顶点着色器通常写法是这样的
    -- CC_Texture0就是默认操作这个Shader的Program的纹理,简单说就是这张图上每个像素点

    #ifdef GL_ES                                        
        precision mediump float;
        varying mediump vec2 v_texCoord;
    #else
        varying vec2 v_texCoord;
    #endif
    attribute vec2 a_texCoord;
    attribute vec4 a_color;
    attribute vec4 a_position;
    varying vec4 v_fragmentColor; 
    void main()
    {
        gl_Position = CC_MVPMatrix * a_position;  
        //gl_Position = CC_PMatrix * a_position; 
        v_fragmentColor = a_color;                                                
        v_texCoord = a_texCoord;       
    }
    

    这里的a_texCoord就是上面lua传入的默认的这张图片的纹理啦,a_color这张图片基本颜色a_position顶点信息,这里为什么还有个v_fragmentColor呢?这个是做什么用的
    举个栗子:假如我在lua设置
    我这张图片:setColor(红色)
    这时候我这张图片应该是先被设置为红色图片再将纹理颜色值传入shader端,但是如果我们不用v_fragmentColor,在片段着色器:

    gl_FragColor = texture2D(CC_Texture0, v_texCoord);
    

    你就会发现显示的是这张图片该是什么颜色就是什么颜色,只有

    gl_FragColor = v_fragmentColor * texture2D(CC_Texture0, v_texCoord);
    

    这张图片显示的才是我们变成红色后的图片,所以我经常把“v_fragmentColor*”写在最后 。
    我再贴一幅图:人物发光效果

    wo5.png

    这张图的术是什么呢?其实就是拿这张图往旁边移动一下绕自己的中心点绕一圈,外围的红色部分就是这偏移出去的部分,由于绕自己中心转的时候中间的不透明值会越加越高,而外围的部分alpha值肯定比较低,这样把两者区分开来,并且让偏移量随时间周期运动看起来就像人物在发光,这次贴上完整的片段着色器代码

    #ifdef GL_ES
    precision mediump float;
    #endif
     
    //所需常量
    uniform sampler2D CC_Texture0;
    varying vec2 v_texCoord;
    varying vec4 v_fragmentColor;
    
    
    #define outlineSize 10.*sin(2.*CC_Time.y)
    // 描边宽度,直接以像素为单位会有分辨率不同大小不同的问题,所以就用比例吧
    #define outlineColor vec3(abs(sin(outlineSize/10.)),0.,0.) // 描边颜色
    #define textureSize vec2(215.0,299.0) // 纹理大小(宽和高),为了计算周围各点的纹理坐标,必须传入它,因为纹理坐标范围是0~1
    
    // 判断在这个角度上距离为outlineSize那一点是不是透明
    int getIsStrokeWithAngel(float angel)
    {
        int stroke = 0;
        float rad = angel * 0.01745329252; // 这个浮点数是 pi / 180,角度转弧度
        float a = texture2D(CC_Texture0, vec2(v_texCoord.x + outlineSize * cos(rad) / textureSize.x, v_texCoord.y + outlineSize * sin(rad) / textureSize.y)).a; 
        // 我把alpha值大于0.5都视为不透明,小于0.5都视为透明
        if (a >= 0.01)
        {
            stroke = 1;
        }
        return stroke;
    }
    void main() {
        // 我的颜色
        vec4 myC = texture2D(CC_Texture0, vec2(v_texCoord.x, v_texCoord.y)); 
        if (myC.a >= 0.5) // 不透明,不管,直接返回
        {
            gl_FragColor =  v_fragmentColor*myC;
            return;
        }
        int strokeCount = 0;
        strokeCount += getIsStrokeWithAngel(0.0);
        strokeCount += getIsStrokeWithAngel(30.0);
        strokeCount += getIsStrokeWithAngel(60.0);
        strokeCount += getIsStrokeWithAngel(90.0);
        strokeCount += getIsStrokeWithAngel(120.0);
        strokeCount += getIsStrokeWithAngel(150.0);
        strokeCount += getIsStrokeWithAngel(180.0);
        strokeCount += getIsStrokeWithAngel(210.0);
        strokeCount += getIsStrokeWithAngel(240.0);
        strokeCount += getIsStrokeWithAngel(270.0);
        strokeCount += getIsStrokeWithAngel(300.0);
        strokeCount += getIsStrokeWithAngel(330.0);
        float dis = pow( pow(abs(v_texCoord.x-0.5),2.) + pow(abs(v_texCoord.y-0.5),2.) , 4.);
        // 四周围至少有一个点是不透明的,这个点要设成描边颜色
        if (strokeCount > 0) 
        {
            myC.rgb = outlineColor;
            myC.a = 1.0*dis;
        }
    
        gl_FragColor = v_fragmentColor * myC;
    }
    

    现在再回过头来看看上面的浮雕,其实它的术就是:
    1.布置一张灰色半透明画布 2.画布-图片往左移动onePixel
    3.画布+图片往右移动onePixel 4.画布颜色值平均化
    至于所谓Posx和Posy的作用就是控制+-的强度了,这两个值我交给屏幕的触控来调整,这样能轻易的看到调整所带来的改变

    水坑

    04.jpg

    还是这个熟悉的漂亮妹子图片,所以说漂亮又皮肤好的人图片再如何扭曲还是漂亮啊!有点跑题了
    看到上面代码的同学可能会问deepwid和waterwid是什么,这就不得不提我早时候做的一个模板了,一个水坑,对-当时假设的需求是上面有一辆车,这辆车经过的时候原地肯定会下陷一个坑再回来嘛,那么问题就来了,下陷是从哪里下陷,下陷的深度是多少,下陷最深处的宽是多少,于是我就带着这三个问题写了这个水坑

    const float pi = 3.141592653589793;
    //从哪里下陷
    float midpoint = PosX;
    //水宽范围
    //float waterwid = keng;
    //水最深处宽带
    //float wid = 0.5;
    float CountScale(float left,float right)
    {
        //水深
        float depth = PosY;
        //防止怪异现象
        if (depth > 1.)
        {
            depth = 1.;
        }
        else if (depth < 0.)
        {
            depth = 0.;
        }
        //像素Y值缩放
        float scale = 1.-depth;
        float length = right - left;
        float mysca = pi/length;
        //向下弯曲的坐标值定在0.-1.之间
        float pos = (v_texCoord.x - left)/length;
        if (pos < (1.-deepwid)/2. )
        {
            pos = 1./(1.-deepwid)*(pos);
            scale = (1.-depth) + depth *(0.5001-0.5*(sin(-pi/2.+pos*2.*pi)) );
        }
        else if( pos > (1.+deepwid)/2.)
        {
            pos = 1./(1.-deepwid)*(pos-deepwid);
            scale = (1.-depth)+ (depth)*(.5001-.5*( sin(-pi/2.+(pos)*2.*pi) ) );
        }
        return scale;
    }
    
    void main() {
        float scale = 1.;
        float left  = midpoint - waterwid/2.;
        float right = midpoint + waterwid/2.;
        if (v_texCoord.x > left && v_texCoord.x < right )
        {
            scale = CountScale(left,right);
        }
        vec4 mycolor = vec4(0.0);
        if (1.-v_texCoord.y < scale )
        {
            mycolor = texture2D(CC_Texture0, vec2(v_texCoord.x,1./scale*(v_texCoord.y-1.+scale))  );
        }
        gl_FragColor = mycolor ;
    }
    

    水坑的术是什么:根据触控的中心点缩小那部分的Y纹理值waterwid就是水宽,也就是这个水坑最上面的宽度值在0~1之间,deepwid那就是水坑下面的宽度了
    最后附上写Shader的时候常用的一些函数和一些不错的网址

    -- 部分好的网站  -- cocos官网
    http://store.cocos.com/stuff/category/12/score_count.html
    -- 一些大师的分享地
    http://www.effecthub.com/#userconsent#
    -- 纯shader纹理
    http://glslsandbox.com/#userconsent#
    -- 游戏素材下载
    http://www.6m5m.com/index.php#userconsent#
    -- 博客,框架一些效果的来源
    http://www.cnblogs.com/hcbin/archive/2012/08/03/2621267.html
    -- 特效视频
    http://i.youku.com/u/UNjE2NDAyODc2?qq-pf-to=pcqq.c2c
    -- 一些基本效果,框架从这里下载过
    http://www.oschina.net/code/snippet_1159242_38156
    -- shader常用语法
    abs                         计算输入值的绝对值。
    acos                        返回输入值反余弦值。
    all                           测试非0值。
    any                         测试输入值中的任何非零值。
    asin                         返回输入值的反正弦值。
    atan                        返回输入值的反正切值。
    atan2                       返回y/x的反正切值。
    ceil                         返回大于或等于输入值的最小整数。
    clamp                      把输入值限制在[min, max]范围内。
    clip                         如果输入向量中的任何元素小于0,则丢弃当前像素。
    cos                         返回输入值的余弦。
    cosh                       返回输入值的双曲余弦。
    cross                      返回两个3D向量的叉积。
    ddx                         返回关于屏幕坐标x轴的偏导数。
    ddy                         返回关于屏幕坐标y轴的偏导数。
    degrees                   弧度到角度的转换
    determinant              返回输入矩阵的值。
    distance                   返回两个输入点间的距离。
    dot                          返回两个向量的点积。
    exp                         返回以e为底数,输入值为指数的指数函数值。
    exp2                       返回以2为底数,输入值为指数的指数函数值。
    faceforward             检测多边形是否位于正面。
    floor                       返回小于等于x的最大整数。
    fmod                       返回a / b的浮点余数。
    frac                        返回输入值的小数部分。
    frexp                       返回输入值的尾数和指数
    fwidth                     返回 abs ( ddx (x) + abs ( ddy(x))。
    isfinite                     如果输入值为有限值则返回true,否则返回false。
    isinf                        如何输入值为无限的则返回true。
    isnan                       如果输入值为NAN或QNAN则返回true。
    ldexp                       frexp的逆运算,返回 x * 2 ^ exp。
    len / lenth                返回输入向量的长度。
    lerp                         对输入值进行插值计算。
    lit                            返回光照向量(环境光,漫反射光,镜面高光,1)。
    log                          返回以e为底的对数。
    log10                      返回以10为底的对数。
    log2                        返回以2为底的对数。
    max                        返回两个输入值中较大的一个。
    min                         返回两个输入值中较小的一个。
    modf                       把输入值分解为整数和小数部分。
    mul                         返回输入矩阵相乘的积。
    normalize                 返回规范化的向量,定义为 x / length(x)。
    pow                        返回输入值的指定次幂。
    radians                    角度到弧度的转换。
    reflect                     返回入射光线i对表面法线n的反射光线。
    refract                     返回在入射光线i,表面法线n,折射率为eta下的折射光线v。
    round                      返回最接近于输入值的整数。
    rsqrt                       返回输入值平方根的倒数。
    saturate                   把输入值限制到[0, 1]之间。
    sign                        计算输入值的符号。
    sin                          计算输入值的正弦值。
    sincos                     返回输入值的正弦和余弦值。
    sinh                        返回x的双曲正弦。
    smoothstep              返回一个在输入值之间平稳变化的插值。
    sqrt                         返回输入值的平方根。
    step                        返回(x >= a)? 1 : 0。
    tan                          返回输入值的正切值。
    fanh                        返回输入值的双曲线切线。
    transpose                 返回输入矩阵的转置。
    tex1D*                    1D纹理查询。
    tex2D*                    2D纹理查询。
    tex3D*                    3D纹理查询。
    texCUBE*                立方纹理查询。
    -- 运用中添加
    fract(x): 取小数部分
    

    自此我的第一篇博客就写完了,最后来一个正常的结尾

    001.png
    我叫雷轰-支付宝changwei821@126.com

    相关文章

      网友评论

      • 咸鱼佬:唯一的小缺憾就是,没有lua执行shader的代码。
      • ccGame:感谢分享

      本文标题:基于cocos2d-lua的shader入门玩转

      本文链接:https://www.haomeiwen.com/subject/kjmjlttx.html