Files
Main/Assets/Launcher/ExternalLibs/CmdBuffer/DistortVfx/DistortVfxScript.cs
2025-01-25 04:38:09 +08:00

334 lines
10 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
namespace Thousandto.Launcher.ExternalLibs
{
/// <summary>
/// 对标记的特效进行扭曲渲染
/// </summary>
[RequireComponent(typeof(Camera))]
public class DistortVfxScript : MonoBehaviour
{
//触发事件
private const CameraEvent CN_TRIGGER_EVENT = CameraEvent.AfterForwardAlpha;
private Camera _camera;
private CommandBuffer _cmd;
private Material _mat;
private int _distortTexID;
private int _strengthID;
//扭曲的强度
[Range(0.0f, 0.2f)]
public float DistortStrength = 0.01f;
private List<Renderer> _renders;
private List<RenderVisibleInfo> _visibledRenders = new List<RenderVisibleInfo>();
private int _visibleCount = 0;
private CameraInfo _ci = new CameraInfo();
private void Start()
{
InitPropertyID();
InitCamera();
InitMat();
InitCmd();
}
private void Update()
{
if (DistortVfxMarkScript.GetAllRenders(out _renders))
{
ConvertRenderVisibleInfo(_renders, _visibledRenders);
RefreshCmd();
}
else
{
if (TestVisibled(_visibledRenders))
{
RefreshCmd();
}
}
}
private void OnEnable()
{
if (_cmd != null && _camera != null)
{
_camera.AddCommandBuffer(CN_TRIGGER_EVENT, _cmd);
}
RefreshMat();
}
private void OnDisable()
{
if (_cmd != null && _camera != null)
{
_camera.RemoveCommandBuffer(CN_TRIGGER_EVENT, _cmd);
}
}
private void OnDestroy()
{
if (_cmd != null)
{
if (_camera != null)
{
_camera.RemoveCommandBuffer(CN_TRIGGER_EVENT, _cmd);
}
_cmd.Release();
_cmd = null;
}
if (_mat != null)
{
GameObject.Destroy(_mat);
_mat = null;
}
}
private void OnValidate()
{
RefreshMat();
}
private void InitPropertyID()
{
_distortTexID = Shader.PropertyToID("_DistortTex");
_strengthID = Shader.PropertyToID("_Strength");
}
private void InitCamera()
{
_camera = GetComponent<Camera>();
_camera.forceIntoRenderTexture = true;
_ci.SetCamera(_camera);
}
private void InitCmd()
{
if (_cmd == null)
{
_cmd = new CommandBuffer();
_cmd.name = "DistortVfxCmd";
if (_camera != null)
{
_camera.AddCommandBuffer(CN_TRIGGER_EVENT, _cmd);
}
}
}
private void InitMat()
{
var sh = RuntimeUtilities.FindShader("Hidden/Ares/PostEffect/DistortVfx");
if (sh != null)
{
_mat = new Material(sh);
_mat.hideFlags = HideFlags.DontSave;
RefreshMat();
}
else
{
Debug.LogError("没有找到Shader:Hidden/Ares/PostEffect/DistortVfx");
}
}
private void RefreshCmd()
{
if (_cmd != null)
{
_cmd.Clear();
if (_visibleCount > 0)
{
_cmd.GetTemporaryRT(_distortTexID, -4, -4, 0, FilterMode.Bilinear);
_cmd.GetTemporaryRT(CmdBufferUtils.FullScreenTexID, -1, -1, 0, FilterMode.Bilinear);
_cmd.SetRenderTarget(_distortTexID);
_cmd.ClearRenderTarget(false, true, Color.black);
//把需要处理的模型进行处理
for (int i = 0; i < _visibledRenders.Count; i++)
{
if (_visibledRenders[i].Render != null && _visibledRenders[i].IsVisible)
{
_cmd.DrawRenderer(_visibledRenders[i].Render, _visibledRenders[i].Render.sharedMaterial);
}
}
//1.把FrameBuffer中的数据进行扭曲处理
_cmd.SetGlobalTexture("_DistortTex", _distortTexID);
_cmd.SetGlobalTexture(CmdBufferUtils.MainTexID, BuiltinRenderTextureType.CameraTarget);
_cmd.SetRenderTarget(CmdBufferUtils.FullScreenTexID);
_cmd.DrawMesh(CmdBufferUtils.QuadMesh, Matrix4x4.identity, _mat);
//2.把扭曲的页面复制到FrameBuffer中.
_cmd.SetGlobalTexture(CmdBufferUtils.MainTexID, CmdBufferUtils.FullScreenTexID);
_cmd.SetRenderTarget(BuiltinRenderTextureType.CameraTarget);
_cmd.DrawMesh(CmdBufferUtils.QuadMesh, Matrix4x4.identity, CmdBufferUtils.BlitMat, 0, 0);
_cmd.ReleaseTemporaryRT(CmdBufferUtils.FullScreenTexID);
_cmd.ReleaseTemporaryRT(_distortTexID);
}
}
}
private void RefreshMat()
{
if (_mat != null)
{
_mat.SetFloat(_strengthID, DistortStrength);
}
}
private void OnPostEffectActive(int val)
{
if (_camera != null && _cmd != null)
{
//根据激活状态来处理
if (val > 0)
{
_camera.RemoveCommandBuffer(CN_TRIGGER_EVENT, _cmd);
_camera.AddCommandBuffer(CN_TRIGGER_EVENT, _cmd);
}
else
{
_camera.RemoveCommandBuffer(CN_TRIGGER_EVENT, _cmd);
}
}
}
private void ConvertRenderVisibleInfo(List<Renderer> all, List<RenderVisibleInfo> vs)
{
vs.Clear();
for (int i = 0; i < all.Count; i++)
{
if (all[i] != null)
{
vs.Add(new RenderVisibleInfo()
{
Render = all[i],
IsVisible = false
});
}
}
TestVisibled(vs);
}
//简单测试,只判断位置是否在摄像机的视图内
private bool TestVisibled(List<RenderVisibleInfo> vs)
{
if (vs.Count <= 0) return false;
bool result = false;
var planes = _ci.GetPlanes();
_visibleCount = 0;
for (int i = 0; i < vs.Count; i++)
{
var rv = vs[i];
//检测是否在范围内
var isVisibled = CheckIsVisibled(planes, rv.Render.bounds);
if (rv.IsVisible != isVisibled)
{
rv.IsVisible = isVisibled;
result = true;
Debug.Log("是否可视改变:"+isVisibled+"::"+ rv.Render.name);
}
//统计显示的Renderer
if (rv.IsVisible)
{
_visibleCount++;
}
}
return result;
}
//判断是否显示: 判断位置点 ,当数量少的时候会有优化
private bool CheckIsVisibled(Camera camera,float near,float far,Bounds bounds)
{
var vp = camera.WorldToViewportPoint(bounds.center);
return vp.x >= 0 && vp.x <= 1 && vp.y >= 0 && vp.y <= 1 && vp.z <= far && vp.z >= near;
}
//判断是否显示 判断AABB 更准确
private bool CheckIsVisibled(Plane[] planes, Bounds bounds)
{
return GeometryUtility.TestPlanesAABB(planes, bounds);
}
//渲染器是否显示的信息
private class RenderVisibleInfo
{
public Renderer Render;
public bool IsVisible;
}
//摄像机的信息
private class CameraInfo
{
private Camera _camera;
private Transform _cameraTrans;
private Plane[] _planes;
private float _near;
private float _far;
private Vector3 _pos;
private Vector3 _angle;
private float _fov;
public void SetCamera(Camera c)
{
_camera = c;
_cameraTrans = c.transform;
_near = c.nearClipPlane;
_far = c.farClipPlane;
_fov = c.fieldOfView;
_pos = _cameraTrans.position;
_angle = _cameraTrans.eulerAngles;
_planes = GeometryUtility.CalculateFrustumPlanes(_camera);
}
public Plane[] GetPlanes()
{
if (_camera != null)
{
if (!(FloatIsEquip(ref _near,_camera.nearClipPlane)
&& FloatIsEquip(ref _far, _camera.farClipPlane)
&& FloatIsEquip(ref _fov, _camera.fieldOfView)
&& Vector3IsEquip(ref _pos, _cameraTrans.position)
&& Vector3IsEquip(ref _angle, _cameraTrans.eulerAngles))
)
{
_planes = GeometryUtility.CalculateFrustumPlanes(_camera);
}
return _planes;
}
return null;
}
private bool Vector3IsEquip(ref Vector3 v1, Vector3 v2)
{
var v = v1 - v2;
if (Mathf.Abs(v.x) > 0.001f || Mathf.Abs(v.y) > 0.001f || Mathf.Abs(v.z) > 0.001f)
{
v1 = v2;
return false;
}
return true;
}
private bool FloatIsEquip(ref float f1, float f2)
{
if (Mathf.Abs(f1 - f2) > 0.001f)
{
f1 = f2;
return false;
}
return true;
}
}
}
}