Unity&Shader基础篇-常用函数的使用与案例

原文:http://www.manew.com/thread-97247-1-1.html

一、前言

继前面的系列文章之后,本篇继续讲解Cg的常用函数的使用案例,帮助巩固Cg语言的基础知识。这些函数都可以在Cg的教程里The Cg Tutorial找到示例代码和函数意义。本文讲解几个常用的函数,分别有
1、Step(a,x):如果x小于a返回0;如果x>或=a返回1
2、Clamp(x,a,a):如果x小于a返回a;如果x>b返回b;如果在a和b之间就返回x
3、smoothstep(min,max,x):返回的值为–2*((x –min )/(max –min ))3 +3*((x –min )/(max –min ))2
4、lerp(a,b,f):线性插值函数,返回值为(1-f)*a+b*f
5、三角函数sin、cos


二、常用函数的使用实例

1、Step函数,在Unity中的Shader实例代码如下:

Shader "Unlit/Scenes_004_Step"
{
    Properties
        {
                _background("背景色",Color)=(0,0,0,0)
        }
        SubShader
        {
                // No culling or depth
                Cull Off ZWrite Off ZTest Always

                CGINCLUDE
                ENDCG
        Pass
        {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag

                #include "UnityCG.cginc"
                float4 _background;

        struct appdata
        {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
        };

        struct v2f
        {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
        };

        v2f vert(appdata v)
        {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                o.uv = v.uv;
                o.uv.y = 1 - o.uv.y;
                return o;
        }

        // Functions
        fixed4 frag(v2f i) : SV_Target
        {
                float2 r = 2.0*(i.uv - 0.5);
                //_ScreenParams是Unity内置的变量
                float aspectRatio = _ScreenParams.x / _ScreenParams.y;
                r.x *= aspectRatio;

                fixed3 pixel = _background.xyz;
                float edge, variable, ret;

                //将屏幕划分成五个部分

                //第一部分
                if (r.x < -0.6*aspectRatio)
                {
                        variable = r.y;
                        edge = 0.2;
                        if (variable > edge)
                        {
                                ret = 1.0;
                        }
                        else
                        {
                                ret = 0;
                        }
                }
                else if (r.x < -0.2*aspectRatio)
                {
                        variable = r.y;
                        edge = -0.2;
                        //step(a,x):如果x<a结果返回0,反之返回1
                        ret = step(edge, variable);
                }
                else if (r.x < 0.2*aspectRatio)
                {
                        ret = 1.0 - step(0.5, r.y);
                }
                else if (r.x < 0.6*aspectRatio)
                {
                        ret = 0.3 + 0.5*step(-0.4, r.y);
                }
                else
                {
                        ret = step(-0.3, r.y) * (1.0 - step(0.2, r.y));
                }
                pixel = fixed3(ret, ret, ret);
                return fixed4(pixel, 1.0);
               }
                ENDCG
        }
     }
}

效果:
这里写图片描述


2、Clamp函数,上述的例子的基础上在SubShader中的CGINCLUDE下 定义:

//定义宏
#define PI 3.14159

修改frag函数如下:

fixed4 frag(v2f i) : SV_Target
    {
        float2 r = 2.0*(i.uv - 0.5);
        //_ScreenParams是Unity内置的变量
        float aspectRatio = _ScreenParams.x / _ScreenParams.y;
        r.x *= aspectRatio;

        fixed3 pixel = _background.xyz;
        float edge, variable, ret;

        //第一部分
        if (i.uv.x < 0.25) { // part1
            ret = i.uv.y;
        }
        else if (i.uv.x < 0.5) { // part2
            float minVal = 0.3;
            float maxVal = 0.6;
            variable = i.uv.y;
            if (variable < minVal) {
                ret = minVal;
            }
            if (variable > minVal && variable < maxVal) {
                ret = variable;
            }
            if (variable > maxVal) {
                ret = maxVal;
            }
        }
        else if (i.uv.x < 0.75) { // part3
            float minVal = 0.6;
            float maxVal = 0.8;
            variable = i.uv.y;
            //clam(x,a,b):x如果小于a返回a,如果大于b返回b,在a~b范围内返回x
            ret = clamp(variable, minVal, maxVal);
        }
        else { // part4
            float y = cos(5.0 * 2.0 * PI *i.uv.y);
            y = (y + 1.0)*0.5; // map [-1,1] to [0,1]
            ret = clamp(y, 0.2, 0.8);
        }

        pixel = fixed3(ret, ret, ret);
        return fixed4(pixel, 1.0);

    }

效果:
这里写图片描述
说明:对比1和2的图会明显发现,在使用Clamp处理的时候,边缘的颜色会有渐变的效果。


3、smoothstep,这个脚本只给出片段着色器部分,其他部分同2,代码如下:

