10.翻页效果具体实现

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

×

喜欢就点赞,疼爱就打赏