我们的美术希望游戏可以更炫一点,想在刀光特效上加上空气扭曲的效果,就是类似燃烧的火焰上方空气扰动的那种效果。这个效果其实很多游戏都有。网络上流传的有一个叫heat_distortion的shader,这个shader使用grabTexture实现的,实现方法很多人都讲过,这里就不细说了。grabTexture比较坑爹,在移动设备上效率很成问题,而且有些机型也不支持,所以我们需要用其他办法实现。
我们用的是后处理的办法.
1. 我们在特效上挂一个片A,给A单独设一个layer L1,然后在Main Camera上挂一个带摄像机的子节点Sub(back ground清成黑色),MainCamera的mask剔除掉L1 ,让Sub只渲染L1。
2. A的材质shader使用heat_distortion,这个shader通过一张噪声图计算出一个UV值,从mainTex中取颜色给后面后处理的时候当UV用。
3. main camera挂载脚步DistortImageEffect。在OnRenderImage中获取Sub的RenderTexture,将这个RT给shader:IE_heat_distortion使用并生成最终结果。
代码:
using UnityEngine;
using System.Collections;
public class DistortImageEffect : MonoBehaviour
{
// 记录参数的rt
RenderTexture rt;
public Material IEHeatMaterial = null;
public Camera sub = null;
// Use this for initialization
void Start()
{
Camera cam = GetComponent<Camera>();
sub.fieldOfView = cam.fieldOfView;
sub.nearClipPlane = cam.nearClipPlane;
sub.farClipPlane = cam.farClipPlane;
sub.aspect = cam.aspect;
int w = Screen.width / 2;
int h = Screen.height / 2;
if (w > SystemInfo.maxTextureSize)
{
h = h * SystemInfo.maxTextureSize / w;
w = SystemInfo.maxTextureSize;
}
if (h > SystemInfo.maxTextureSize)
{
w = w * SystemInfo.maxTextureSize / h;
h = SystemInfo.maxTextureSize;
}
rt = RenderTexture.GetTemporary(Screen.width / 2, Screen.height / 2);
sub.targetTexture = rt;
}
void OnDestroy()
{
RenderTexture.ReleaseTemporary(rt);
}
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (sub == null)
{
Graphics.Blit(source, destination);
return;
}
IEHeatMaterial.SetTexture("_NormalTex", rt);
Graphics.Blit(source, destination, IEHeatMaterial, 0);
}
}
Shader "Distortion" {
Properties {
_NoiseTex ("Noise (RG)", 2D) = "white" {}
_MainTex ("Alpha (A)", 2D) = "white" {}
_HeatTime ("Heat Time", range (0,1.5)) = 1
_HeatForce ("Heat Force", float) = 0.1
}
Category {
Tags { "RenderType"="Opaque" }
Cull Off Lighting Off ZWrite Off Blend Off Fog {Mode Off}
LOD 100
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
float2 texcoord: TEXCOORD0;
};
struct v2f {
float4 vertex : POSITION;
float4 uvgrab : TEXCOORD0;
float2 uvmain : TEXCOORD1;
float2 uv : TEXCOORD2;
};
sampler2D _NoiseTex;
float4 _NoiseTex_ST;
sampler2D _MainTex;
float4 _MainTex_ST;
float _HeatForce;
float _HeatTime;
v2f vert (appdata_t v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
#if UNITY_UV_STARTS_AT_TOP
float scale = -1.0;
#else
float scale = 1.0;
#endif
o.uvgrab.xy = (float2(o.vertex.x, o.vertex.y*scale) + o.vertex.w) * 0.5;
o.uvgrab.zw = o.vertex.zw;
o.uvmain = TRANSFORM_TEX( v.texcoord, _MainTex );
o.uv = v.texcoord;
return o;
}
half4 frag( v2f i ) : COLOR
{
half4 offsetColor1 = tex2D(_NoiseTex, i.uvmain + _Time.xz*_HeatTime);
half4 offsetColor2 = tex2D(_NoiseTex, i.uvmain - _Time.yx*_HeatTime);
i.uvgrab.x += ((offsetColor1.r + offsetColor2.r) - 1) * _HeatForce;
i.uvgrab.y += ((offsetColor1.g + offsetColor2.g) - 1) * _HeatForce;
return tex2D(_MainTex, i.uvgrab);
}
ENDCG
}
}
}
Fallback off
}
Shader "IEHeatDistortion" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_NormalTex ("Noise Texture (RGB)", 2D) = "white" {}
}
Category {
ZTest Off Cull Off ZWrite Off Blend Off
SubShader {
Pass {
Fog { Mode off }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
float2 texcoord: TEXCOORD0;
};
struct v2f {
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
float4 _MainTex_ST;
float4 _NormalTex_ST;
sampler2D _NormalTex;
sampler2D _MainTex;
v2f vert (appdata_t v)
{
v2f o;
v.vertex.z = 0.1;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.texcoord;
return o;
}
half4 frag( v2f i ) : COLOR
{
half4 col = tex2D( _NormalTex, i.uv);
half4 tint = tex2D( _MainTex, i.uv + col);
return tint;
}
ENDCG
}
}
}
}