庄懂-BoyanTata的个人空间_哔哩哔哩_Bilibili
顶点平移/缩放/旋转/案例动画

顶点平移
在AlphaBlend基础上,追加Y轴向上周期性位移

代码部分

Translation
对一个顶点位置信息做一个处理,这个处理就是顶点动画
这个方法没用常规return返回方法,它的返回值是void
inout : 为这个参数给了一张出入的通行证,后面的参数可以进入Inout做一些操作,然后再跑出去,那这样一个参数它在方法内就可能会被改变,而不需要返回其它的值

顶点平移/核心代码分析
时间乘以一个时间流动的速度,这样我们可以控制动画的快慢,为了保证这个浮点的精度,我们对乘完的结果取余, 然后让时间的流动保持在0-1的区间,但是我们外面用的是一个sin的函数,所以0-1是不行的,我们要让里面的这个值是0-6.283185,所以要乘以一个2π(TWO_PI),乘完2π在用sin这个周期性的函数给括上,它就可以做这样一个周期性的波动,得到这个波动之后,我们还想控制它的波动的范围,所以我们乘以一个范围_MoveRange的变量,这样我们就会得到一个上下起伏的偏移值,我们要把这个偏移值加到原本的Y轴数值上去,这就是一个顶点动画的过程.

Shader "AP01/L19/Translation"
{
Properties
{
_MainTex ("RGB:颜色 A:透贴", 2d) = "gray"{}
_Opacity ("透明度", range(0, 1)) = 0.5
_MoveRange ("移动范围", range(0.0, 3.0)) = 1.0
_MoveSpeed ("移动速度", range(0.0, 3.0)) = 1.0
}
SubShader
{
Tags
{
"Queue"="Transparent" // 调整渲染顺序
"RenderType"="Transparent" // 对应改为Cutout
"ForceNoShadowCasting"="True" // 关闭阴影投射
"IgnoreProjector"="True" // 不响应投射器
}
Pass
{
Name "FORWARD"
Tags { "LightMode"="ForwardBase" }
Blend One OneMinusSrcAlpha // 修改混合方式One/SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
// 输入参数
uniform sampler2D _MainTex; uniform float4 _MainTex_ST;
uniform half _Opacity;
uniform float _MoveRange;
uniform float _MoveSpeed;
// 输入结构
struct VertexInput
{
float4 vertex : POSITION; // 顶点位置 总是必要
float2 uv : TEXCOORD0; // UV信息 采样贴图用
};
// 输出结构
struct VertexOutput
{
float4 pos : SV_POSITION; // 顶点位置 总是必要
float2 uv : TEXCOORD0; // UV信息 采样贴图用
};
// 声明常量
#define TWO_PI 6.283185
// 顶点动画方法
void Translation (inout float3 vertex)
{
vertex.y += _MoveRange * sin(frac(_Time.z * _MoveSpeed) * TWO_PI);
}
// 输入结构>>>顶点Shader>>>输出结构
VertexOutput vert (VertexInput v)
{
VertexOutput o = (VertexOutput)0;
Translation(v.vertex.xyz);
o.pos = UnityObjectToClipPos(v.vertex); // 顶点位置 OS>CS
o.uv = TRANSFORM_TEX(v.uv, _MainTex); // UV信息 支持TilingOffset
return o;
}
// 输出结构>>>像素
half4 frag(VertexOutput i) : COLOR
{
half4 var_MainTex = tex2D(_MainTex, i.uv); // 采样贴图 RGB颜色 A透贴
half3 finalRGB = var_MainTex.rgb;
half opacity = var_MainTex.a * _Opacity;
return half4(finalRGB * opacity, opacity); // 返回值
}
ENDCG
}
}
}
顶点缩放
在AlphaBlend基础上,基于模型原点追加周期性缩放


Scaling
缩放一般都是用乘法,算出它缩放的一个比例,去做一个周期性的变化,再去乘之(平移是加,缩放是乘)

顶点缩放/核心代码分析
平移是用加法,缩放是用乘法,在一个不希望产生一个例如是-20%的结果,所以想要在原本100%比例大小的范围上去做一个浮动,所以前面要加一个1.0(1.0就是100%)
另外需要在面板上设置为Range,避免滑动过程中出现负向缩放

