US4L5——翻页效果
US4L5——翻页效果
书本翻页效果
在游戏开发中,书本翻页效果就是,字面意思,用 Shader 模拟出书本翻页时的动态效果
这种效果常用与游戏和动画中,比如用于制作一些 3D UI,制作一些书本交互功能等等
一句话概括它的基本原理:对顶点进行平移 —> 矩阵旋转 —> 再平移,并利用三角函数相关知识制做出起伏感
关键点:
-
如何制作旋转
我们将利用以前学习的 旋转矩阵 相关知识,来对顶点进行旋转变换,
但是,旋转前我们需要先对顶点进行平移,否则模型将绕着模型中心点旋转。
因为旋转之前进行了平移,因此旋转结束后,我们需要再将顶点平移回来如果不平移,旋转效果就变成了这样:
进行平移,才能让书页沿侧边一轴旋转,至于平移的距离,由书页本身宽度的一半决定:
旋转矩阵如下:
绕 x 轴旋转 β 度,旋转矩阵为:
绕 y 轴旋转 β 度,旋转矩阵为:
绕 z 轴旋转 β 度,旋转矩阵为:
-
如何制作起伏感
只需要在旋转前利用三角函数 让顶点在 Y 轴上进行偏移即可
并且 0 ~ 90 ~ 180 度之间变换时,0 和 180 度不需要起伏感,90 度时起伏感最大(页面最弯曲)1
2
3float 波形权重 = 1 - abs(90 - 旋转角度) / 90; // 波形权重,用于控制起伏程度,90度是起伏程度最大,0和180时不起伏
v.vertex.y += sin(v.vertex.x * 波长) * 波形权重 * 起伏程度; // y轴产生上下位移顶点起伏
v.vertex.x -= v.vertex.x * 波形权重 * 收缩程度; // x轴收缩,实现起伏时缩短的效果,让翻页更真实
书本翻页效果的具体实现
-
新建 Shader,命名为
PageTurning
,删除无用代码 -
属性声明 属性映射
- 正面纹理:
_MainTex
- 背面纹理:
_BackTex
- 翻页进度 (0~180度):
_AngleProgress
- x 轴收缩程度 (0~1):
_WeightX
- y 轴弯曲程度 (0~1)
_WeightY
- 波长 (0~3):
_WaveLength
- 平移距离:
_MoveDis
1
2
3
4
5
6
7
8
9
10Properties
{
_MainTex("Texture", 2D) = "white"{} // 书页正面纹理
_BackTex("BackTex", 2D) = "white"{} // 书页背面纹理
_AngleProgress("AngleProgress", Range(0, 180)) = 0 // 翻页进度
_WeightX("WeightX", Range(0, 1)) = 0 // x轴收缩程度权重
_WeightY("WeightY", Range(0, 1)) = 0 // y轴弯曲程度权重
_WaveLength("WaveLength", Range(0, 3)) = 0 // 波长
_MoveDis("MoveDis", Float) = 0 // 平移距离,由书页宽度决定
} - 正面纹理:
-
由于要双面渲染
Cull Off
1
2
3
4
5
6SubShader
{
Tags { "RenderType"="Opaque" }
Cull Off // 正反都要渲染
Pass { /*...*/ }
} -
由于要使用
VFACE
语义,加入编译指令#pragma target 3.0
1
2
3
4
5
6
7
8CGPROGRAM
// ...
ENDCG -
结构体
顶点和UV
1
2
3
4
5struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
}; -
顶点着色器
-
先实现基本的翻页效果
- 利用
sincos()
函数得到当前翻页精度对应的sin
和cos
值 - 基于得到的值,构建旋转矩阵
- 旋转前先平移
- 基于旋转矩阵进行顶点旋转
- 旋转结束再平移回去
- 将处理完毕的顶点转换到裁剪空间中
- UV 赋值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19v2f o;
// 利用sincos函数,配合自定义参数,翻页的角度(进度)来得到对应的sin和cos值
float sinValue;
float cosValue;
sincos(radians(_AngleProgress), sinValue, cosValue);
// 构建Z轴旋转矩阵
float4x4 rotationM = {
cosValue, -sinValue, 0, 0,
sinValue, cosValue, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
};
// 因为是基于左侧绕Z轴旋转的,因此我们将其按照X轴方向进行平移,旋转完毕后在平移回来
v.vertex += float4(_MoveDis, 0, 0, 0);
float4 pos = mul(rotationM, v.vertex);
pos -= float4(_MoveDis, 0, 0, 0);
o.vertex = UnityObjectToClipPos(pos);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); - 利用
-
然后实现起伏效果,将起伏相关的计算加入到旋转矩阵之前
1
2
3
4// 进行起伏效果
float weight = 1 - abs(90 - _AngleProgress) / 90; // 起伏权重计算
v.vertex.y += sin(v.vertex.x * _WaveLength) * weight * _WeightY; // Y轴上下起伏
v.vertex.x -= v.vertex.x * weight * _WeightX; // X轴权重
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30v2f vert (appdata_base v)
{
v2f o;
// 利用sincos函数,配合自定义参数,翻页的角度(进度)来得到对应的sin和cos值
float sinValue;
float cosValue;
sincos(radians(_AngleProgress), sinValue, cosValue);
// 构建Z轴旋转矩阵
float4x4 rotationM = {
cosValue, -sinValue, 0, 0,
sinValue, cosValue, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
};
// 进行起伏效果
float weight = 1 - abs(90 - _AngleProgress) / 90; // 起伏权重计算
v.vertex.y += sin(v.vertex.x * _WaveLength) * weight * _WeightY; // Y轴上下起伏
v.vertex.x -= v.vertex.x * weight * _WeightX; // X轴权重
// 因为是基于左侧绕Z轴旋转的,因此我们将其按照X轴方向进行平移,旋转完毕后在平移回来
v.vertex += float4(_MoveDis, 0, 0, 0);
float4 pos = mul(rotationM, v.vertex);
pos -= float4(_MoveDis, 0, 0, 0);
o.vertex = UnityObjectToClipPos(pos);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
} -
-
片元着色器
通过
VFACE
语义判断正反面进行对应的采样即可1
2
3
4
5
6fixed4 frag (v2f i, fixed face : VFACE) : SV_Target
{
// 通过face来进行正反面判断,因为face的语义是VFACE,因此Unity Shader会自动传入对应的参数
fixed4 color = face > 0 ? tex2D(_MainTex, i.uv) : tex2D(_BackTex, i.uv);
return color;
}
显示效果(旋转角度60度,x轴收缩程度权重,y轴弯曲程度权重都是0.25,波长为0.75,平移距离为5):
可见,随着旋转角度的变化,书页本身呈现出翻页旋转效果,且具有起伏感,之后可以通过 C# 代码去控制翻页角度