10.翻页效果-具体实现
10.1 知识点
知识回顾:基本原理
旋转关键点
利用旋转矩阵基于某一轴进行旋转。
注意:根据模型中心点决定平移方向,需先平移,再旋转,最后平移回去。起伏关键点
利用三角函数 Sin 使顶点在 Y 轴方向产生位置偏移。
在 0°、90°、180°之间变化时,0°和180°不需要起伏效果,而 90°时起伏效果最大(页面最弯曲)。
书本翻页效果的具体实现
主要步骤
- 新建 Shader PageTurning,删除无用代码。
- 属性声明与属性映射:
- 正面纹理:
_MainTex
- 背面纹理:
_BackTex
- 翻页进度(0~180°):
_AngleProgress
- Y 轴弯曲程度(0~1):
_WeightY
- X 轴收缩程度(0~1):
_WeightX
- 波长(0~3):
_WaveLength
- 平移距离:
_MoveDis
- 正面纹理:
- 为实现双面渲染,关闭背面剔除。
- 使用 VFACE 语义时,需加入编译指令:
#pragma target 3.0
。 - 定义结构体,包含顶点与 UV 信息。
- 顶点着色器:
- 利用
sincos
函数获得当前翻页角度对应的正弦和余弦值,构建旋转矩阵。 - 旋转前先对顶点进行平移;旋转后再平移回去,并将处理后的顶点转换到裁剪空间,同时赋予 UV。
- 在旋转矩阵之前加入起伏相关的计算,实现顶点起伏效果,增强视觉层次感与动态表现。
- 利用
- 片元着色器:
根据 VFACE 语义判断正反面,分别采样对应纹理。
新建 Shader PageTurning,删除无用代码
Shader "Unlit/Lesson10_PageTurning"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {} // 正面纹理
}
SubShader
{
Tags
{
"RenderType"="Opaque"
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f
{
float2 uv : TEXCOORD0; // 纹理坐标
float4 vertex : SV_POSITION; // 顶点坐标
};
// 正面和背面纹理采样器
sampler2D _MainTex;
v2f vert(appdata_base appdata_base )
{
}
fixed4 frag(v2f v2f , fixed face:VFACE) : SV_Target
{
}
ENDCG
}
}
}
声明属性和属性映射
包括正面纹理、背面纹理、翻页进度(0180°)、Y 轴弯曲程度(01)、X 轴收缩程度(01)、波长(03)和平移距离
Properties
{
_MainTex ("Texture", 2D) = "white" {} // 正面纹理
_BackTex("BackTex", 2D) = ""{} // 背面纹理
_AngleProgress("_AngleProgress", Range(0,180)) = 0 // 翻页角度进度,范围为0到180度
_WeightX("WeightX", Range(0,1)) = 0 // X轴方向的缩放权重
_WeightY("WeightY", Range(0,1)) = 0 // Y轴方向的弯曲权重
_WaveLength("WaveLength", Range(0,3))= 0 // 波动的波长
_MoveDis("MoveDis", Float) = 0 // 平移的距离
}
// 正面和背面纹理采样器
sampler2D _MainTex;
sampler2D _BackTex;
// 可调参数
float _AngleProgress; // 翻页角度进度
fixed _WeightX; // X轴缩放权重
fixed _WeightY; // Y轴弯曲权重
float _WaveLength; // 波长
float _MoveDis; // 平移距离
双面渲染设置
由于要双面渲染,要关闭背面剔除,确保双面都可见
Cull Off // 关闭背面剔除,确保双面都可见
使用 VFACE 语义的编译指令
由于要使用VFACE语义,加入编译指令 target 3.0
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
结构体定义:顶点和 UV
struct v2f
{
float2 uv : TEXCOORD0; // 纹理坐标
float4 vertex : SV_POSITION; // 顶点坐标
};
在顶点着色器中,首先需要实现一个基本的翻页效果。通过使用 sincos
函数,可以获得当前翻页精度对应的正弦值和余弦值,基于这些值构建旋转矩阵。在进行旋转之前,需要先对顶点进行平移操作,然后使用构建好的旋转矩阵对顶点进行旋转处理。旋转完成后,再将顶点平移回原位置,并将处理后的顶点转换到裁剪空间中,同时完成 UV 的赋值操作。为了增加动态的视觉效果,可以在旋转矩阵之前加入与起伏相关的计算,从而实现顶点的起伏效果,使翻页更具视觉层次感和动态表现力。
v2f vert(appdata_base appdata_base)
{
v2f v2f;
// _AngleProgress 是翻页的旋转角度,单位为度(0°~180°)。
// sincos 函数用于同时计算给定角度的正弦值 (sin) 和余弦值 (cos),以避免分别调用 sin 和 cos 提高效率。
// radians(_AngleProgress) 将角度转换为弧度,因为 sincos 函数需要弧度作为输入。
// 输出结果:
// - s:sin(弧度),即对应角度的正弦值,用于计算旋转后顶点的 Y 轴分量变化。
// - c:cos(弧度),即对应角度的余弦值,用于计算旋转后顶点的 X 轴分量变化。
float s; // 存储 sin(_AngleProgress) 的结果
float c; // 存储 cos(_AngleProgress) 的结果
sincos(radians(_AngleProgress), s, c);
// Z轴旋转矩阵,用于模拟翻页效果
float4x4 rotationM = {
c, -s, 0, 0,
s, c, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
};
// 基于 Z 轴旋转,旋转前先对顶点沿 X 轴平移
appdata_base.vertex += float4(_MoveDis, 0, 0, 0);
// 计算角度进度的权重,用于动态控制形变效果,0°和180°时权重为0,90°时权重为1
float weight = 1 - abs(90 - _AngleProgress) / 90;
// Y 轴上下起伏处理,通过正弦函数模拟波动
appdata_base.vertex.y += sin(appdata_base.vertex.x * _WaveLength) * weight * _WeightY;
// X 轴缩放处理,基于权重进行适度压缩
appdata_base.vertex.x -= appdata_base.vertex.x * weight * _WeightX;
// 应用旋转变换
float4 position = mul(rotationM, appdata_base.vertex);
// 旋转后将顶点移回原位置
position -= float4(_MoveDis, 0, 0, 0);
// 转换为裁剪空间坐标
v2f.vertex = UnityObjectToClipPos(position);
v2f.uv = appdata_base.texcoord; // 保留纹理坐标
return v2f;
}
片元着色器:通过 VFACE 语义采样正反面纹理
fixed4 frag(v2f v2f, fixed face:VFACE) : SV_Target
{
// 根据正反面选择对应纹理进行采样
fixed4 col = face > 0 ? tex2D(_MainTex, v2f.uv) : tex2D(_BackTex, v2f.uv);
return col;
}
查看效果
通过上述 Shader 的实现,将其应用到书本翻页效果中,即可在运行时观察到动态的翻页效果和相应的起伏变化。
10.2 知识点代码
Lesson10_PageTurning.shader
Shader "Unlit/Lesson10_PageTurning"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {} // 正面纹理
_BackTex("BackTex", 2D) = ""{} // 背面纹理
_AngleProgress("_AngleProgress", Range(0,180)) = 0 // 翻页角度进度,范围为0到180度
_WeightX("WeightX", Range(0,1)) = 0 // X轴方向的缩放权重
_WeightY("WeightY", Range(0,1)) = 0 // Y轴方向的弯曲权重
_WaveLength("WaveLength", Range(0,3))= 0 // 波动的波长
_MoveDis("MoveDis", Float) = 0 // 平移的距离
}
SubShader
{
Tags
{
"RenderType"="Opaque"
}
Cull Off // 关闭背面剔除,确保双面都可见
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0
#include "UnityCG.cginc"
struct v2f
{
float2 uv : TEXCOORD0; // 纹理坐标
float4 vertex : SV_POSITION; // 顶点坐标
};
// 正面和背面纹理采样器
sampler2D _MainTex;
sampler2D _BackTex;
// 可调参数
float _AngleProgress; // 翻页角度进度
fixed _WeightX; // X轴缩放权重
fixed _WeightY; // Y轴弯曲权重
float _WaveLength; // 波长
float _MoveDis; // 平移距离
v2f vert(appdata_base appdata_base)
{
v2f v2f;
// _AngleProgress 是翻页的旋转角度,单位为度(0°~180°)。
// sincos 函数用于同时计算给定角度的正弦值 (sin) 和余弦值 (cos),以避免分别调用 sin 和 cos 提高效率。
// radians(_AngleProgress) 会将角度转换为弧度制,因为 sincos 函数需要弧度作为输入。
// 输出结果:
// - s:sin(弧度),即对应角度的正弦值,用于计算旋转后顶点的 Y 轴分量变化。
// - c:cos(弧度),即对应角度的余弦值,用于计算旋转后顶点的 X 轴分量变化。
// sincos 在这里的作用是为后续旋转矩阵的构建提供所需的正弦和余弦值。
float s; // 用于存储 sin(_AngleProgress) 的结果
float c; // 用于存储 cos(_AngleProgress) 的结果
sincos(radians(_AngleProgress), s, c);
// Z轴旋转矩阵,用于模拟翻页效果
float4x4 rotationM = {
c, -s, 0, 0,
s, c, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
};
// 由于基于Z轴旋转 在旋转之前对顶点沿X轴进行平移
appdata_base.vertex += float4(_MoveDis, 0, 0, 0);
// 计算角度进度的权重,用于动态控制形变效果 0和180的时候是0 咋子90度的时候是1
float weight = 1 - abs(90 - _AngleProgress) / 90;
// Y轴上下起伏处理,通过正弦函数模拟波动
appdata_base.vertex.y += sin(appdata_base.vertex.x * _WaveLength) * weight * _WeightY;
// X轴缩放处理,基于权重进行适度压缩
appdata_base.vertex.x -= appdata_base.vertex.x * weight * _WeightX;
// 应用旋转变换
float4 position = mul(rotationM, appdata_base.vertex);
// 旋转后将顶点移回原来的位置
position -= float4(_MoveDis, 0, 0, 0);
// 转换为裁剪空间坐标
v2f.vertex = UnityObjectToClipPos(position);
v2f.uv = appdata_base.texcoord; // 保留纹理坐标
return v2f;
}
fixed4 frag(v2f v2f, fixed face:VFACE) : SV_Target
{
// 根据正反面选择对应纹理进行采样
fixed4 col = face > 0 ? tex2D(_MainTex, v2f.uv) : tex2D(_BackTex, v2f.uv);
return col;
}
ENDCG
}
}
}
Lesson10_翻页效果_具体实现.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson10_翻页效果_具体实现 : MonoBehaviour
{
void Start()
{
#region 知识回顾 基本原理
//1.旋转关键点
//利用旋转矩阵基于某一个轴进行旋转
//注意:根据模型中心点决定平移方向
// 先平移 再旋转 再平移回去
//2.起伏关键点
//利用三角函数Sin让顶点在Y轴进行位置偏移
//并且0~90~180度之间变化时,0和180度不需要起伏感,90度时起伏感最大(页面最弯曲)
#endregion
#region 知识点 书本翻页效果的具体实现
//1.新建Shader PageTurning
// 删除无用代码
//2.属性声明 属性映射
// 正面纹理 _MainTex
// 背面纹理 _BackTex
// 翻页进度(0~180度) _AngleProgress
// y轴弯曲程度(0~1) _WeightY
// x轴收缩程度(0~1) _WeightY
// 波长(0~3) _WaveLength
// 平移距离 _MoveDis
//3.由于要双面渲染
// Cull Off
//4.由于要使用VFACE语义,加入编译指令
// #pragma target 3.0
//5.结构体
// 顶点和UV
//6.顶点着色器
// 先实现基本的翻页效果
// 1.利用sincos函数得到当前翻页精度对应的sin和cos值
// 2.基于得到的值,构建旋转矩阵
// 3.旋转前先平移
// 3.基于旋转矩阵进行顶点旋转
// 4.旋转结束再平移回去
// 5.将处理完毕的顶点转换到裁剪空间中
// 6.UV赋值
// 实现起伏效果
// 将起伏相关的计算加入到旋转矩阵之前
//7.片元着色器
// 通过VFACE语义判断正反面进行对应的采样即可
#endregion
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com