目录

前言

一、效果预览

1.弧形边缘光

二、效果制作

1. 制作弧形边缘光

2.弧形边缘光进阶

3.弧形边缘光调节渐变范围

4.边缘光突变

5.同心圆

 三、加入世界坐标做效果

1.绘制结界

2.斑马球

3.效果合并

 四、作者的碎碎念


前言

有粉丝建议说,让我继续更新Shader,我可以出一些简单常用的效果,带着大家写一下,我觉得这是个好主意。

换句话说,带着大家看一下,学到的知识,应该怎么去应用。

我会把常见的调节的一些细致过程逐步写下来,如果遇见没讲过的知识点,就详细讲解一下。

一、效果预览

1.弧形边缘光

这个效果是,对于圆形或者圆弧形的物体,在边缘发光。(如图1所示)

图1 边缘光

二、效果制作

1. 制作弧形边缘光

我们先统一一下思路,发光用到的语义是自发光,之前有讲过,详情看链接。

Unity | Shader基础知识(第十二集:颜色混合)_shade 颜色混合接口-CSDN博客

边缘发光就是当我们的视线和法线的点乘的值在0附近,或0以下的时候,就发光,也是之前讲的,看链接。(如图2所示)Unity | Shader基础知识(第十三集:编写内置着色器阶段总结和表面着色器的补充介绍)_unity viewdir-CSDN博客

图2 viewDir

后面写代码,讲太多遍的就不重新注释了,只注释没讲过的。

Shader "Custom/010"
{
     Properties
    {
    //设置自发光的颜色
    _Color("Color",Color)=(0,0.5,0.5,0)
    }

    SubShader
    {
    CGPROGRAM
    #pragma surface surf Lambert

    float4 _Color;

    struct Input
    {
        //直接获取viewDir
        float3 viewDir;
    };

    void surf(Input IN,inout SurfaceOutput o)
    {
        //点乘后,如果接近1,说明在视线正中,接近0则在视线边缘          
        half dotp =dot(IN.viewDir,o.Normal);

        //设置自发光,我们需要当正中时没有颜色,就是*0,在边缘有颜色,就是*1
        //和上面的数字刚好相反,所以我们用(1-dotp)来得到上面的效果
        o.Emission =_Color.rgb*(1-dotp);
    }
    ENDCG
    }
}

 得到效果为(如图3所示)

图3 发光球

2.弧形边缘光进阶

大部分模型都是有自己贴图的,所以我们在发光的基础上,加上贴图

Shader "Custom/010"
{
     Properties
    {
    //放图片进入
    _MainTex("MainTex",2D)="white"{} 
    //设置自发光的颜色
    _Color("Color",Color)=(0,0.5,0.5,0)
    }

    SubShader
    {
    CGPROGRAM
    #pragma surface surf Lambert

    sampler2D _MainTex;
    float4 _Color;

    struct Input
    {
        float2 uv_MainTex;

        //直接获取viewDir
        float3 viewDir;
    };

    void surf(Input IN,inout SurfaceOutput o)
    {
        //这都是之前讲过的,不进行二次讲解了
        o.Albedo =tex2D(_MainTex,IN.uv_MainTex).rgb;

        //点乘后,如果接近1,说明在视线正中,接近0则在视线边缘          
        half dotp =dot(IN.viewDir,o.Normal);
        //设置自发光,我们需要当正中时没有颜色,就是*0,在边缘有颜色,就是*1
        //和上面的数字刚好相反,所以我们用(1-dotp)来得到上面的效果
        o.Emission =_Color.rgb*(1-dotp);
    }
    ENDCG
    }
}

得到的效果为(如图4所示)

图4 弧形边缘光进阶

3.弧形边缘光调节渐变范围

大部分时候,上面的发光范围都是有点大了,所以我们需要加个参数进行调节。

数学知识:

_Color.rgb*(1-dotp)中的(1-dotp)是一个线性变化的数据,变化是平均的,就像数数一样:0.1,0.2,0.3......

我们怎么才能让它的增长变快?前面一直比较小,越往后越大。

有一个非常简单的方法:平方

{0.1^{2}}{0.2^{2}}{0.3^{2}}.......

如果你觉得平方变化不满意,可以是其他次方

 所以我们增加一个参数,来改变光范围。

函数知识:

次方的公式是pow(底数,次方)

Shader "Custom/010"
{
     Properties
    {
    _MainTex("MainTex",2D)="white"{} 
    _Color("Color",Color)=(0,0.5,0.5,0)
    //边缘光范围参数
    _Power("Power",Range(0.8,8)) = 3
    }

    SubShader
    {
    CGPROGRAM
    #pragma surface surf Lambert

    sampler2D _MainTex;
    float4 _Color;
    //接入参数
    float _Power;

    struct Input
    {
        float2 uv_MainTex;
        float3 viewDir;
    };

    void surf(Input IN,inout SurfaceOutput o)
    {
        o.Albedo =tex2D(_MainTex,IN.uv_MainTex).rgb;         
        half dotp =dot(IN.viewDir,o.Normal);

                             //重新设置范围变化曲线
        o.Emission =_Color.rgb*pow((1-dotp),_Power);
    }
    ENDCG
    }
}

效果(如图5所示)

图5 弧形边缘光调节渐变范围
4.边缘光突变

截止到上图,虽然我们把边缘光可以锁定在非常边缘,但它的过度还是一个渐变的,如果我希望它不要渐变,而是突然出现,怎么办?

答:做条件判断(但在此之前,为了后面计算方便,我们对数据做一个简单的处理)

我们知道前面的乘积最终结果是(-1,1),但实际我们应用时,小于-1的部分,我们完全不需要,因为都是看不见的部分和0的效果一样。

函数知识:

当数据小于0时,直接取0,当数据大于1时,直接取1,其他数据不变。

saturate()

因为前面的代码没变,这里只复制修改过的代码块。(要么太长了)

    void surf(Input IN,inout SurfaceOutput o)
    {
        o.Albedo =tex2D(_MainTex,IN.uv_MainTex).rgb; 
                    //把小于0的数据都删掉
        half dotp =saturate(dot(IN.viewDir,o.Normal));

        o.Emission =_Color.rgb*pow((1-dotp),_Power);
    }

为了方便计算,我们把(1-dotp)也合并到上面。

    void surf(Input IN,inout SurfaceOutput o)
    {
        o.Albedo =tex2D(_MainTex,IN.uv_MainTex).rgb; 
                    //合并计算
        half dotp =1-saturate(dot(IN.viewDir,o.Normal));

        o.Emission =_Color.rgb*pow(dotp,_Power);
    }

我们可以进行突变判断,当dotp>0.8时,显示边缘光,反之不显示。

    void surf(Input IN,inout SurfaceOutput o)
    {
        o.Albedo =tex2D(_MainTex,IN.uv_MainTex).rgb; 
                    //合并计算
        half dotp =1-saturate(dot(IN.viewDir,o.Normal));

        //渐变边缘光(暂时废弃)
        //o.Emission =_Color.rgb*pow(dotp,_Power);

        //突变边缘光       //如果大于0.8,就等于1,反之等于0
        o.Emission =_Color.rgb*dotp>0.8?1:0;
    }

效果(如图6所示)

图6 突变边缘光

如果你还是希望有一点渐变,不太明显,哈哈~可以自己尝试。

        //有一点渐变
        o.Emission =_Color.rgb*dotp>0.8?dotp:0;
5.同心圆

同样继续利用这个规律,我们可以做出同心圆。打了注释的地方是有增加,其他地方不变。

