最近项目要用到扭曲效果,查了一些资料,关于崩坏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