Unity shader实现梦幻旋屏特效
发表于2017-06-01
游戏开发中,往往会用到一些屏幕特效,这样做出来的效果会让玩家更能沉浸在游戏中,下面就给大家介绍下用shader实现一种梦幻的旋屏特效。
下图展现的是一种“旋屏”效果,它会旋转屏幕图像,且距离中心点越远的点旋转角度越大。这种效果特别适合营造“梦幻”感,比如,在RPG游戏中,经过一段“旋屏”特效,主角穿越到了10年前。

1、编写Shader
下面的着色器代码使用了顶点/片元着色器处理旋屏特效的功能。这里定义3个属性,其中_MainTex代表屏幕贴图,_Rot 代表基准的旋转角度。核心代码在片元着色器frag中实现。
如下图所示,屏幕图像被归一到[0,1]的空间中,中心点为(0.5,0.5)。假设某个点的uv坐标为(x,y),经过一系列处理,它的坐标变为(x1,y1),而(x1,y1)便是实现旋转效果后的uv坐标。
2、使用材质
新建c#文件,编写ScreenRot类,它由一个共有变量mtl,在它的OnRenderImage方法中调用Graphics.Blit将屏幕图像(对应shader中的_MainTex)与材质混合起来。
然后给新建一个名为ScreenRot的材质,使用上述编写的Shader。然后给摄像机添加ScreenRot组件,设置刚刚创建的材质,如下图所示。

运行游戏,调整材质的“Rotation”属性,即可看到旋转特效。
这个效果能够运用在很多场合,比如使用“正向旋转→切换场景→反向旋转”实现切屏特效。

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代码如下所示:
[C#] 纯文本查看 复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | 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)与材质混合起来。
[C#] 纯文本查看 复制代码
01 02 03 04 05 06 07 08 09 10 11 12 | 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修改成下面的代码,即可让屏幕自动旋转。
[C#] 纯文本查看 复制代码
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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); } } |
这个效果能够运用在很多场合,比如使用“正向旋转→切换场景→反向旋转”实现切屏特效。