Shader "Custom/010"
{
     Properties
    {
    _MainTex("MainTex",2D)="white"{} 
    //外圈颜色
    _Color("Color",Color)=(0,0.5,0.5,0)
    //内圈颜色
    _Color2("Color2",Color)=(0,0.5,0.5,0)
    _Power("Power",Range(0.8,8)) = 3
    }

    SubShader
    {
    CGPROGRAM
    #pragma surface surf Lambert

    sampler2D _MainTex;
    //外圈声明
    float4 _Color;
    //内圈声明
    float4 _Color2;
    float _Power;

    struct Input
    {
        float2 uv_MainTex;
        float3 viewDir;
    };

    void surf(Input IN,inout SurfaceOutput o)
    {
        o.Albedo =tex2D(_MainTex,IN.uv_MainTex).rgb; 
        half dotp =1-saturate(dot(IN.viewDir,o.Normal));
        //同心圆
        o.Emission =dotp>0.5?_Color.rgb:_Color2.rgb;
    }
    ENDCG
    }
}

效果(如图7所示)

图7 同心圆

备注:多层同心圆

//多层同心圆                                            //用了个绿色
o.Emission =dotp>0.8?_Color.rgb:dotp>0.5?_Color2.rgb:float4(0,1,0,0);
图8 多层同心圆

 三、加入世界坐标做效果

1.绘制结界

动画片里经常有那种从地底下穿越上来,东西就变色了,这里也可以做这种效果,不过,我们需要先加入世界坐标。

其他的代码还是用之前的,改了改了哪里下面就替换哪部分!!!

    struct Input
    {
        float2 uv_MainTex;
        float3 viewDir;
        //加入世界坐标
        float3 worldPos;
    };

当y>0时,变色~

    void surf(Input IN,inout SurfaceOutput o)
    {
        o.Albedo =tex2D(_MainTex,IN.uv_MainTex).rgb; 
        half dotp =1-saturate(dot(IN.viewDir,o.Normal));

        //绘制结界
        o.Emission =IN.worldPos.y>0?_Color:float4(1,1,1,1);
    }

效果(如图9所示)

图9 结界球
2.斑马球

 我们继续对世界坐标做文章,无论我们球的y坐标是多少,我们把奇数染成白的,偶数染成黑的。

数学知识:

整数位是偶数,除以2,结果的小数位在0~0.5之间

整数位是奇数,除以2,结果的小数位在0.5~1之间

备注:你们可以验证一下

3.2/2=1.6,因为3.2的整数位3是奇数,所以结果的小数位0.6,在0.5~1之间。

函数知识:

取小数的函数。

frac()

例:frac(16.89)=0.89

因为我们的球目前本来就不大,所以我们把y坐标都乘10,然后再计算。

    void surf(Input IN,inout SurfaceOutput o)
    {
        half dotp =1-saturate(dot(IN.viewDir,o.Normal));

        //绘制斑马                                    //黑色        //白色
        o.Emission = frac(IN.worldPos.y*10/2)>0.5?float4(1,1,1,1):float4(0,0,0,0);
    }

效果(如图10所示)

图10 斑马球
3.效果合并

我们可以把之前的边缘光效果和贴图效果再打开。

    void surf(Input IN,inout SurfaceOutput o)
    {
        //打开贴图
        o.Albedo =tex2D(_MainTex,IN.uv_MainTex).rgb; 
         
        half dotp =1-saturate(dot(IN.viewDir,o.Normal));
        //绘制斑马
        o.Emission = frac(IN.worldPos.y*10/2)>0.5?
                //颜色乘了边缘光那会的数值    
float4(1,1,1,1)*dotp:float4(0,0,0,0)*dotp;
    }

效果(如图11所示)

图11 效果合并

是不是有那味了~ 

 四、作者的碎碎念

考虑到好多宝宝,学了功能也不知道怎么去用, 专门出了这个简单应用专题,供大家练习。

喜欢的话希望大家给我点赞,收藏加关注哦~ღ( ´・ᴗ・` )比心

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