写在前面:
这个系列来源于书籍冯乐乐老师的《unity shader 入门精要》,
参考的代码库“https://github.com/candycat1992/Unity_Shaders_Book”
我自己跟着写了一遍代码,并在代码块里给shader代码加上了比较详细的注释
更为仔细的解释和unity shader原理和知识书里都有,本blog不做详细解释,推荐买书来看。
本项目的所有代码在https://github.com/takashiwangbh/Unity-shader-effect-reproduction/tree/main
介绍
在unity中,我们可以创建很多不同的动画,有使用相机的,使用关键节点,使用脚本等,如果想创建一个动画,那方法还是比较多的,但是在游戏中,在一些经常会重复的动画,比如2D游戏的背景动画或者某一个游戏中无人机的上下摆动模拟动画,基本都是使用脚本或者shader去制作的。这期就介绍怎么使用shader制作序列帧动画,滚动动画和顶点动画。
这期效果图贴不上来,我的github仓库的readme文件有效果预览可以去那里看
序列帧动画
这个动画有点类似于定格动画,也就是我们要准备一个包括每一帧的纹理,shader会帮助一帧一帧的播放。
创建一个shader给材质,再创建一个平面(Quad),将材质赋给平面。
由于我们的贴图是8X8的,所以要将horizontal和vertical设为8
shader code
Shader "Image Sequence Animation" {
Properties {
// 定义着色器的属性
_Color ("Color Tint", Color) = (1, 1, 1, 1) // 颜色叠加,用于调整纹理颜色
_MainTex ("Image Sequence", 2D) = "white" {
} // 用于播放动画的图片序列纹理
_HorizontalAmount ("Horizontal Amount", Float) = 4 // 横向切分的数量
_VerticalAmount ("Vertical Amount", Float) = 4 // 纵向切分的数量
_Speed ("Speed", Range(1, 100)) = 30 // 动画播放速度
}
SubShader {
Tags {
// 设置渲染队列和属性,确保透明对象正确渲染
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
}
Pass {
Tags {
"LightMode"="ForwardBase" } // 使用正向渲染路径
ZWrite Off // 关闭深度写入,因为是透明对象
Blend SrcAlpha OneMinusSrcAlpha // 设置透明混合模式
CGPROGRAM
#pragma vertex vert // 定义顶点着色器
#pragma fragment frag // 定义片段着色器
#include "UnityCG.cginc" // 引入 Unity 常用的工具函数库
// 声明属性和变量
fixed4 _Color; // 颜色叠加属性
sampler2D _MainTex; // 图片序列纹理
float4 _MainTex_ST; // 纹理的缩放和偏移
float _HorizontalAmount; // 横向切分的数量
float _VerticalAmount; // 纵向切分的数量
float _Speed; // 动画播放速度
// 顶点输入数据结构
struct a2v {
float4 vertex : POSITION; // 顶点位置
float2 texcoord : TEXCOORD0; // 顶点的纹理坐标
};
// 顶点着色器输出数据结构
struct v2f {
float4 pos : SV_POSITION; // 裁剪空间位置
float2 uv : TEXCOORD0; // 纹理坐标
};
// 顶点着色器
v2f vert (a2v v) {
v2f o;
// 转换到裁剪空间坐标
o.pos = UnityObjectToClipPos(v.vertex);
// 转换纹理坐标
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
// 片段着色器
fixed4 frag (v2f i) : SV_Target {
// 根据当前时间计算当前帧
float time = floor(_Time.y * _Speed); // 使用 `_Time.y` 获取全局时间
float row = floor(time / _HorizontalAmount); // 当前帧在第几行