/// author:Saber /// desc:角色相机泛光效果脚本 /// 2020年2月21日优化:参考了catlikecoding的模糊思路优化逻辑提升效果,模糊rt使用局部摄像机获取到的画面进行处理 using System.Collections; using System.Collections.Generic; using UnityEngine; [ExecuteInEditMode] [RequireComponent(typeof(Camera))] public class RoleCameraBloom : MonoBehaviour { public RenderTexture cullRenderTexture; // 用来接收泛光相机的裁剪RT private ChangeShaderRender scr_script; // 泛光摄像机上的渲染脚本 #region 泛光特效相关参数 //模糊散值 public Material bloomMaterial; // 泛光特效相关材质 public bool render_bloom_effect = false; // 泛光特效关键参数,设置为true时会开始渲染泛光特效 #endregion private bool isSupported; // 设备支持变量 // 模糊摄像机获取到的画面依然需要做阈值处理,不过初始默认值为0 [Range(0, 10)] public float threshold = 1; // 避免阈值附近的画面忽然点亮,这里设置一个软阈值处理阈值附近的颜色值的泛光效果 [Range(0, 1)] public float softThreshold = 0.5f; [Range(0, 10)] public float intensity = 1; #region 渲染pass常量 const int BoxDownPrefilterPass = 0; // 对泛光部分降采样的pass const int BoxDownPass = 1; // 降序采样pass const int BoxUpPass = 2; // 升序采样pass,最大限度利用到所有的像素颜色贡献 const int ApplyBloomPass = 3; // 泛光颜色叠加pass const int DebugBloomPass = 4; // 测试pass,不叠加原颜色,只渲染泛光处理的效果 #endregion [Range(1, 8)] public int iterations = 4; // 泛光迭代次数 RenderTexture[] textures = new RenderTexture[8]; // 临时纹理链表 RenderTexture currentDestination; RenderTexture currentSource; Vector4 filter; // 膝盖曲线过滤值 public bool debug = false; // Start is called before the first frame update void Start() { // 检查平台支持,不支持则直接禁用 isSupported = CheckSupport(); this.enabled = isSupported; if(scr_script != null){ scr_script.enabled = isSupported; } // 测试逻辑 // TestLogic(); } #region 测试逻辑 private void TestLogic() { RenderTexture temp_render = new RenderTexture(1280, 600, 0); GameObject child = this.transform.GetChild(0).gameObject; Camera temp_camera = child.GetComponent(); child.AddComponent(); ChangeShaderRender temp_scr = child.GetComponent(); Shader shader = Shader.Find("SaberShader/BloomClipShader"); temp_scr.target_shader = shader; SetBloomCameraData(temp_camera, temp_render, temp_scr); } #endregion private bool CheckSupport() { if (SystemInfo.supportsImageEffects == false) { Debug.LogWarning("当前平台不支持!"); return false; } return true; } // Lua接口,将泛光摄像机和泛光摄像机输出的rt交给本类来控制 public void SetBloomCameraData(Camera camera, RenderTexture rt, ChangeShaderRender change_shader_render){ cullRenderTexture = rt; camera.targetTexture = cullRenderTexture; render_bloom_effect = true; scr_script = change_shader_render; // 效果运行与否还要看设备支不支持特效 scr_script.enabled = isSupported; } // 正式渲染 // 这个方法要注意在退出游戏的时候把渲染逻辑门关闭,一般情况下把render_bloom_effect置false void OnRenderImage(RenderTexture src, RenderTexture dest) { if (bloomMaterial != null && render_bloom_effect && cullRenderTexture != null) { // 膝盖曲线的值可以在这里预先算好 float knee = threshold * softThreshold; filter.x = threshold; filter.y = filter.x - knee; filter.z = 2f * knee; filter.w = 0.25f / (knee + 0.00001f); bloomMaterial.SetVector("_Filter", filter); bloomMaterial.SetFloat("_Intensity", Mathf.GammaToLinearSpace(intensity)); // 降低一次采样,将剧目摄像机的内容传递到临时的最终画面RT中 int width = src.width / 4; int height = src.height / 4; currentDestination = textures[0] = RenderTexture.GetTemporary(width, height, 0, src.format); Graphics.Blit(cullRenderTexture, currentDestination, bloomMaterial, BoxDownPrefilterPass); currentSource = currentDestination; // 循环降采样,并将结果保存 int i = 1; for (; i < iterations; i++) { width /= 2; height /= 2; if (height < 2) { break; } currentDestination = textures[i] = RenderTexture.GetTemporary(width, height, 0, src.format); Graphics.Blit(currentSource, currentDestination, bloomMaterial, BoxDownPass); currentSource = currentDestination; } // 再对降采样后的画面继续宁升采样,将每个像素的颜色平均出去 for (i -= 2; i >= 0; i--) { currentDestination = textures[i]; textures[i] = null; Graphics.Blit(currentSource, currentDestination, bloomMaterial, BoxUpPass); RenderTexture.ReleaseTemporary(currentSource); currentSource = currentDestination; } if (debug) { Graphics.Blit(currentSource, dest, bloomMaterial, DebugBloomPass); } else { bloomMaterial.SetTexture("_SourceTex", src); Graphics.Blit(currentSource, dest, bloomMaterial, ApplyBloomPass); } RenderTexture.ReleaseTemporary(currentSource); } else { Graphics.Blit(src, dest); } } public void ReleaseTempRes() { render_bloom_effect = false; if(scr_script != null) { scr_script.enabled = false; } } void OnDestroy(){ ReleaseTempRes(); } }