Unity | Shader基础知识(第十四集:简单效果练习)

目录

前言

一、效果预览

1.弧形边缘光

二、效果制作

[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

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

cs 复制代码
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.弧形边缘光进阶

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

cs 复制代码
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......

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

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

.......

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

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

函数知识:

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

cs 复制代码
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()

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

cs 复制代码
    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)也合并到上面。

cs 复制代码
    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时,显示边缘光,反之不显示。

cs 复制代码
    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 突变边缘光

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

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

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

cs 复制代码
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 同心圆

备注:多层同心圆

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

图8 多层同心圆

三、加入世界坐标做效果

1.绘制结界

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

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

cs 复制代码
    struct Input
    {
        float2 uv_MainTex;
        float3 viewDir;
        //加入世界坐标
        float3 worldPos;
    };

当y>0时,变色~

cs 复制代码
    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,然后再计算。

cs 复制代码
    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.效果合并

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

cs 复制代码
    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 效果合并

是不是有那味了~

四、作者的碎碎念

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

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

相关推荐
erxij7 分钟前
【游戏引擎之路】登神长阶(十四)——OpenGL教程:士别三日,当刮目相看
c++·经验分享·游戏·3d·游戏引擎
咩咩觉主2 小时前
尽量通俗易懂地概述.Net && U nity跨语言/跨平台相关知识
unity·c#·.net·.netcore
墨笺染尘缘3 小时前
Unity——对RectTransform进行操作
ui·unity·c#·游戏引擎
AgilityBaby3 小时前
FairyGUI和Unity联动(入门篇)
unity·游戏引擎
这不比博人传燃?6 小时前
传奇996_19——龙岭总结
游戏引擎
一步一个foot-print16 小时前
C# unity 星期几 年月日控制
unity·c#
erxij17 小时前
【游戏引擎之路】登神长阶(十三)——Vulkan教程:讲个笑话:离开舒适区
c++·经验分享·游戏·3d·游戏引擎
无敌最俊朗@1 天前
unity3d————Sprite(精灵图片)
学习·游戏·unity·c#·游戏引擎
皮皮陶1 天前
Unity WebGL交互通信
unity·交互·webgl
程序员正茂1 天前
PICO+Unity MR空间网格
unity·mr·pico