Shader "AP01/L19/Scaling"
{
Properties
{
_MainTex ("RGB:颜色 A:透贴", 2d) = "gray"{}
_Opacity ("透明度", range(0, 1)) = 0.5
_ScaleRange ("缩放范围", range(0.0, 0.5)) = 0.2
_ScaleSpeed ("缩放速度", range(0.0, 3.0)) = 1.0
}
SubShader
{
Tags
{
"Queue"="Transparent" // 调整渲染顺序
"RenderType"="Transparent" // 对应改为Cutout
"ForceNoShadowCasting"="True" // 关闭阴影投射
"IgnoreProjector"="True" // 不响应投射器
}
Pass
{
Name "FORWARD"
Tags { "LightMode"="ForwardBase" }
Blend One OneMinusSrcAlpha // 修改混合方式One/SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
// 输入参数
uniform sampler2D _MainTex; uniform float4 _MainTex_ST;
uniform half _Opacity;
uniform float _ScaleRange;
uniform float _ScaleSpeed;
// 输入结构
struct VertexInput
{
float4 vertex : POSITION; // 顶点位置 总是必要
float2 uv : TEXCOORD0; // UV信息 采样贴图用
};
// 输出结构
struct VertexOutput
{
float4 pos : SV_POSITION; // 顶点位置 总是必要
float2 uv : TEXCOORD0; // UV信息 采样贴图用
};
// 声明常量
#define TWO_PI 6.283185
// 顶点动画方法
void Scaling (inout float3 vertex)
{
vertex *= 1.0 + _ScaleRange * sin(frac(_Time.z * _ScaleSpeed) * TWO_PI);
}
// 输入结构>>>顶点Shader>>>输出结构
VertexOutput vert (VertexInput v)
{
VertexOutput o = (VertexOutput)0;
Scaling(v.vertex.xyz);
o.pos = UnityObjectToClipPos(v.vertex); // 顶点位置 OS>CS
o.uv = TRANSFORM_TEX(v.uv, _MainTex); // UV信息 支持TilingOffset
return o;
}
// 输出结构>>>像素
half4 frag(VertexOutput i) : COLOR
{
half4 var_MainTex = tex2D(_MainTex, i.uv); // 采样贴图 RGB颜色 A透贴
half3 finalRGB = var_MainTex.rgb;
half opacity = var_MainTex.a * _Opacity;
return half4(finalRGB * opacity, opacity); // 返回值
}
ENDCG
}
}
}
顶点旋转
在AlphaBlend基础上,基于模型Y轴(竖向)追加周期性旋转

代码部分

以平移为例子,确定了平移的偏移量
我们将平移量作用到顶点数据上去

顶点旋转/核心代码分析
取到时间,对时间做一个速度上的缩放,然后再对其进行取一个余,然后乘一个2π,用这样一个值去驱动Sin函数,得到一个-1到1的波动的数值,然后用-1到1去乘以一个_RotateRange这样一个范围,就得到角度的偏移
对角度的应用,首先将角度转成弧度,方便后面三角函数的运算,因为三角函数取的参数是弧度而不是角度