fixed4 frag(v2f i) : SV_Target
        {

                fixed3 pixel = _background.xyz;

                float edge, variable, ret;

                if (i.uv.x < 1.0 / 5.0) { // part1
                        edge = 0.5;
                        ret = step(edge, i.uv.y);
                }
                else if (i.uv.x < 2.0 / 5.0) { // part2
                        float edge0 = 0.45;
                        float edge1 = 0.55;
                        float t = (i.uv.y - edge0) / (edge1 - edge0);
                        float t1 = clamp(t, 0.0, 1.0);
                        ret = t1;
                }
                else if (i.uv.x < 3.0 / 5.0) { // part3
                        float edge0 = 0.45;
                        float edge1 = 0.55;
                        float t = clamp((i.uv.y - edge0) / (edge1 - edge0), 0.0, 1.0);
                        float t1 = 3.0*t*t - 2.0*t*t*t;
                        ret = t1;
                }
                else if (i.uv.x < 4.0 / 5.0) { // part4

                        //smoothstep(min,max,x):x=-2*((x-min)/(max-min))^3+3*((x-min)/(max-min))^2,当x=min时返回0,当x=max时返回1
                        ret = smoothstep(0.45, 0.55, i.uv.y);
                }
                else if (i.uv.x < 5.0 / 5.0) {
                        float edge0 = 0.45;
                        float edge1 = 0.55;
                        float t = clamp((i.uv.y - edge0) / (edge1 - edge0), 0.0, 1.0);
                        float t1 = t*t*t*(t*(t*6.0 - 15.0) + 10.0);
                        ret = t1;
                }


                pixel = fixed3(ret, ret, ret);
                return fixed4(pixel, 1.0);
        }

得到的效果图如上图所示,这个效果要对比之前的两个,可以发现有明显的过渡效果。当然不是说这个函数就比之前的函数好用,各有千秋,只是在本篇文章中特有的安排而已。效果:
这里写图片描述


4、lerp函数,同上例子,在上例子中Properties熟悉中添加:

        _col1("颜色1",Color) = (0,0,0,0)
        _col2("颜色2",Color) = (0,0,0,0)

并且再Pass通道中定义这两个东西:

    float4 _col1;
    float4 _col2;

修改下frag函数:

        fixed4 frag(v2f i) : SV_Target
        {

                fixed3 pixel = _background.xyz;

                fixed3 ret;

                if (i.uv.x < 1.0 / 5.0) { // part1
                        float x0 = 0.2;
                        float x1 = 0.7;
                        float m = 0.1;
                        float val = x0 * (1.0 - m) + x1*m;
                        ret = fixed3(val, val, val);
                }
                else if (i.uv.x < 2.0 / 5.0) { // part2
                        float x0 = 0.2;
                        float x1 = 0.7;
                        float m = i.uv.y;
                        float val = x0*(1.0 - m) + x1*m;
                        ret = fixed3(val, val, val);
                }
                else if (i.uv.x < 3.0 / 5.0) { // part3
                        float x0 = 0.2;
                        float x1 = 0.7;
                        float m = i.uv.y;
                        //lerp(a,b,f)返回(1-f)*a+b*f
                        float val = lerp(x0, x1, m);
                        ret = fixed3(val, val, val);

                }
                else if (i.uv.x < 4.0 / 5.0) { // part4
                        float m = i.uv.y;
                        ret = lerp(_col1, _col2, m);
                }
                else if (i.uv.x < 5.0 / 5.0) {
                        float m = smoothstep(0.5, 0.6, i.uv.y);
                        ret = lerp(_col1, _col2, m);
                }
                pixel = ret;
                return fixed4(pixel, 1.0);
        }

效果:
这里写图片描述
这个Shader中对lerp函数和smoothstep函数做了对比,其中第三个区域是使用lerp函数的效果,第四个区域是先进行了smoothstep处理再进行lerp处理的效果。


5、lerp函数与直接的颜色加减进行对比:完整的Shader代码如下:

