最近一直在研究学习shader,发几个我研究的几个shader吧,忘记在哪里找的的shader了,有的我做了简单的修改。
其中有我学习时加的注释和个人理解,以帮助初学者学习shader,当然大佬就不用看了···
话不多说,直接上源代码:
Shader "SepShader/SnowShader" {
Properties
{
_MainTex("Base (RGB)", 2D) = "white" {}
_Snow("Snow Level", Range(0,1)) = 0
_SnowColor("Snow Color", Color) = (1.0,1.0,1.0,1.0)
_SnowDirection("Snow Direction", Vector) = (0,1,0)//此处表示的其实是雪落下方向的反向,比如我们默认雪是从天空直接落下来的,理论上应该为(0,-1,0),但是此处取得是它的反向,用的是(0,1,0)
_SnowDepth("Snow Depth", Range(0,0.3)) = 0.1
//新的凹凸纹理贴图
_Bump("Bump", 2D) = "bump" {}
//湿润度
_Wetness("Wetness", Range(0, 0.5)) = 0.3
}
SubShader{
Tags{ "RenderType" = "Opaque" }
LOD 200
CGPROGRAM
//我们在下边代码的末尾加入了vertex:vert表示我们将使用自己定义的vertex函数,此函数名称为vert。
#pragma surface surf Lambert vertex:vert
sampler2D _MainTex;
//必须添加一个与Properties代码区中的同名的_Bump变量,作为Properties中_Bump的引用。
//具体缘由详见教程第一部分。
sampler2D _Bump;
float _Snow;
float4 _SnowColor;
float4 _SnowDirection;
float _SnowDepth;
float _Wetness;
//此处要注意,我们不能直接用每个像素的法向值,因为法向贴图(normal map)为我们计算的每个像素法向值是在切空间的法向值,所以我们必须重新计算出每个像素在世界坐标系下真正的法向值,以此来和SnowDirection进行比较来决定此处是否覆盖上雪的颜色。
//得到世界坐标系下的法向量还是有点麻烦的,为此我还特意看了下官方文档。因为我们的表面着色器写入了o.Normal,所以按照第一部分的讲解,我们需要使用INTERNAL_DATA来计算世界坐标系下的法向量,此处我们使用了WorldNormalVector函数。
struct Input {
float2 uv_MainTex;
//用来得到_Bump的uv坐标
float2 uv_Bump;
float3 worldNormal;
INTERNAL_DATA
};
void surf(Input IN, inout SurfaceOutput o)
{
//该像素的真实颜色值
half4 c = tex2D(_MainTex, IN.uv_MainTex);
//从凹凸贴图中得到该像素的法向量
o.Normal = UnpackNormal(tex2D(_Bump, IN.uv_Bump));
half difference = dot(WorldNormalVector(IN, o.Normal), _SnowDirection.xyz) - lerp(1, 0, _Snow);
difference = saturate(difference / _Wetness);
o.Albedo = difference * _SnowColor.rgb + (1 - difference) *c;
o.Alpha = c.a;
}
/*
首先我们传给vert函数一个参数appdata_full v,参数的类型为appdata_full(Unity内置类型),该类型包含了纹理坐标,法向量,顶点位置,以及切线信息。如果你还需要使用其他的数据类型,你可以使用自定义的输入结构体作为pixel函数的第二个参数传递额外的信息 — 目前我们不需要这样做。
_SnowDirection使用的是世界坐标系,但是我们需要的其实是模型局部坐标系下的_SnowDirection。所以我们需要先将_SnowDirection转化到模型的局部坐标系下。而我们只需要将_SnowDirection乘以Unity内置矩阵 – UNITY_MATRIX_IT_MV(IT表示Inverse Transpose逆转置矩阵,MV表示 ModelView矩阵,该矩阵表示是ModelView的逆转置矩阵)。
现在我们得到了该顶点的法向量(vert函数应该是对每个vertex调用一次,相对于surf函数对每个pixel调用一次)。我们仍然像上面做积雪效果时那样,将转换坐标系空间后的雪落下相反方向和模型局部坐标系下的法向量进行点乘,得到的结果仍然和一个插值比较。不过此时插值项不再是_Snow,而是_Snow*2/3,这表示只有那些更接近雪落下方向的区域才会增加雪的厚度,更符合自然现象。
而这些通过测试的区域,沿着(sn.xyz+v.normal)方向进行加厚,也就是将其顶点沿此方向伸展一定距离。注意到增厚的程度取决于_SnowDepth和_Snow,而增厚的方向是由物体法向和雪落的方向综合作用的,这也符合自然现象。
*/
void vert(inout appdata_full v) {
//将_SnowDirection转化到模型的局部坐标系下
if (dot(v.normal, _SnowDirection.xyz) >= lerp(1, 0, ((1 - _Wetness) * _Snow * 2) / 3))
{
v.vertex.xyz += (_SnowDirection.xyz + v.normal) * _SnowDepth * _Snow;
}
}
ENDCG
}
FallBack "Diffuse"
}
上一个效果图吧:
积雪
注:该shader有用到法线贴图