Unity特效扭曲效果(參照崩壞3處理螢幕方式實現)
最近專案要用到扭曲效果,查了一些資料,關於崩壞3扭曲效果分析
http://forum.china.unity3d.com/forum.php?mod=viewthread&tid=32271&page=1&authorid=1
關於崩壞的效果
下面是關於熱扭曲原理,來自上面連結
在渲染扭曲效果的過程中,我們使用3個通道來儲存扭曲的渲染結果,2個用於儲存UV偏移,另一個用於儲存扭曲強度Mask,扭曲強度Mask用於執行深度剪裁和基於距離的強度控制。
使用單獨的Pass渲染扭曲結果到幀緩衝紋理對於移動平臺來說開銷較大,所以我們在最終的後處理中整合應用了扭曲效果,相比前者要快很多。 但這種方法也可能導致靠前面的物體由於沒有分層處理而混入後面扭曲材質的問題,不過考慮到移動平臺的效能限制,相對於整體效果而言這種妥協是值得的。
關於對應的實現方法
1.用替換shader方法來渲染一張扭曲mask圖,原來shader有一個來顏色儲存扭曲強度和扭曲偏移,替換的shader直接渲染這個顏色,扭曲強度還會跟距離有關
2.把mask圖對應的顏色和螢幕對於的畫素進行偏移
先放圖工程效果圖,最後面有工程地址
實現過程
1.改一下官方粒子shader,第一加個顏色,第二加rendertype標籤用於替換渲染用,這裡我隨便加一個shader作為例子,之後需要什麼就加什麼
下面是我加自帶的Mobile/Additive加了_DistortColor和加了標籤"RenderType"="Distortion"
_DistortColor用處
綠色通道為強度,紅色通道為x偏移幅度,藍色通道為y偏移幅度
// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt) // Simplified Additive Particle shader. Differences from regular Additive Particle one: // - no Tint color // - no Smooth particle support // - no AlphaTest // - no ColorMask Shader "Effect/Distortion/Mobile/Additive" { Properties { _MainTex ("Particle Texture", 2D) = "white" {} _DistortColor("Distort Color", Color) = (0,1,0,1) } Category { Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Distortion" "PreviewType"="Plane"} Blend SrcAlpha One Cull Off Lighting Off ZWrite Off Fog { Color (0,0,0,0) } BindChannels { Bind "Color", color Bind "Vertex", vertex Bind "TexCoord", texcoord } SubShader { Pass { SetTexture [_MainTex] { combine texture * primary } } } } }
2.加個替換shader,替換Distortion標籤的shader要拿_DistortColor來進行渲染,其他的渲染為黑色
這裡是沒有寫根據距離算強度的
Shader "Hidden/ImageEffect Replace"
{
//替換標籤是Distortion的shader
SubShader
{
Tags{ "RenderType" = "Distortion" "Queue" = "Transparent" }
Blend SrcAlpha One
Pass
{
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
uniform sampler2D _MainTex;
uniform float4 _DistortColor;
half4 frag(v2f_img i) : COLOR
{
half4 c = tex2D(_MainTex,i.uv);
c.x = _DistortColor.r;
c.y = _DistortColor.g;
c.z = _DistortColor.b;
return c;
}
ENDCG
}
}
//替換標籤是Opaque的shader,這裡直接渲染為黑色
SubShader
{
Tags { "RenderType" = "Opaque" }
Pass
{
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
half4 frag(v2f_img i) : COLOR
{
return half4(0,0,0,1);
}
ENDCG
}
}
Fallback Off
}
這裡加了根據距離計算強度,把上面對應的shader改一下就可以,如下,就能實現根據距離算強度,自己替換一下就可以
//替換標籤是Distortion的shader
SubShader
{
Tags{ "RenderType" = "Distortion" "Queue" = "Transparent" }
Blend SrcAlpha One
Pass
{
CGPROGRAM
#pragma vertex vert_mask
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
struct v2f_Mask
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
//頂點到攝像機距離
float lengthInCamera : TEXCOORD1;
};
uniform sampler2D _MainTex;
uniform float4 _DistortColor;
v2f_Mask vert_mask(appdata_img v)
{
v2f_Mask o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
//計算頂點到攝像機向量的長度(距離)
o.lengthInCamera = length(_WorldSpaceCameraPos - v.vertex.xyz);
return o;
}
half4 frag(v2f_Mask i) : COLOR
{
//這裡是根據5米內強度正常,5米外強度慢慢減弱,減到0
half s = (1 - clamp((i.lengthInCamera - 5)*0.2, 0, 1));
half4 c = tex2D(_MainTex,i.uv);
c.x = _DistortColor.r * s;
c.y = _DistortColor.g * s;
c.z = _DistortColor.b * s;
return c;
}
ENDCG
}
}
3.關於渲染圖的渲染,對於的mask圖攝像機enable = false,把一張rendertexture給mask攝像機,然後直接用RenderWithShader渲染
maskRenderCamera.RenderWithShader(replaceShader, "RenderType");
下面是渲染圖
4.拿到mask圖直接和螢幕進行處理,除了mask圖,還要一張噪音圖來進行隨機,然後進行uv偏移
下面是對於的shader
Shader "Effect/Distortion/ScreenEffect/DistortionEffect"
{
Properties
{
_MainTex("Base (RGB)", 2D) = "white" {}
_NoiseTex("Noise", 2D) = "black" {}//預設給黑色,也就是不會偏移
_MaskTex("Mask", 2D) = "black" {}//預設給黑色,權重為0,我這裡定義綠色是扭曲
}
CGINCLUDE
#include "UnityCG.cginc"
uniform sampler2D _MainTex;
uniform sampler2D _NoiseTex;
uniform sampler2D _MaskTex;
fixed4 frag(v2f_img i) : SV_Target
{
//取樣Mask圖獲得權重資訊
fixed4 factor = tex2D(_MaskTex, i.uv);
//根據時間改變取樣噪聲圖獲得隨機的輸出
float4 noise = tex2D(_NoiseTex, i.uv - _Time.xy * factor.rb);
//以隨機的輸出*控制係數得到偏移值
float2 offset = noise.xy * 0.04 * factor.rb;
//畫素取樣時偏移offset,根據mask圖的綠色通道來偏移
float2 uv = offset * step(0.1, factor.g) * factor.g + i.uv;
return tex2D(_MainTex, uv);
}
ENDCG
SubShader
{
Pass
{
ZTest Always
Cull Off
ZWrite Off
Fog{ Mode off }
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
ENDCG
}
}
Fallback off
}
5.最後就是對應的後處理指令碼,包括mask渲染和uv扭曲處理
using System;
using UnityEngine;
using System.Collections;
public class DistortionEffect : MonoBehaviour
{
/// <summary>
/// 扭曲對應的材質球
/// </summary>
public Material _DistortMat;
/// <summary>
/// 特定渲染圖
/// </summary>
private RenderTexture m_R;
/// <summary>
/// mask攝像機
/// </summary>
public Camera maskRenderCamera;
/// <summary>
/// 替換shader
/// </summary>
public Shader replaceShader;
private void Awake()
{
#if UNITY_EDITOR
m_R = RenderTexture.GetTemporary(Screen.width / 2, Screen.height / 2, 0, RenderTextureFormat.ARGB32);
#else
m_R = RenderTexture.GetTemporary(Screen.width / 2, Screen.height / 2, 0, RenderTextureFormat.ARGB32);
#endif
maskRenderCamera.enabled = false;
maskRenderCamera.clearFlags = CameraClearFlags.SolidColor;
maskRenderCamera.backgroundColor = Color.black;
maskRenderCamera.targetTexture = m_R;
}
/// <summary>
/// 處理mask圖
/// </summary>
private void OnPreRender()
{
maskRenderCamera.RenderWithShader(replaceShader, "RenderType");
}
/// <summary>
/// 後處理
/// </summary>
/// <param name="source"></param>
/// <param name="destination"></param>
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
_DistortMat.SetTexture("_MaskTex", m_R);
Graphics.Blit(source, destination, _DistortMat);
}
}
新建一個扭曲材質球,把噪音圖拖上去
MainCamera掛上後處理指令碼
MainCamera節點下建立一個camera作為mask攝像機
然後把扭曲材質球,替換shader,mask攝像機拖到後處理指令碼,如下圖
最後把粒子特效的shader換成前面修改的shader
這裡的顏色是調偏移和強度
這樣子就可以看到效果
最後就是工程地址,工程有點大,下載了unity一個免費的粒子,作為做演示
還有根據距離算強度的我這邊沒有加到工程裡面,懶得上傳多一次工程,直接自己改一下程式碼就好
連結:https://pan.baidu.com/s/1N0C3SqbXkgeHLwFua_uSbQ
提取碼:us1z