Shader "Unlit/Scenes_005_ColorAdd&Substr"
{
        Properties
        {

        }
        SubShader
        {
                // No culling or depth
                Cull Off ZWrite Off ZTest Always

                // 追加
                CGINCLUDE
                // 添加画圆盘的方法
                float disk(float2 r, float2 center, float radius) {
                float distanceFromCenter = length(r - center);
                float outsideOfDisk = smoothstep(radius - 0.005, radius + 0.005, distanceFromCenter);
                float insideOfDisk = 1.0 - outsideOfDisk;
                return insideOfDisk;

                }
                ENDCG


        Pass
        {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag

                #include "UnityCG.cginc"

        struct appdata
        {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
        };

        struct v2f
        {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
        };

        v2f vert(appdata v)
        {
                v2f o;
                o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
                o.uv = v.uv;
                o.uv.y = 1 - o.uv.y;
                return o;
        }

        // ANTI-ALIASING WITH SMOOTHSTEP
        fixed4 frag(v2f i) : SV_Target
        {
                float2 r = 2.0 * (i.uv - 0.5);
                float aspectRatio = _ScreenParams.x / _ScreenParams.y;
                r.x *= aspectRatio;

                fixed3 black = float3(0.0, 0.0, 0.0); // black
                fixed3 white = float3(1.0, 1.0, 1.0);
                fixed3 gray = float3(0.3, 0.3, 0.3);
                fixed3 col1 = float3(0.216, 0.471, 0.698); // blue
                fixed3 col2 = float3(1.00, 0.329, 0.298); // red
                fixed3 col3 = float3(0.867, 0.910, 0.247); // yellow

                fixed3 ret;
                fixed3 pixel;
                float d;


                //三个部分的画法各有优势

                //第一部分背景是灰色,简单的覆盖叠加
                if (i.uv.x < 1.0 / 3.0) { // part1
                        ret = gray;
                        d = disk(r, float2(-1.1, 0.3), 0.4);
                        ret = lerp(ret, col1, d);
                        d = disk(r, float2(-1.3, 0.0), 0.4);
                        ret = lerp(ret, col2, d);
                        d = disk(r, float2(-1.05, -0.3), 0.4);
                        ret = lerp(ret, col3, d);
                }

                //第二部分背景是黑色,通过颜色相加来实现
                else if (i.uv.x < 2.0 / 3.0) { // part2
                                                                           // Color addition
                        ret = black;
                        ret += disk(r, float2(0.1, 0.3), 0.4) * col1;
                        ret += disk(r, float2(-0.1, 0.0), 0.4) * col2;
                        ret += disk(r, float2(0.15, -0.3), 0.4) * col3;
                }

                //第三部分背景是白色,通过相减来实现颜色的显示
                else if (i.uv.x < 3.0 / 3.0) { // part3
                                                                           // Color substraction
                        ret = white;
                        ret -= disk(r, float2(1.1, 0.3), 0.4) * col1;
                        ret -= disk(r, float2(1.05, 0.0), 0.4) * col2;
                        ret -= disk(r, float2(1.35, -0.25), 0.4) * col3;

                }
                pixel = ret;
                return fixed4(pixel, 1.0);
        }


                ENDCG
        }
        }
}

得到的效果图如图所示:第一部分通过lerp函数来处理颜色重叠的部分,第二、三部分之间通过颜色的加减来处理颜色重叠的部分。
这里写图片描述


6、三角函数,sin、cos函数:利用正弦余弦函数来做图形的旋转,效果图如图所示:
这里写图片描述

蓝色的网格和蓝色的矩形、圆盘都是固定的,旋转的是红色的网格以及网格上的矩阵和圆盘,它的shader代码如下:

