准备
- 创建shader文件、将它拖到新建的材质球上。不赘述。
- 在场景中创建一个plane用于演示,不赘述。
编写VF shader
- 首先我们定义一些要用到的属性:
//外部属性 ,用于初始传值
Properties {
_Color ("Color", Color) = (1,1,1,1) //整体颜色
_MainTex ("Main Tex", 2D) = "white" {} //河流纹理
_Magnitude ("Wave Magnitude",Float) = 1 //波动幅度
_Frequency("Wave Frequency",Float) = 1 //波动频率
_InvWaveLength("Wave Length",Float) = 10 //波动长度倒数
_Speed("Wave Speed",Float) = 0.5 //波动速度
}
- 然后着手写VF着色器,首先定义一些标签。依次声明渲染物体类型是透明的,渲染队列为透明,忽略投影,关闭批处理。
SubShader {
//顶点动画需要关闭批处理
//标签中的两个Transparent意义不同:RenderType中标记物体的渲染类型,而Queue中表示渲染透明几何体阶段时调用此shader(控制渲染时机)
//详情:https://blog.csdn.net/treepulse/article/details/53484505 ; https://docs.unity3d.com/Manual/SL-SubShaderTags.html
//另外注意,Tags分为subshader tags 和 pass tags ,前者适用于所有pass,后者只针对一个pass
Tags { "RenderType"="Transparent" "Queue"="Transparent" "IgnoreProjector"="True" "DisableBatching"="True" }
- 重要部分编写
Pass{
// pass tags 详情:https://docs.unity3d.com/Manual/SL-PassTags.html
Tags{ "LightMode" = "ForwardBase"}//设置光照类型
ZWrite off //关闭深度写入,避免层次覆盖
Blend SrcAlpha OneMinusSrcAlpha //开启颜色混合模式
Cull off //让水流的每一个面都显示
CGPROGRAM
#pragma vertex vert //vextex着色器阶段
#pragma fragment frag //fragment着色器阶段
#include "UnityCG.cginc"
//对属性的声明,便于后续使用
//采样器,用于才片元片元着色器输出阶段对纹理的采样
//通常和tex2D配合使用
//采样器详情:https://docs.unity3d.com/Manual/SL-SamplerStates.html
sampler2D _MainTexer;
//属性对应的值变量
float4 _MainTex_ST;
fixed4 _Color;
float _Magnitude;
float _Frequency;
float _InvWaveLength;
float _Speed;
//定义输入顶点着色器阶段的数据结构
struct Input{
float4 vertex : POSITION; //顶点位置
float4 texcoord : TEXCOORD0; //纹理坐标
};
//定义顶点着色器阶段输出的数据结构
struct v2f{
//SV_前缀的变量代表system value,
//DX10之后就推荐使用SV_POSITION作为vertex shader的输出和fragment shader的输入了,
//注意vertex shader的输入还是使用POSITION!
float4 pos:SV_POSITION;
float2 uv:TEXCOORD0; //纹理坐标
};
//输出v2f到下一渲染阶段
v2f vert(Input v){
v2f o ;
float4 offset;
//因为只希望顶点在X方向移动,所以yzw方向设置为0
offset.yzw = float3(0,0,0);
/*
偏移公式: 正弦化(频率 * 时间 + (顶点x坐标 + 顶点y坐标 + 顶点z坐标) * 波长倒数)*振幅
为了让不同的位置有不同的有不同的位移,加上了模型空间下的位置分量
float4 _Time : 表示自场景加载经过的时间 ,4个分量分别是 (t/20, t, t*2, t*3)
另外: float4 _SinTime/_CosTime :4个分量分别是 (t/8, t/4, t/2, t)
float4 unity_DeltaTime: 4个分量的值分别是(dt, 1/dt, smoothDt, 1/smoothDt)
*/
offset.x = sin(_Frequency*_Time.y
+v.vertex.x*_InvWaveLength
+v.vertex.y *_InvWaveLength
+v.vertex.z*_InvWaveLength)*_Magnitude;
//o.pos = mul(UNITY_MATRIX_MVP,v.vertex + offset); 将顶点坐标转换成齐次裁剪空间,
//mul函数相当于UnityObjectToClipPos函数 ,但是要通过两次矩阵运算,
// 如果只是为了将顶点空间转换成齐次裁剪空间,推荐使用UnityObjectToClipPos函数。
o.pos = UnityObjectToClipPos(v.vertex + offset);
//TRANSFORM_TEX:#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
//定义中的 name##_ST,表示要先声明一个与第二个参数同名且以_ST结尾的“变量”。比如此处的 _MainTex_ST。
//作用:将模型顶点的uv和Tiling、Offset两个变量进行运算(xy控制缩放,zw控制偏移),计算出实际显示用的顶点uv。
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv +=float2(0,_Time.y * _Speed);
return o;
}
//SV_TARGET是系统值,表示该函数返回的是用于下一个阶段OutPut Merger的颜色值
//tex2D(s, t) s:采样器(之前定义的sampler2D) , t:uv坐标 输出纹理信息
fixed4 frag(v2f i):SV_TARGET{
//纹理采样
fixed4 c = tex2D(_MainTexer,i.uv);
//设置输出颜色
c.rgb *= _Color.rgb;
return c;
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
给材质球贴上水纹理
完成。
网友评论