游戏观察1月19日消息,在游戏场景的设计中,经常会用到需要设计屏幕特效的时候,比如“旋屏”就是一种被经常使用的特效,应用于穿越等情景中。下面我们就来了解一下如何通过Unity特效设计实现“梦幻旋屏”效果。
游戏开发中,往往会用到一些屏幕特效。下图展现的是一种“旋屏”效果,它会旋转屏幕图像,且距离中心点越远的点旋转角度越大。这种效果特别适合营造“梦幻”感,比如,在RPG游戏中,经过一段“旋屏”特效,主角穿越到了10年前。
1、编写Shader
下面的着色器代码使用了顶点/片元着色器处理旋屏特效的功能。这里定义3个属性,其中_MainTex代表屏幕贴图,_Rot 代表基准的旋转角度。核心代码在片元着色器frag中实现。
如下图所示,屏幕图像被归一到[0,1]的空间中,中心点为(0.5,0.5)。假设某个点的uv坐标为(x,y),经过一系列处理,它的坐标变为(x1,y1),而(x1,y1)便是实现旋转效果后的uv坐标。
由“float distance = sqrt((i.uv.x - 0.5)*(i.uv.x - 0.5) +(i.uv.y - 0.5)*(i.uv.y - 0.5));”可以计算点到屏幕中心的距离distance。由于距离越远旋转角度越大,使用“_Rot *=distance”将角度增量基准与距离联系起来,即可获取需要旋转的角度:angle = _Rot*distance + A。
由反正切公式可得∠A = atan((y - 0.5)/(x - 0.5)),由于atan的取值为[-π/2,π/2],还需根据y值确定∠A所在的象限,故而∠A = step(x,0.5)*PI+ atan((y - 0.5)/(x - 0.5)) 。计算∠A 后,便可由angle = _Rot*distance + A计算总的旋转角度。
前面已经计算了点到屏幕中心的距离distance,故而:
x1 = 0.5 + distance *cos(angle)
y1 = 0.5 + distance *sin(angle)
Shader代码如下所示:
Shader "Lpy/ScreenRot"
{
Properties
{
_MainTex ("Main Tex", 2D) = "white" {}
_Rot ("Rotation", float) = 0
}
SubShader
{
Tags {"Queue"="Geometry"}
Pass
{
Tags { "LightMode"="ForwardBase" }
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#define PI 3.14159265358979
sampler2D _MainTex;
float _Rot;
struct a2v
{
float4 vertex : POSITION;
float3 texcoord : TEXCOORD0;
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (a2v v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
//与中心点(0.5,0.5)的距离
float distance = sqrt((i.uv.x - 0.5)*(i.uv.x - 0.5) +(i.uv.y - 0.5)*(i.uv.y - 0.5));
//距离越大,旋转角度越大
_Rot *=distance;
//计算旋转角度
float angle = step(i.uv.x,0.5)*PI+ atan((i.uv.y - 0.5)/(i.uv.x - 0.5)) + _Rot;
//计算坐标
i.uv.x = 0.5 + distance *cos(angle);
i.uv.y = 0.5 + distance *sin(angle);
fixed4 c = tex2D(_MainTex, i.uv);
return c;
}
ENDCG
}
}
FallBack "Specular"
}
2、使用材质
新建c#文件,编写ScreenRot类,它由一个共有变量mtl,在它的OnRenderImage方法中调用Graphics.Blit将屏幕图像(对应shader中的_MainTex)与材质混合起来。
using UnityEngine;
using System.Collections;
public class ScreenRot : MonoBehaviour
{
public Material mtl;
void OnRenderImage(RenderTexture src, RenderTexture dest)
{
Graphics.Blit (src, dest,mtl);
}
}
然后给新建一个名为ScreenRot的材质,使用上述编写的Shader。然后给摄像机添加ScreenRot组件,设置刚刚创建的材质,如下图所示。
运行游戏,调整材质的“Rotation”属性,即可看到旋转特效。
3、代码中引用
Shader中并没有涉及时间的控制,旋转速度需要由c#代码控制,将ScreenRot修改成下面的代码,即可让屏幕自动旋转。
using UnityEngine;
using System.Collections;
public class ScreenRot : MonoBehaviour
{
public Material mtl;
public float rot;
// Update is called once per frame
void Update ()
{
rot += 0.1f;
}
void OnRenderImage(RenderTexture src, RenderTexture dest)
{
if (rot == 0.0)
return;
mtl.SetFloat("_Rot", rot);
Graphics.Blit (src, dest,mtl);
}
}
这个效果能够运用在很多场合,比如使用“正向旋转→切换场景→反向旋转”实现切屏特效。