Shader "Unlit/Scenes_005_Rotation"
{
    Properties
    {
        _RotateAngle("旋转的角度",Range(0,360)) = 36
    }
        SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

        // 
        CGINCLUDE

#define PI 3.14159

        // 使用函数来创建网格,返回的值再乘以颜色及得到网格图形
        float coordinateGrid(float2 r)
    {
        float3 axisCol = float3(0.0, 0.0, 1.0);
        float3 gridCol = float3(0.5, 0.5, 0.5);
        float ret = 0.0;

        // 画线
        const float tickWidth = 0.1;
        for (float i = -2.0; i<2.0; i += tickWidth) {
            ret += 1.0 - smoothstep(0.0, 0.008, abs(r.x - i));
            ret += 1.0 - smoothstep(0.0, 0.008, abs(r.y - i));
        }

        // 画坐标轴
        ret += 1.0 - smoothstep(0.001, 0.015, abs(r.x));
        ret += 1.0 - smoothstep(0.001, 0.015, abs(r.y));
        return ret;
    }

    // 在圆盘里面的都返回1
    float disk(float2 r, float2 center, float radius) {
        return 1.0 - smoothstep(radius - 0.005, radius + 0.005, length(r - center));
    }

    // 在长方形里面的都返回1
    float rectangle(float2 r, float2 bottomLeft, float2 topRight) {
        float ret;
        float d = 0.005;
        ret = smoothstep(bottomLeft.x - d, bottomLeft.x + d, r.x);
        ret *= smoothstep(bottomLeft.y - d, bottomLeft.y + d, r.y);
        ret *= 1.0 - smoothstep(topRight.y - d, topRight.y + d, r.y);
        ret *= 1.0 - smoothstep(topRight.x - d, topRight.x + d, r.x);
        return ret;
    }
    ENDCG


        Pass
    {
        CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"

        uniform float _RotateAngle;

    struct appdata
    {
        float4 vertex : POSITION;
        float2 uv : TEXCOORD0;
    };

    struct v2f
    {
        float2 uv : TEXCOORD0;
        float4 vertex : SV_POSITION;
    };

    v2f vert(appdata v)
    {
        v2f o;
        o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
        o.uv = v.uv;
        o.uv.y = 1 - o.uv.y;
        return o;
    }

    //坐标变换:旋转
    fixed4 frag(v2f i) : SV_Target
    {
        float2 r = 2.0 * (i.uv - 0.5);
        float aspectRatio = _ScreenParams.x / _ScreenParams.y;
        r.x *= aspectRatio;

        fixed3 bgCol = float3(1.0, 1.0, 1.0); // white

        fixed3 col1 = float3(0.216, 0.471, 0.698); // blue
        fixed3 col2 = float3(1.00, 0.329, 0.298); // red
        fixed3 col3 = float3(0.867, 0.910, 0.247); // yellow

        fixed3 ret;
        float2 q;
        float angle;
        //angle = 0.2*PI; // 旋转36度
        angle = _RotateAngle / PI;
        q.x = cos(angle)*r.x + sin(angle)*r.y;
        q.y = -sin(angle)*r.x + cos(angle)*r.y;

        ret = bgCol;
        // 画出这两个坐标系

        //底色浅一点为固定轴
        ret = lerp(ret, col1, coordinateGrid(r)*0.4);

        //要旋转的坐标轴
        ret = lerp(ret, col2, coordinateGrid(q));

        // 画出各种图形

        //在固定坐标系的图形
        ret = lerp(ret, col1, disk(r, float2(1.0, 0.0), 0.2));
        ret = lerp(ret, col1, rectangle(r, float2(-0.8, 0.2), float2(-0.5, 0.4)));

        //在可旋转坐标系的图形
        ret = lerp(ret, col2, disk(q, float2(1.0, 0.0), 0.2));
        ret = lerp(ret, col2, rectangle(q, float2(-0.8, 0.2),float2(-0.5, 0.4)));

        fixed3 pixel = ret;
        return fixed4(pixel, 1.0);
    }


        ENDCG
    }
    }
}

7、使用lerp函数来进行缩放
效果图所示:同样,蓝色部分是固定的,红色部分是缩放的的对象。
这里写图片描述

Shader "Unlit/Scenes_005_Scale"
{
    Properties
    {
        _ScaleValue("缩放因子",Range(0.1,10)) = 1
    }
        SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

        CGINCLUDE

#define PI 3.14159

        // 使用函数来创建网格,返回的值再乘以颜色及得到网格图形
        float coordinateGrid(float2 r) {
        float3 axisCol = float3(0.0, 0.0, 1.0);
        float3 gridCol = float3(0.5, 0.5, 0.5);
        float ret = 0.0;

        // 画网格
        const float tickWidth = 0.1;
        for (float i = -2.0; i<2.0; i += tickWidth) {
            ret += 1.0 - smoothstep(0.0, 0.008, abs(r.x - i));
            ret += 1.0 - smoothstep(0.0, 0.008, abs(r.y - i));
        }

        // 画坐标轴
        ret += 1.0 - smoothstep(0.001, 0.015, abs(r.x));
        ret += 1.0 - smoothstep(0.001, 0.015, abs(r.y));
        return ret;
    }

    // 在圆盘内的返回1
    float disk(float2 r, float2 center, float radius) {
        return 1.0 - smoothstep(radius - 0.005, radius + 0.005, length(r - center));
    }

    // 在长方形内的返回1
    float rectangle(float2 r, float2 bottomLeft, float2 topRight) {
        float ret;
        float d = 0.005;
        ret = smoothstep(bottomLeft.x - d, bottomLeft.x + d, r.x);
        ret *= smoothstep(bottomLeft.y - d, bottomLeft.y + d, r.y);
        ret *= 1.0 - smoothstep(topRight.y - d, topRight.y + d, r.y);
        ret *= 1.0 - smoothstep(topRight.x - d, topRight.x + d, r.x);
        return ret;
    }
    ENDCG


        Pass
    {
        CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"

        uniform float _ScaleValue;

    struct appdata
    {
        float4 vertex : POSITION;
        float2 uv : TEXCOORD0;
    };

    struct v2f
    {
        float2 uv : TEXCOORD0;
        float4 vertex : SV_POSITION;
    };

    v2f vert(appdata v)
    {
        v2f o;
        o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
        o.uv = v.uv;
        o.uv.y = 1 - o.uv.y;
        return o;
    }

    //坐标转换:缩放
    fixed4 frag(v2f i) : SV_Target
    {
        float2 r = 2.0 * (i.uv - 0.5);
        float aspectRatio = _ScreenParams.x / _ScreenParams.y;
        r.x *= aspectRatio;

        fixed3 bgCol = float3(1.0, 1.0, 1.0); // white

        fixed3 col1 = float3(0.216, 0.471, 0.698); // blue
        fixed3 col2 = float3(1.00, 0.329, 0.298); // red
        fixed3 col3 = float3(0.867, 0.910, 0.247); // yellow

        fixed3 ret;
        ret = bgCol;

        // 固定的坐标系
        ret = lerp(ret, col1, coordinateGrid(r) / 2.0);

        // 缩放
        float2 q = _ScaleValue*r;
        ret = lerp(ret, col2, coordinateGrid(q));

        // 画各个图形

        //在原始坐标系中画
        ret = lerp(ret, col1, disk(r, float2(0.0, 0.0), 0.1));
        ret = lerp(ret, col1, rectangle(r, float2(-0.5, 0.0),float2(-0.2, 0.2)));

        //在可缩放坐标系中画
        ret = lerp(ret, col2, disk(q, float2(0.0, 0.0), 0.1)); //大
        ret = lerp(ret, col2, rectangle(q, float2(-0.5, 0.0),float2(-0.2, 0.2))); //大



        fixed3 pixel = ret;
        return fixed4(pixel, 1.0);
    }


        ENDCG
    }
    }
}

