美文网首页Unity3D
【Unity3D】卷轴特效

【Unity3D】卷轴特效

作者: LittleFatSheep | 来源:发表于2023-03-13 09:32 被阅读0次

    1 原理

    当一个圆在地面上沿直线匀速滚动时,圆上固定点的运动轨迹称为旋轮线(或摆线、圆滚线)。本文实现的卷轴特效使用了旋轮线相关理论。

    以下是卷轴特效原理及公式推导,将屏幕坐标 (x) 映射到纹理坐标 (u)。

    注意:屏幕坐标 x 值域为 [0, ScreenWidth],这里已归一化到 [0, 1]。

    本文代码资源见→Unity3D Shader卷轴滚动特效

    2 代码实现

    RollEffect.cs

    using UnityEngine;
     
    [RequireComponent(typeof(Camera))]  // 屏幕后处理特效一般都需要绑定在像机上
    public class RollEffect : MonoBehaviour {
        public float radius = 0.05f; // 圆半径
        public float rollSpeed = 0.8f; // 圆滚动角速度
        private Texture rollTex; // 滚动轴纹理
        private Texture backTex; // 底部背景纹理
        private float rollTime = 0; // 滚动时间
        private float maxRollTime; // 最长滚动时间
        private float rollDirection = 1; // 滚动方向(1: 向右, -1: 向左)
        private Material rollMaterial; // 滚动特效材质
        private bool enableRoll = false; // 滚动特效开关
     
        private void Awake() {
            rollMaterial = new Material(Shader.Find("Custom/Curl/Roll"));
            rollMaterial.hideFlags = HideFlags.DontSave;
            rollTex = Resources.Load<Texture>("RollTex");
            backTex = Resources.Load<Texture>("BackTex");
        }
     
        private void Update() {
            if (Input.GetMouseButton(0)) {
                rollTime = 0;
                maxRollTime = 1 / rollSpeed / radius;
                enableRoll = true;
            }
        }
     
        private void OnRenderImage (RenderTexture source, RenderTexture destination) {
            if (enableRoll) {
                rollMaterial.SetTexture("_RollTex", rollTex);
                rollMaterial.SetTexture("_BackTex", backTex);
                rollMaterial.SetFloat("_theta", rollSpeed);
                rollMaterial.SetFloat("_r", radius);
                rollMaterial.SetFloat("_t", rollTime);
                IncreaseTime();
                Graphics.Blit (source, destination, rollMaterial);
            }  else {
                Graphics.Blit (source, destination);
            }
        }
     
        private void IncreaseTime() { // 时间自增
            rollTime += rollDirection * Time.deltaTime;
            if (rollTime > maxRollTime) {
                rollTime = maxRollTime;
                rollDirection = -rollDirection; // 反向卷轴
            } else if (rollTime < 0) {
                rollTime = 0;
                rollDirection = -rollDirection;
            }
        }
    }
    

    说明: RollEffect 脚本组件需要挂在相机上。

    Roll.shader

    Shader "Custom/Curl/Roll"
    {
        Properties 
        {
            _MainTex ("mainTex", 2D) = "white" {}
            _RollTex ("rollTex", 2D) = "white" {}
            _BackTex ("backTex", 2D) = "white" {}
        }
     
        SubShader 
        {
            Pass
            {
                ZTest Always
                Cull Off
                ZWrite Off
                Fog { Mode off }
     
                CGPROGRAM
     
                #pragma vertex vert_img // UnityCG.cginc中定义了vert_img方法, 对vertex和texcoord进行了处理, 输出v2f_img中的pos和uv
                #pragma fragment frag
                #pragma fragmentoption ARB_precision_hint_fastest
     
                #include "UnityCG.cginc"
     
                sampler2D _MainTex;
                sampler2D _RollTex; // 滚动轴纹理
                sampler2D _BackTex; // 底部背景纹理
                float _theta; // 圆滚动角速度
                float _r; // 圆半径
                float _t; // 滚动时间
     
                fixed4 roll(float rho, float v)
                { // 滚动变换, 将屏幕坐标映射到纹理坐标
                    float trt = _theta * _r * _t;
                    if (rho < trt - _r)
                    {
                        return tex2D(_BackTex, float2(rho, v));
                    }
                    else if (rho < trt)
                    {
                        float a = trt - rho;
                        float phi = acos(a / _r);
                        float u = trt - (UNITY_HALF_PI + phi) * _r;
                        if (u > 0)
                        {
                            return tex2D(_RollTex, float2(u, v)) * pow(sin(phi), 2);
                        }
                        u = trt - (UNITY_HALF_PI - phi) * _r;
                        return tex2D(_MainTex, float2(u, v)); // 刚开始卷动时会触发
                    }
                    else if (rho < trt + _r)
                    {
                        float a = rho - trt;
                        float phi = acos(a / _r);
                        float u = trt - (3 * UNITY_HALF_PI - phi) * _r;
                        if (u > 0)
                        {
                            return tex2D(_RollTex, float2(u, v)) * pow(sin(phi), 2);
                        }
                        return tex2D(_MainTex, float2(rho, v)); // 刚开始卷动时会触发
                    }
                    else
                    {
                        return tex2D(_MainTex, float2(rho, v));
                    }
                }
     
                fixed4 frag(v2f_img i) : SV_Target // uv坐标的计算不能在顶点着色器中进行, 因为屏后处理的顶点只有屏幕的4个角顶点
                {
                    return roll(i.pos.x / _ScreenParams.x, i.uv.y);
                }
     
                ENDCG
            }
        }
     
        Fallback off
    }
    

    3 运行效果

    4 推荐阅读

    声明:本文转自【Unity3D】卷轴特效

    相关文章

      网友评论

        本文标题:【Unity3D】卷轴特效

        本文链接:https://www.haomeiwen.com/subject/wgpohdtx.html