Shader "AP01/L19/Rotation"
{
Properties
{
_MainTex ("RGB:颜色 A:透贴", 2d) = "gray"{}
_Opacity ("透明度", range(0, 1)) = 0.5
_RotateRange ("旋转范围", range(0.0, 45.0)) = 20.0
_RotateSpeed ("旋转速度", range(0.0, 3.0)) = 1.0
}
SubShader
{
Tags
{
"Queue"="Transparent" // 调整渲染顺序
"RenderType"="Transparent" // 对应改为Cutout
"ForceNoShadowCasting"="True" // 关闭阴影投射
"IgnoreProjector"="True" // 不响应投射器
}
Pass
{
Name "FORWARD"
Tags { "LightMode"="ForwardBase" }
Blend One OneMinusSrcAlpha // 修改混合方式One/SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
// 输入参数
uniform sampler2D _MainTex; uniform float4 _MainTex_ST;
uniform half _Opacity;
uniform float _RotateRange;
uniform float _RotateSpeed;
// 输入结构
struct VertexInput
{
float4 vertex : POSITION; // 顶点位置 总是必要
float2 uv : TEXCOORD0; // UV信息 采样贴图用
};
// 输出结构
struct VertexOutput
{
float4 pos : SV_POSITION; // 顶点位置 总是必要
float2 uv : TEXCOORD0; // UV信息 采样贴图用
};
// 声明常量
#define TWO_PI 6.283185
// 顶点动画方法
void Rotation (inout float3 vertex)
{
float angleY = _RotateRange * sin(frac(_Time.z * _RotateSpeed) * TWO_PI);
float radY = radians(angleY);
float sinY, cosY = 0;
sincos(radY, sinY, cosY);
vertex.xz = float2
(
vertex.x * cosY - vertex.z * sinY,
vertex.x * sinY + vertex.z * cosY
);
}
// 输入结构>>>顶点Shader>>>输出结构
VertexOutput vert (VertexInput v)
{
VertexOutput o = (VertexOutput)0;
Rotation(v.vertex.xyz);
o.pos = UnityObjectToClipPos(v.vertex); // 顶点位置 OS>CS
o.uv = TRANSFORM_TEX(v.uv, _MainTex); // UV信息 支持TilingOffset
return o;
}
// 输出结构>>>像素
half4 frag(VertexOutput i) : COLOR
{
half4 var_MainTex = tex2D(_MainTex, i.uv); // 采样贴图 RGB颜色 A透贴
half3 finalRGB = var_MainTex.rgb;
half opacity = var_MainTex.a * _Opacity;
return half4(finalRGB * opacity, opacity); // 返回值
}
ENDCG
}
}
}
假设有一个向量a和b,a是从A指向B的一个向量,b是从A指向D的一个向量,那a+b和a-b分别是?
加法相当于把向量a,AB平移到D的位置,得到一个DC,然后把AD平移到B的位置,得到一个BC,这样就围城了一个平行四边形,那a+b等于从A指向C的向量,就相当于两个向量求它的和,就是将其拼成一个平行四边形,平行四边形的这两个对角一连线就是它们的和,它们另外一个对角就是它们的差,向量的差是指向被减数的,a-b当然是a被减

现在我们的模型是沿Y轴旋转,就当于Y轴上是没有变化的,只有X和Z轴有变化,现在假设模型上有一个点,它的坐标值是(X,Z),然后旋转一个角度theta之后,这个黑色的点就旋转到红色的点上了,我们已知原始点坐标是(X,Z),旋转theta角度之后,红色的坐标值是多少?如果我们知道这个坐标值跟这个旋转角theta的对应关系,代码也就可以写了
先将黑色的点理解为一个向量,这个向量就是从原点指向黑点的一个向量,这个向量的值也就是它的坐标值,然后将这个向量值根据它的X轴坐标和Z轴坐标分成两个向量,原始的向量等于这两个向量相加,很显然是一个平行四边形,这两个向量也同样旋转theta角度之后,就变成了红色的这两个向量,两个红色的向量相加也等于红色的坐标点,可以视作整个坐标系旋转了一个theta角,之前是一个长方形,旋转之后仍然是一个长方形,求原点指向红点的向量,知道红色的两个向量就可以了,红色向量是从黑色向量转过来的,旋转是不改变向量长度的,黑色向量长度是X,那旋转后的红色向量长度仍然是X,红色向量投影到X轴上的长度是CosX,邻边比斜边是Cos,知道斜边的长度,那邻边长度是X乘以Cos_theta,另一条边是对边比斜边,那就是X乘以Sin_theta,那红色坐标就是(X*Cos_theta,X*Sin_theta)
同样的,这个黑色的向量,它的长度就是Z,它旋转之后红色向量长度还是Z,那红色向量投影到X轴上的长度是对边比斜边Sin_theta,它投的是负的象限,就是-Sin_theta,那就是斜边的长度是Z,要乘以一个负的Sin_theta等于这个分量,然后Z轴上的分量是邻边比斜边,就是Z*Cos_theta,最后是分量相加





综合案例_幽灵夜巡


代码部分



核心代码分析

