Unity | Shader基础知识(第十四集:简单效果练习)
用简单的Shader知识学习写不同的Shader效果,图超多
目录
前言
有粉丝建议说,让我继续更新Shader,我可以出一些简单常用的效果,带着大家写一下,我觉得这是个好主意。
换句话说,带着大家看一下,学到的知识,应该怎么去应用。
我会把常见的调节的一些细致过程逐步写下来,如果遇见没讲过的知识点,就详细讲解一下。
一、效果预览
1.弧形边缘光
这个效果是,对于圆形或者圆弧形的物体,在边缘发光。(如图1所示)
二、效果制作
1. 制作弧形边缘光
我们先统一一下思路,发光用到的语义是自发光,之前有讲过,详情看链接。
Unity | Shader基础知识(第十二集:颜色混合)_shade 颜色混合接口-CSDN博客
边缘发光就是当我们的视线和法线的点乘的值在0附近,或0以下的时候,就发光,也是之前讲的,看链接。(如图2所示)Unity | Shader基础知识(第十三集:编写内置着色器阶段总结和表面着色器的补充介绍)_unity viewdir-CSDN博客
后面写代码,讲太多遍的就不重新注释了,只注释没讲过的。
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所示)
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所示)
3.弧形边缘光调节渐变范围
大部分时候,上面的发光范围都是有点大了,所以我们需要加个参数进行调节。
数学知识:
_Color.rgb*(1-dotp)中的(1-dotp)是一个线性变化的数据,变化是平均的,就像数数一样:0.1,0.2,0.3......
我们怎么才能让它的增长变快?前面一直比较小,越往后越大。
有一个非常简单的方法:平方
,,.......
如果你觉得平方变化不满意,可以是其他次方
所以我们增加一个参数,来改变光范围。
函数知识:
次方的公式是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所示)
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所示)
如果你还是希望有一点渐变,不太明显,哈哈~可以自己尝试。
//有一点渐变
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所示)
备注:多层同心圆
//多层同心圆 //用了个绿色
o.Emission =dotp>0.8?_Color.rgb:dotp>0.5?_Color2.rgb:float4(0,1,0,0);
三、加入世界坐标做效果
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所示)
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所示)
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所示)
是不是有那味了~
四、作者的碎碎念
考虑到好多宝宝,学了功能也不知道怎么去用, 专门出了这个简单应用专题,供大家练习。
喜欢的话希望大家给我点赞,收藏加关注哦~ღ( ´・ᴗ・` )比心
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)