using NaughtyAttributes;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class PostEffectsManager : MonoBehaviour
{
private Camera cam;
public Camera Camera
{
get
{
if (cam == null)
{
cam = GetComponent();
}
return cam;
}
}
public Vector3 CamPos { get { return Camera.transform.position; } }
public Shader lerpShader;
private Material weightMaterial;
public Material WeightMaterial
{
get
{
if (weightMaterial == null && lerpShader != null)
weightMaterial = new Material(lerpShader);
return weightMaterial;
}
}
private Material blendMaterial;
public Material BlendMaterial
{
get
{
if (blendMaterial == null && lerpShader != null)
blendMaterial = new Material(lerpShader);
return blendMaterial;
}
}
private Dictionary> sortedVolumes = new Dictionary>();
private List sortPriority = new List();
private PostEffectsVolume lastVolume = null;
private PostEffectsVolume currentVolume = null;
private void LateUpdate()
{
//获取场景中的后处理体并进行排序
PostEffectsVolume[] volumes = FindObjectsOfType();
sortedVolumes.Clear();
sortPriority.Clear();
for (int i = 0; i < volumes.Length; i++)
{
if (sortedVolumes.ContainsKey(volumes[i].priority))
{
sortedVolumes[volumes[i].priority].Add(volumes[i]);
}
else
{
sortPriority.Add(volumes[i].priority);
sortedVolumes.Add(volumes[i].priority, new List() { volumes[i] });
}
}
sortPriority.Sort((a, b) => b.CompareTo(a));
//获取当前所在后处理体
PostEffectsVolume volume = currentVolume;
for (int i = 0; i < sortPriority.Count; i++)
{
List currentPriorityVolumes = sortedVolumes[sortPriority[i]];
for (int j = 0; j < currentPriorityVolumes.Count; j++)
{
if (currentPriorityVolumes[j].IsInVolume(CamPos) && currentPriorityVolumes[j].enabled)
{
currentVolume = currentPriorityVolumes[j];
break;//我们只处理一个
}
else
{
currentVolume = null;
}
}
if (currentVolume != null)
{
break;
}
}
if(volume != currentVolume)
{
lastVolume = volume;
}
if (currentVolume == null)
{
lastVolume = null;
}
}
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
//后处理
if (currentVolume != null)
{
//Set Camera
if (lastVolume != null)
Camera.depthTextureMode = (DepthTextureMode)Mathf.Max(
lastVolume.depthTextureMode, currentVolume.depthTextureMode);
//PostEffectRender
RenderTexture current = PostEffectRender(source, currentVolume);
if (currentVolume.IsInBlendField(CamPos))
{
//Blend Distance
BlendMaterial.SetTexture("_PostEffectTex", current);
BlendMaterial.SetFloat("_LerpValue", currentVolume.GetCurrentBlendDistance(CamPos));
if (lastVolume != null && lastVolume != currentVolume)
{
RenderTexture last = PostEffectRender(source, lastVolume);
Graphics.Blit(last, destination, BlendMaterial);
RenderTexture.ReleaseTemporary(last);
}
else
{
Graphics.Blit(source, destination, BlendMaterial);
}
}
else
{
Graphics.Blit(current, destination);
}
RenderTexture.ReleaseTemporary(current);
}
else
{
Camera.depthTextureMode = DepthTextureMode.None;
Graphics.Blit(source, destination);
}
}
private RenderTexture PostEffectRender(RenderTexture source, PostEffectsVolume volume)
{
//PostEffectRender
PostEffectSettings[] postEffects = volume.profile.postEffects;
RenderTexture rt = RenderTexture.GetTemporary(source.width, source.height);
Graphics.Blit(source, rt);
for (int i = 0; i < postEffects.Length; i++)
{
if (postEffects[i].Enable)
{
RenderTexture rt2 = RenderTexture.GetTemporary(rt.width, rt.height);
postEffects[i].Renderer(rt, rt2);
RenderTexture.ReleaseTemporary(rt);
rt = rt2;
}
}
//Weight
WeightMaterial.SetTexture("_PostEffectTex", rt);
WeightMaterial.SetFloat("_LerpValue", currentVolume.weight);
RenderTexture result = RenderTexture.GetTemporary(source.width, source.height);
Graphics.Blit(source, result, WeightMaterial);
RenderTexture.ReleaseTemporary(rt);
return result;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using NaughtyAttributes;
public enum PostEffectsVolumeMode
{
Global,
Box,
Sphere,
ConvexMesh,
}
public class PostEffectsVolume : MonoBehaviour
{
/// 效果范围
public PostEffectsVolumeMode mode = PostEffectsVolumeMode.Global;
/// 后处理范围碰撞体
private Collider volumeCollider;
/// 优先级:当相机位于多个后处理体中时优先选择最大的
public int priority = 0;
/// 权重
[Range(0, 1)]
public float weight = 1;
/// 过渡距离
[Min(0)]
[ShowIf("UseBlend")]
public float blendDistance = 1;
private bool UseBlend() { return mode != PostEffectsVolumeMode.Global; }
/// 纹理信息模式
[Dropdown("depthTextureModes")]
public int depthTextureMode = 0;
private List depthTextureModes
{
get
{
return new List() { 0, 1, 2, 4 };
}
}
/// 后处理列表
//[Expandable]
public PostEffectsProfile profile;
/// 是否在后处理体影响范围内
public bool IsInVolume(Vector3 worldPos)
{
if (volumeCollider == null)
volumeCollider = GetComponent();
if (mode == PostEffectsVolumeMode.Global)
{
return profile != null && profile.CanUse;
}
else if (volumeCollider != null)
{
return profile != null && profile.CanUse && volumeCollider.bounds.Contains(worldPos);
}
else
{
return false;
}
}
/// 是否在过渡区域
public bool IsInBlendField(Vector3 CamPos)
{
switch (mode)
{
case PostEffectsVolumeMode.Global:
return false;
case PostEffectsVolumeMode.Box:
BoxCollider box = GetComponent();
Bounds blendOut = box.bounds;
Bounds blendIn = new Bounds(box.bounds.center,
box.bounds.size - blendDistance * box.bounds.size.normalized);
return blendOut.Contains(CamPos) && !blendIn.Contains(CamPos);
case PostEffectsVolumeMode.Sphere:
SphereCollider sphere = GetComponent();
float dis = Vector3.Distance(sphere.bounds.center, CamPos);
return dis < sphere.radius && dis > sphere.radius - blendDistance;
case PostEffectsVolumeMode.ConvexMesh:
//TODO 射线检测 Collider.Raycast
return false;
}
return false;
}
/// 过渡值
public float GetCurrentBlendDistance(Vector3 CamPos)
{
if (IsInBlendField(CamPos))
{
if (volumeCollider == null)
volumeCollider = GetComponent();
float dis = Vector3.Distance(volumeCollider.bounds.center, CamPos);
Vector3 dir = (volumeCollider.bounds.center - CamPos).normalized;
Debug.DrawRay(volumeCollider.bounds.center, dir, Color.red);
float blendStep = 1;
switch (mode)
{
case PostEffectsVolumeMode.Box:
BoxCollider box = GetComponent();
Ray ray = new Ray(box.bounds.center, dir);
Bounds blendOut = box.bounds;
Bounds blendIn = new Bounds(box.bounds.center,
box.bounds.size - blendDistance * box.bounds.size.normalized);
float outDis, inDis;
blendOut.IntersectRay(ray, out outDis);
blendIn.IntersectRay(ray, out inDis);
float blendDis = Mathf.Abs(outDis) - Mathf.Abs(inDis);
blendStep = (blendDis - (dis - Mathf.Abs(inDis))) / blendDis;
break;
case PostEffectsVolumeMode.Sphere:
SphereCollider sphere = GetComponent();
blendStep = (blendDistance - (dis - (sphere.radius - blendDistance))) / blendDistance;
break;
case PostEffectsVolumeMode.ConvexMesh:
//TODO 射线检测 Collider.Raycast
blendStep = 1;
break;
}
return Mathf.Clamp01(blendStep);
}
else
{
return 1;
}
}
private void OnDrawGizmos()
{
if (mode == PostEffectsVolumeMode.Box)
{
BoxCollider box = GetComponent();
Gizmos.color = new Color(0, 1, 0, 0.2f);
Gizmos.DrawCube(box.bounds.center, box.bounds.size);
Gizmos.DrawWireCube(box.bounds.center, box.size);
Gizmos.color = new Color(0, 1, 0, 0.3f);
Gizmos.DrawCube(box.bounds.center, box.bounds.size - blendDistance * box.bounds.size.normalized);
Gizmos.DrawWireCube(box.bounds.center, box.bounds.size - blendDistance * box.bounds.size.normalized);
}
else if (mode == PostEffectsVolumeMode.Sphere)
{
SphereCollider sphere = GetComponent();
Gizmos.color = new Color(0, 1, 0, 0.2f);
Gizmos.DrawSphere(sphere.bounds.center, sphere.radius);
Gizmos.DrawWireSphere(sphere.center, sphere.radius);
Gizmos.color = new Color(0, 1, 0, 0.3f);
Gizmos.DrawSphere(sphere.bounds.center, sphere.radius - blendDistance);
Gizmos.DrawWireSphere(sphere.bounds.center, sphere.radius - blendDistance);
}
else if (mode == PostEffectsVolumeMode.ConvexMesh)
{
Mesh mesh = GetComponent().sharedMesh;
if (mesh != null)
{
Gizmos.color = new Color(0, 1, 0, 0.2f);
Gizmos.DrawMesh(mesh, transform.position, transform.rotation, transform.localScale);
Gizmos.color = new Color(0, 1, 0, 0.3f);
Vector3 size = mesh.bounds.size;
Gizmos.DrawMesh(mesh, transform.position, transform.rotation, transform.localScale - blendDistance * size.normalized);
}
}
}
}
using NaughtyAttributes;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "PostEffectsProfile", menuName = "PostEffects/PostEffectsProfile")]
public class PostEffectsProfile : ScriptableObject
{
public bool CanUse { get { return postEffects != null && postEffects.Length > 0; } }
[Expandable]
public PostEffectSettings[] postEffects;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PostEffectSettings : ScriptableObject
{
public bool Enable { get { return Material != null && enable; } set { enable = value; } }
public bool enable;
public Shader shader;
private Material material;
public Material Material
{
get
{
if (material == null && shader != null)
material = new Material(shader);
return material;
}
}
/// 后处理渲染
public virtual void Renderer(RenderTexture source, RenderTexture destination)
{
if (Material != null)
{
SetMaterialData(Material);
Graphics.Blit(source, destination, Material);
}
}
/// 配置材质
protected virtual void SetMaterialData(Material material)
{
}
}
Shader "LSQ/PostProcessing/Lerp"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
sampler2D _MainTex;
sampler2D _PostEffectTex;
fixed _LerpValue;
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
fixed4 postCol = tex2D(_PostEffectTex, i.uv);
return lerp(col, postCol, _LerpValue);
}
ENDCG
}
}
}
案例:
Shader "LSQ/PostEffect/RadialBlur"
{
Properties
{
_MainTex ("Base (RGB)", 2D) = "" {}
}
CGINCLUDE
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_TexelSize;
float2 _BlurRadius;
float2 _RadialCenter;
int _Iteration;
v2f vert(appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
half4 frag(v2f i) : SV_Target
{
half4 color = half4(0,0,0,0);
float2 blurVector = (i.uv.xy - _RadialCenter.xy) * _BlurRadius.xy;
for(int j = 0; j < _Iteration; j++)
{
half4 tmpColor = tex2D(_MainTex, i.uv.xy);
color += tmpColor;
i.uv.xy += blurVector;
}
return color / (float)_Iteration;
}
ENDCG
Subshader
{
Blend One Zero
Pass {
ZTest Always Cull Off ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
Fallback off
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[CreateAssetMenu(fileName = "RadialBlurSettings", menuName = "PostEffects/Blur/RadialBlur")]
public class RadialBlurSettings : PostEffectSettings
{
public Vector2 _BlurRadius;
public Vector2 _RadialCenter;
public int _Iteration = 8;
protected override void SetMaterialData(Material material)
{
material.SetVector("_BlurRadius", _BlurRadius);
material.SetVector("_RadialCenter", _RadialCenter);
material.SetInt("_Iteration", _Iteration);
}
}