Shader "AP01/L19/AnimGhost"
{
Properties
{
_MainTex ("RGB:颜色 A:透贴", 2d) = "gray"{}
_Opacity ("透明度", range(0, 1)) = 0.5
_ScaleParams ("天使圈缩放 X:强度 Y:速度 Z:校正", vector) = (0.2, 1.0, 4.5, 0.0)
_SwingXParams ("X轴扭动 X:强度 Y:速度 Z:波长", vector) = (1.0, 3.0, 1.0, 0.0)
_SwingZParams ("Z轴扭动 X:强度 Y:速度 Z:波长", vector) = (1.0, 3.0, 1.0, 0.0)
_SwingYParams ("Y轴起伏 X:强度 Y:速度 Z:滞后", vector) = (1.0, 3.0, 0.3, 0.0)
_ShakeYParams ("Y轴摇头 X:强度 Y:速度 Z:滞后", vector) = (20.0, 3.0, 0.3, 0.0)
}
SubShader
{
Tags
{
"Queue"="Transparent" // 调整渲染顺序
"RenderType"="Transparent" // 对应改为Cutout
"ForceNoShadowCasting"="True" // 关闭阴影投射
"IgnoreProjector"="True" // 不响应投射器
}
Pass
{
Name "FORWARD"
Tags { "LightMode"="ForwardBase" }
Blend One OneMinusSrcAlpha // 修改混合方式One/SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#pragma multi_compile_fwdbase_fullshadows
#pragma target 3.0
// 输入参数
uniform sampler2D _MainTex; uniform float4 _MainTex_ST;
uniform half _Opacity;
uniform float4 _ScaleParams;
uniform float3 _SwingXParams;
uniform float3 _SwingZParams;
uniform float3 _SwingYParams;
uniform float3 _ShakeYParams;
// 输入结构
struct VertexInput
{
float4 vertex : POSITION; // 顶点位置 总是必要
float2 uv : TEXCOORD0; // UV信息 采样贴图用
float4 color : COLOR; // 顶点色 遮罩用
};
// 输出结构
struct VertexOutput
{
float4 pos : SV_POSITION; // 顶点位置 总是必要
float2 uv : TEXCOORD0; // UV信息 采样贴图用
float4 color : COLOR;
};
// 声明常量
#define TWO_PI 6.283185
// 顶点动画方法
void AnimGhost (inout float3 vertex, inout float3 color)
{
// 缩放天使圈
float scale = _ScaleParams.x * color.g * sin(frac(_Time.z * _ScaleParams.y) * TWO_PI);
vertex.xyz *= 1.0 + scale;
vertex.y -= _ScaleParams.z * scale;
// 幽灵摆动
float swingX = _SwingXParams.x * sin(frac(_Time.z * _SwingXParams.y + vertex.y * _SwingXParams.z) * TWO_PI);
float swingZ = _SwingZParams.x * sin(frac(_Time.z * _SwingZParams.y + vertex.y * _SwingZParams.z) * TWO_PI);
vertex.xz += float2(swingX, swingZ) * color.r;
// 幽灵摇头
float radY = radians(_ShakeYParams.x) * (1.0 - color.r) * sin(frac(_Time.z * _ShakeYParams.y - color.g * _ShakeYParams.z) * TWO_PI);
float sinY, cosY = 0;
sincos(radY, sinY, cosY);
vertex.xz = float2(
vertex.x * cosY - vertex.z * sinY,
vertex.x * sinY + vertex.z * cosY
);
// 幽灵起伏
float swingY = _SwingYParams.x * sin(frac(_Time.z * _SwingYParams.y - color.g * _SwingYParams.z) * TWO_PI);
vertex.y += swingY;
// 处理顶点色
float lightness = 1.0 + color.g * 1.0 + scale * 2.0;
color = float3(lightness, lightness, lightness);
}
// 输入结构>>>顶点Shader>>>输出结构
VertexOutput vert (VertexInput v)
{
VertexOutput o = (VertexOutput)0;
AnimGhost(v.vertex.xyz, v.color.rgb);
o.pos = UnityObjectToClipPos(v.vertex); // 顶点位置 OS>CS
o.uv = TRANSFORM_TEX(v.uv, _MainTex); // UV信息 支持TilingOffset
o.color = v.color;
return o;
}
// 输出结构>>>像素
half4 frag(VertexOutput i) : COLOR
{
half4 var_MainTex = tex2D(_MainTex, i.uv); // 采样贴图 RGB颜色 A透贴
half3 finalRGB = var_MainTex.rgb * i.color.rgb;
half opacity = var_MainTex.a * _Opacity;
return half4(finalRGB * opacity, opacity); // 返回值
}
ENDCG
}
}
}