using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; namespace Thousandto.Launcher.ExternalLibs { /// /// 对标记的特效进行扭曲渲染 /// [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 _renders; private List _visibledRenders = new List(); 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.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 all, List 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 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; } } } }