8、平移以及旋转和平移的组合
效果如图所示,这个Shader部分分了两个部分对旋转和平移进行组合使用,分别是先旋转在平移和先平移再旋转,效果和Shader的代码如下:
这里写图片描述

Shader "Unlit/Scenes_005_Transform"
{
    Properties
    {
        _RotationAngle("旋转角",Range(0,360)) = 0
        _LRotatedTranslatedX("左半部分X方向平移",Range(0,1)) = 0
        _LRotatedTranslatedY("左半部分Y方向平移",Range(0,1)) = 0

        _RRotatedTranslatedX("右半部分X方向平移",Range(0,1)) = 0
        _RRotatedTranslatedY("右半部分Y方向平移",Range(0,1)) = 0
    }
        SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always
        CGINCLUDE
#define PI 3.1415926

        // 通过函数来画网格
        float coordinateGrid(float2 r) {
        float3 axisCol = float3(0.0, 0.0, 1.0);
        float3 gridCol = float3(0.5, 0.5, 0.5);
        float ret = 0.0;

        // 画网线
        const float tickWidth = 0.1;
        for (float i = -2.0; i<2.0; i += tickWidth) {
            ret += 1.0 - smoothstep(0.0, 0.008, abs(r.x - i));
            ret += 1.0 - smoothstep(0.0, 0.008, abs(r.y - i));
        }

        // 画坐标轴
        ret += 1.0 - smoothstep(0.001, 0.015, abs(r.x));
        ret += 1.0 - smoothstep(0.001, 0.015, abs(r.y));
        return ret;
    }

    // 圆内的返回1
    float disk(float2 r, float2 center, float radius) {
        return 1.0 - smoothstep(radius - 0.005, radius + 0.005, length(r - center));
    }

    // 在长方形内返回1
    float rectangle(float2 r, float2 bottomLeft, float2 topRight) {
        float ret;
        float d = 0.005;
        ret = smoothstep(bottomLeft.x - d, bottomLeft.x + d, r.x);
        ret *= smoothstep(bottomLeft.y - d, bottomLeft.y + d, r.y);
        ret *= 1.0 - smoothstep(topRight.y - d, topRight.y + d, r.y);
        ret *= 1.0 - smoothstep(topRight.x - d, topRight.x + d, r.x);
        return ret;
    }
    ENDCG


        Pass
    {
        CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"

        uniform float _RotationAngle;
    uniform float _LRotatedTranslatedX;
    uniform float _LRotatedTranslatedY;
    uniform float _RRotatedTranslatedX;
    uniform float _RRotatedTranslatedY;


    struct appdata
    {
        float4 vertex : POSITION;
        float2 uv : TEXCOORD0;
    };

    struct v2f
    {
        float2 uv : TEXCOORD0;
        float4 vertex : SV_POSITION;
    };

    v2f vert(appdata v)
    {
        v2f o;
        o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
        o.uv = v.uv;
        o.uv.y = 1 - o.uv.y;
        return o;
    }

    //坐标旋转和平移
    fixed4 frag(v2f i) : SV_Target
    {
        float2 r = 2.0 * (i.uv - 0.5);
        float aspectRatio = _ScreenParams.x / _ScreenParams.y;
        r.x *= aspectRatio;

        fixed3 bgCol = float3(1.0, 1.0, 1.0); // white

        fixed3 col1 = float3(0.216, 0.471, 0.698); // blue
        fixed3 col2 = float3(1.00, 0.329, 0.298); // red
        fixed3 col3 = float3(0.867, 0.910, 0.247); // yellow

        fixed3 ret;
        ret = bgCol;

        float angle = _RotationAngle / PI;
        float2x2 rotationMatrix = float2x2(cos(angle), -sin(angle),
            sin(angle), cos(angle));

        //分两部分进行转换

        //左半部分
        if (i.uv.x < 1.0 / 2.0)
        {
            r = r - float2(-aspectRatio / 2.0, 0);
            float2 rotated = mul(rotationMatrix, r);
            float2 rotatedTranslated = rotated - float2(_LRotatedTranslatedX, _LRotatedTranslatedY);

            //原始坐标
            ret = lerp(ret, col1, coordinateGrid(r) * 0.3);
            //旋转坐标
            ret = lerp(ret, col2, coordinateGrid(rotated)*0.3);
            //旋转再平移
            ret = lerp(ret, col3, coordinateGrid(rotatedTranslated)*0.3);

            //原始坐标的图形
            ret = lerp(ret, col1, rectangle(r, float2(-0.1, -0.2), float2(0.1, 0.2)));
            //旋转之后的坐标图形
            ret = lerp(ret, col2, rectangle(rotated, float2(-0.1, -0.2), float2(0.1, 0.2)));
            //旋转再平移之后
            ret = lerp(ret, col3, rectangle(rotatedTranslated, float2(-0.1, -0.2), float2(0.1, 0.2)));
        }

        //右半部分
        else if (i.uv.x < 2.0 / 2.0) {

            r = r - float2(aspectRatio / 2.0, 0);
            //平移
            float2 translated = r - float2(_RRotatedTranslatedX, _RRotatedTranslatedY);
            //平移再旋转
            float2 translatedRotated = mul(rotationMatrix, translated);

            //原始坐标
            ret = lerp(ret, col1, coordinateGrid(r) * 0.3);
            //平移
            ret = lerp(ret, col2, coordinateGrid(translated)*0.3);
            //平移之后再旋转
            ret = lerp(ret, col3, coordinateGrid(translatedRotated)*0.3);

            ret = lerp(ret, col1, rectangle(r, float2(-0.1, -0.2), float2(0.1, 0.2)));
            ret = lerp(ret, col2, rectangle(translated, float2(-0.1, -0.2), float2(0.1, 0.2)));
            ret = lerp(ret, col3, rectangle(translatedRotated, float2(-0.1, -0.2), float2(0.1, 0.2)));
        }




        fixed3 pixel = ret;
        return fixed4(pixel, 1.0);
    }


        ENDCG
    }
    }
}

