当一个圆在地面上沿直线匀速滚动时,圆上固定点的运动轨迹称为旋轮线(或摆线、圆滚线)。本文实现的卷轴特效使用了旋轮线相关理论。
以下是卷轴特效原理及公式推导,将屏幕坐标 (x) 映射到纹理坐标 (u)。
注意:屏幕坐标 x 值域为 [0, ScreenWidth],这里已归一化到 [0, 1]。
本文代码资源见→Unity3D Shader卷轴滚动特效。
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("RollTex");backTex = Resources.Load("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 AlwaysCull OffZWrite OffFog { 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; // 滚动时间float4 roll(float rho, float v){ // 滚动变换, 将屏幕坐标映射到纹理坐标if (rho < _theta * _r * _t - _r){return tex2D(_BackTex, float2(rho, v));}else if (rho < _theta * _r * _t){float a = _theta * _r * _t - rho;float phi = acos(a / _r);float u = _theta * _r * _t - (UNITY_HALF_PI + phi) * _r;if (u > 0){return tex2D(_RollTex, float2(u, v)) * pow(sin(phi), 2);}u = _theta * _r * _t - (UNITY_HALF_PI - phi) * _r;return tex2D(_MainTex, float2(u, v)); // 刚开始卷动时会触发}else if (rho < _theta * _r * _t + _r){float a = rho - _theta * _r * _t;float phi = acos(a / _r);float u = _theta * _r * _t - (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
}
下一篇:队列(C语言实现)