拿一个飞毯效果作为讲解案例,开始之前先展示最终效果:最终效果
准备工作在新场景里创建两个Plane,一个名为Ground,一个名为Carpet,缩放到适合的大小
创建两个材质,跟两个Plane的名字保持一致,并赋予给上一步创建的Plane
自己找适合的地面贴图贴在Ground材质上即可
新建SurfaceShader,拖给Carpet材质,然后开始编写shader
定义Inspector属性
Properties
{
_MainTex ("Albedo (RGBA)", 2D) = "white" {}
_AlphaTest("AlphaTest", Range(0, 1)) = 0
_MetallicTex("Metallic", 2D) = "white"{}
_Metallic ("Intensity", Range(0,1)) = 0.0
_SmoothnessTex("Smoothness", 2D) = "white"{}
_Smoothness ("Intensity", Range(0,1)) = 0
_NormalTex("Normal", 2D) = "bump"
_Normal("Intensity", float) = 1
_Speed("Speed", float) = 1
_Frequency("Frequency", float) = 1
_Amplitude("Amplitude", float) = 1
}定义了物理材质常用的Albedo、Metallic、Smoothness、Normal
然后又定义了飞毯的飞行速度,波浪出现的频率和波浪起伏的高度
_AlphaTest属性是为了接下来传给alphatest,用于像素裁切
渲染类型、顺序、光照模型等一系列设置
Tags
{
"RenderType" = "Opaque"
"Queue" = "AlphaTest"
}
LOD 200
CULL Off
CGPROGRAM
#pragma surface surf Standard fullforwardshadows vertex:vert addshadow alphatest:_AlphaTest
#pragma target 3.0渲染类型为不透明
渲染顺序为透明度测试
CULL off:关闭模型面的剔除,这样模型的背面也会被渲染
使用默认的Standard光照模型
我们使用自定义的顶点函,命名为vert
修改了顶点函数之后,投影会出现跟模型不一致的现象,所以使用addshadow指令重新计算正确投影
透明类型为alphatest,然后把一开始定义的_AlphaTest属性传递过来,小于这个数值的像素会被裁切,大于这个数值的像素会被保留
定义属性和结构体
sampler2D _MainTex;
sampler2D _MetallicTex;
sampler2D _SmoothnessTex;
sampler2D _NormalTex;
struct Input
{
float2 uv_MainTex;
};
half _Smoothness;
half _Metallic;
float _Normal;
half _Speed;
half _Frequency;
half _Amplitude;在CG里重新定义之前的属性
定义Input结构体存储贴图坐标。
这里只是存储了Albedo贴图的坐标,接下来对所有贴图的采样都会使用Albedo的坐标,这么做有两大好处:减少插值寄存器的数量,节省性能消耗
在材质的Inspector上只需要修改Albedo即可调整所有贴图的UV坐标
定义顶点函数
void vert(inout appdata_full v)
{
float time = _Time.y * _Speed;
float offset = (sin(time + v.vertex.z * _Frequency)) * _Amplitude;
v.vertex.y += offset;
}这里使用了CG里提供的时间变量Time的y分量乘以_Speed属性,得到新的时间变量,以后我们就可以在材质的Inspector通过调节Speed属性控制飞毯的速度了
顶点z轴的坐标与_Frequency相乘,相当于对z轴进行了一个范围的缩放。然后跟time变量相加,是为了把顶点Z轴方向与时间结合在一起变为动态变量。经过sin处理之后,可以得出一个随时间推迟而循环浮动的偏移值。最后乘以_Amplitude属性可以控制偏移的大小
顶点的y方向加上这个偏移值,实现垂直方向上的上下浮动效果
定义表面函数
void surf (Input IN, inout SurfaceOutputStandard o)
{
fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
o.Metallic = tex2D(_MetallicTex, IN.uv_MainTex) * _Metallic;
o.Smoothness = tex2D(_SmoothnessTex, IN.uv_MainTex) * _Smoothness;
fixed3 n = UnpackNormal(tex2D(_NormalTex, IN.uv_MainTex)).rgb;
n.x *= _Normal;
n.y *= _Normal;
o.Normal = n;
}
ENDCG使用Albedo贴图的坐标对所有的贴图进行采样,得出表面函数最终的输出变量
法线的x和y分量分别乘以_Normal属性,可以控制法线强度,最终输出到表面函数的法线变量
贴图
本次案例使用的是一张带有Alpha通道的贴图,Alpha通道用于透明测试。RGB通道A通道
Inspector参数设置
最后再展示一遍最终的效果:
从动图可以看出,地面上接受到了飞毯的动态投影,并且灯光也穿过了破洞,射在了地面上。
可以说投影计算的非常完美。