三、放两个大招—各种动画效果
1、效果如图所示:从左到右分别是五个不同的动画效果,使用到的函数都是前面讲解的函数加上之后补充的旋转、缩放和平移效果。
这里写图片描述
Shader代码如下:

Shader "Unlit/Scenes_005_Animations"
{
    Properties
    {
        _SpeedY("第一部分的速度",Range(0,3)) = 1
        _Amplitude("第二部分的振幅",Range(0,1)) = 0.8
        _RSpeedX("圆周运动X方向的速度",Range(0,10)) = 5
        _RSpeedY("圆周运动Y方向的速度",Range(0,10)) = 5
        _RAmplitudeY("圆周运动Y方向的幅度",Range(0,1)) = 0.1
        _RAmplitudeX("圆周运动X方向的幅度",Range(0,1)) = 0.1
        _ChainAnimSpeed("链条运动的速度",Range(0,10)) = 5
        _ChainAnimRotaSpeed("链条运动的旋转速度",Range(0,10)) = 3
        _JumpSpeed("跳跃运动的速度",Range(0,10)) = 2

    }
        SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always
        CGINCLUDE
#define PI 3.1415926

        // 通过函数来画网格
        float coordinateGrid(float2 r) {
        float3 axisCol = float3(0.0, 0.0, 1.0);
        float3 gridCol = float3(0.5, 0.5, 0.5);
        float ret = 0.0;

        // 画网线
        const float tickWidth = 0.1;
        for (float i = -2.0; i<2.0; i += tickWidth) {
            ret += 1.0 - smoothstep(0.0, 0.008, abs(r.x - i));
            ret += 1.0 - smoothstep(0.0, 0.008, abs(r.y - i));
        }

        // 画坐标轴
        ret += 1.0 - smoothstep(0.001, 0.015, abs(r.x));
        ret += 1.0 - smoothstep(0.001, 0.015, abs(r.y));
        return ret;
    }

    // 圆内的返回1
    float disk(float2 r, float2 center, float radius)
    {
        return 1.0 - smoothstep(radius - 0.005, radius + 0.005, length(r - center));
    }

    // 在长方形内返回1
    float rectangle(float2 r, float2 bottomLeft, float2 topRight)
    {
        float ret;
        float d = 0.005;
        ret = smoothstep(bottomLeft.x - d, bottomLeft.x + d, r.x);
        ret *= smoothstep(bottomLeft.y - d, bottomLeft.y + d, r.y);
        ret *= 1.0 - smoothstep(topRight.y - d, topRight.y + d, r.y);
        ret *= 1.0 - smoothstep(topRight.x - d, topRight.x + d, r.x);
        return ret;
    }
    float mod(float  a, float  b)
    {
        return a - b*floor(a / b);
    }
    ENDCG


        Pass
    {
        CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"
        uniform float _Amplitude;
    uniform float _SpeedY;
    uniform float _RSpeedX;
    uniform float _RSpeedY;
    uniform float _RAmplitudeY;
    uniform float _RAmplitudeX;
    uniform float _ChainAnimSpeed;
    uniform float _ChainAnimRotaSpeed;
    uniform float _JumpSpeed;

    struct appdata
    {
        float4 vertex : POSITION;
        float2 uv : TEXCOORD0;
    };

    struct v2f
    {
        float2 uv : TEXCOORD0;
        float4 vertex : SV_POSITION;
    };

    v2f vert(appdata v)
    {
        v2f o;
        o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
        o.uv = v.uv;
        o.uv.y = 1 - o.uv.y;
        return o;
    }

    //动画,使用到了Unity内置的变量_Time 四维向量(t/20, t, t*2, t*3),_Time.y=t;
    fixed4 frag(v2f i) : SV_Target
    {
        float2 r = 2.0 * (i.uv - 0.5);
        float aspectRatio = _ScreenParams.x / _ScreenParams.y;
        r.x *= aspectRatio;

        fixed3 bgCol = float3(1.0, 1.0, 1.0); // white

        fixed3 col1 = float3(0.216, 0.471, 0.698); // blue
        fixed3 col2 = float3(1.00, 0.329, 0.298); // red
        fixed3 col3 = float3(0.867, 0.910, 0.247); // yellow

        fixed3 ret;
        ret = bgCol;



        //第一部分,循环向上运动
        if (i.uv.x < 1.0 / 5.0)
        {
            float2 q = r + float2(aspectRatio*4.0 / 5.0, 0);

            ret = fixed3(0.3, 0.3, 0.3);
            //unity内置的时间向量
            float y = _SpeedY*_Time.y;

            //使得y在-1到1之间
            y = mod(y,2.0) - 1.0;
            ret = lerp(ret, col1, disk(q, float2(0.0, y), 0.1));
        }
        //第二部分,循环来回并缩放运动
        else if (i.uv.x < 2.0 / 5.0)
        {
            float2 q = r + float2(aspectRatio*2.0 / 5.0, 0);
            ret = fixed3(0.4, 0.4, 0.4);
            //添加振幅
            float y = _Amplitude * sin(0.5*_Time.y* 2.0 * PI);
            float radius = 0.15 + 0.05 * sin(_Time.y * 8.0);
            ret = lerp(ret, col1, disk(q, float2(0.0, y), radius));
        }
        //第三部分,圆周运动并变换颜色
        else if (i.uv.x < 3.0 / 5.0)
        {
            float2 q = r + float2(aspectRatio * 0 / 5.0, 0);
            ret = float3(0.5, 0.5, 0.5);

            float x = _RAmplitudeX*cos(_Time.y*_RSpeedX);
            float y = _RAmplitudeY*sin(_Time.y*_RSpeedY);
            float radius = 0.2 + 0.1*sin(_Time.y*2.0);
            fixed3 color = lerp(col1, col2, sin(_Time.y)*0.5 + 0.5);
            ret = lerp(ret, color, rectangle(q, float2(x - 0.1, y - 0.1), float2(x + 0.1, y + 0.1)));
        }
        //第四部分,链条运动
        else if (i.uv.x < 4.0 / 5.0)
        {
            float2 q = r + float2(-aspectRatio*2.0 / 5.0, 0);
            ret = float3(0.4, 0.4, 0.4);

            for (float i = -1.0; i<1.0; i += 0.2)
            {
                float x = 0.2 * cos(_Time.y*_ChainAnimSpeed + i*PI);
                float y = i;

                float2 s = q - float2(x, y);
                float angle = _Time.y * _ChainAnimRotaSpeed + i;
                float2x2 rot = float2x2(cos(angle), -sin(angle),
                    sin(angle),  cos(angle));
                s = mul(rot, s);
                ret = lerp(ret, col1, rectangle(s, float2(-0.06, -0.06), float2(0.06, 0.06)));
            }
        }
        //第五部分,跳跃运动
        else if (i.uv.x < 5.0 / 5.0)
        {
            float2 q = r + float2(-aspectRatio*4.0 / 5.0, 0);
            ret = float3(0.3, 0.3, 0.3);

            float speed = _JumpSpeed;
            float t = _Time.y * speed;
            float stopEveryAngle = PI / 2.0;
            float stopRatio = 0.5;
            //floor(x):返回小于等于t的最大整数     frac(x):返回x的小数部分
            float t1 = (floor(t) + smoothstep(0.0, 1.0 - stopRatio, frac(t)))*stopEveryAngle;

            float x = -0.2*cos(t1);
            float y = 0.3 * sin(t1);
            float dx = 0.1 + 0.03 * sin(t*10.0);
            float dy = 0.1 + 0.03 * sin(t*10.0 + PI);
            ret = lerp(ret, col1, rectangle(q, float2(x - dx, y - dy), float2(x + dx, y + dy)));


        }


        fixed3 pixel = ret;
        return fixed4(pixel, 1.0);
    }


        ENDCG
    }
    }
}

