【Unity3D】卷轴特效

33 篇文章 11 订阅

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 推荐阅读

  • 11
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

little_fat_sheep

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值