本篇文章依然是读书笔记。
本文主要写的是unity uv动画相关的内容。
主要是基于unity提供的内置的_Time变量。
代码如下:
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shaders Book/Chapter 11/Water" {
Properties {
_MainTex ("Main Tex", 2D) = "white" {}
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_Magnitude ("Distortion Magnitude", Float) = 1
_Frequency ("Distortion Frequency", Float) = 1
_InvWaveLength ("Distortion Inverse Wave Length", Float) = 10
_Speed ("Speed", Float) = 0.01
}
SubShader {
// Need to disable batching because of the vertex animation
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}
Pass {
Tags { "LightMode"="ForwardBase" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex; //真正的水纹理
float4 _MainTex_ST; //这个用于unity的TRANSFORM_TEX,取xy为缩放,zw为偏移
fixed4 _Color;
float _Magnitude; //振幅
float _Frequency; //这个是变化频率。即控制sin函数的参数变化速率
float _InvWaveLength; //当time为0的时候这个就是Sin函数的初始值,我觉得不是那么有用
float _Speed; //uv变化speed,如果没有这个,就只有顶点呈sin函数,没有uv采样的变化
struct a2v {
float4 vertex : POSITION; //模型空间的位置
float4 texcoord : TEXCOORD0; //纹理
};
struct v2f {
float4 pos : SV_POSITION; //裁剪空间坐标
float2 uv : TEXCOORD0; //uv坐标,用于纹理采样 [0-1]
};
v2f vert(a2v v) {
v2f o;
float4 offset;
offset.yzw = float3(0.0, 0.0, 0.0);
//经过咨询unity shader里单独有的_Time这个东西,其他引擎估计也有不过我暂时不知道
//offset.x = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength) * _Magnitude;
offset.x = sin(_Frequency * _Time.y + v.vertex.z * _InvWaveLength) * _Magnitude; //为什么这里是对x进行offset,如果你打开这个场景就明白了
//使用这个x根本不是固定的。我们可以看到在unity的这个模型视图里其x轴垂直向下,z是水平方向,y是向屏幕内的
//那么这里只是因为坐标轴的原因我们来调整x的offset,实际上如果我们想做旗子的效果,那就是在y轴上offset,如果想做左右跳动的效果,那在这个模型上就是做左右的offset
//这个offset是周期性的,所以使用正弦函数,实际上这里用我注释的代码是一样的效果,但是如果想做出不同的波段,那么就要用到y和z,因为三条水波的y轴都不一样(z其实一样)
o.pos = UnityObjectToClipPos(v.vertex + offset); //这个应该是mvp矩阵变换,将顶点模型空间坐标转换到裁剪空间
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv += float2(0.0, _Time.y * _Speed); //uv做一个v的位移,大概是世界乘以速度,问题是uv增加不会到头吗到1
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed4 c = tex2D(_MainTex, i.uv); //纹理取样
c.rgb *= _Color.rgb;
return c;
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
核心的内容是在vertex shader里去计算顶点的偏移量。这里使用的是x坐标,并不是什么特殊原因,是该unity场景的特殊性导致的。只要是垂直方向的offset即可,不一定是x,然后根据时间去计算uv,在片段着色器中纹理取样,然后再乘上本身的rgb值
接下来看一下offset的计算,我们有3条水纹,3条水纹的振幅和频率必须不同,这里使用的其实也是基于坐标的不同