代码中使用到了“_Time”变量,这个是Unity内置的四维向量,(t/20,t,t*2,t*3)因此“_Time.y=t”即获得系统的渲染的单位时间。
2、等离子流动效果,效果图如图所示:
这里写图片描述
Shader代码如下:

Shader "Unlit/Scenes_005_Plasma"
{
    Properties
    {
        _WaveSpeed("波浪速度",Range(0,10)) = 8
        _ColorValue1("混合颜色1",Range(0,360)) = 180
        _ColorValue2("混合颜色2",Range(0,360)) = 180
    }
        SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always
        CGINCLUDE
#define PI 3.1415926
        ENDCG


        Pass
    {
        CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"
        uniform float _WaveSpeed;
    uniform float _ColorValue1;
    uniform float _ColorValue2;

    struct appdata
    {
        float4 vertex : POSITION;
        float2 uv : TEXCOORD0;
    };

    struct v2f
    {
        float2 uv : TEXCOORD0;
        float4 vertex : SV_POSITION;
    };

    v2f vert(appdata v)
    {
        v2f o;
        o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
        o.uv = v.uv;
        o.uv.y = 1 - o.uv.y;
        return o;
    }

    //等离子效果
    fixed4 frag(v2f i) : SV_Target
    {
        float2 r = 2.0 * (i.uv - 0.5);
        float aspectRatio = _ScreenParams.x / _ScreenParams.y;
        r.x *= aspectRatio;
        float t = _Time.y*_WaveSpeed;
        r = r*8.0;

        float v1 = sin(r.x + t);
        float v2 = sin(r.y + t);
        float v3 = sin(r.x + r.y + t);
        float v4 = sin(sqrt(r.x*r.x + r.y*r.y) + 1.7*t);
        float v = v1 + v2 + v3 + v4;
        fixed3 ret;

        //第一部分垂直波浪
        if (i.uv.x < 1.0 / 10.0)
        {
            ret = float3(v1, v1, v1);
        }
        //第二部分水平波浪
        else if (i.uv.x < 2.0 / 10.0)
        {
            ret = float3(v2, v2, v2);
        }

        //第三部分对角线波浪
        else if (i.uv.x < 3.0 / 10.0)
        {
            ret = float3(v3, v3, v3);
        }
        //第四部分圆环波浪
        else if (i.uv.x < 4.0 / 10.0)
        {
            ret = float3(v4, v4, v4);
        }
        //第五部分所有波浪的综合
        else if (i.uv.x < 5.0 / 10.0)
        {
            ret = float3(v, v, v);
        }
        //第六部分通过正、余弦函数添加周期性渐变
        else if (i.uv.x < 6.0 / 10.0)
        {
            ret = float3(sin(2.0 * v), sin(2.0 * v), sin(2.0 * v));
        }
        //第七部分混合各种颜色
        else if (i.uv.x < 10.0 / 10.0)
        {
            ret = float3(sin(v), sin(v + _ColorValue1 / PI), sin(v + _ColorValue2 / PI));
        }

        ret = 0.5 + 0.5 * ret;

        fixed3 pixel = ret;
        return fixed4(pixel, 1.0);
    }
        ENDCG
    }
    }
}

四、总结

1、Cg函数虽简单,使用得当也是逼格暴涨,对于想学好Shader童鞋来讲,还是要多从Cg语言基础着手,通过实例练习,不仅可以巩固基础知识,而且也可以在做的过程中添加学习的信心和兴趣。
2、正弦、余弦函数配合时间变量的使用能做到非常不错的动画效果,最好的效果还是要从根本上来讲还是数学,当今世界是学好数学和英语走遍天下都不怕了。


好了,感谢原作者的分享。

每天进步一点点!!!

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值