// author : Saber // date : 2020/12/8 // desc : 将当前项目中的后处理集中处理 using System.Collections; using System.Collections.Generic; using UnityEngine; using System; using LuaInterface; using UnityStandardAssets.ImageEffects; public enum Resolution { Low = 0, High = 1, } public enum BlurType { Standard = 0, Sgx = 1, } // [ExecuteInEditMode, ImageEffectAllowedInSceneView] [RequireComponent (typeof(Camera))] public class PostEffect : PostEffectsBase { #region FXAA参数 public bool EnableFXAA = false; // FXAA总开关 const int fxaaPass = 1; [Range(0.0312f, 0.0833f)] public float contrastThreshold = 0.0312f; [Range(0.063f, 0.333f)] public float relativeThreshold = 0.063f; [Range(0f, 1f)] public float subpixelBlending = 1f; public Shader FXAAShader; public bool lowQuality; Material fxaaMaterial; #endregion #region bloom效果参数 public bool EnableBloom = false; [Range(0.0f, 1.5f)] public float threshold = 0.47f; [Range(0.0f, 3f)] public float intensity = 0.75f; [Range(0.25f, 5.5f)] public float blurSize = 0.46f; int bloomDowmSample = 1; float bloomBlurAmount = 0.5f; int passOffset = 0; Resolution resolution = Resolution.Low; [Range(1, 4)] public int blurIterations = 1; public BlurType blurType= BlurType.Standard; public Shader fastBloomShader = null; public Shader dualBloomShader = null; private Material fastBloomMaterial = null; private Material dualBloomMaterial = null; public bool use_dual_bloom = false; const int DUAL_BLOOM_RENDER_PASS = 0; const int DUAL_BLOOM_DOWNSAMPLE_PASS = 1; const int DUAL_BLOOM_UPSAMPLE_PASS = 2; const int DUAL_BLOOM_LUMINACE_PASS = 3; #endregion #region 高斯模糊/双重模糊后处理效果参数 public bool EnableBlur = false; public bool use_dual_blur = false; // 使用双面模糊 #region 高斯模糊相关参数 const int BLUR_PASS_HOR = 0; // 水平模糊pass const int BLUR_PASS_VER = 1; // 垂直模糊pass public Shader blurShader; // 高斯模糊shader Material blurMaterial; // 高速模糊材质 [HideInInspector] public bool render_blur_effect = false; // 高斯模糊关键参数,设置为true时会开始渲染高斯模糊纹理 public bool get_screen_shot = false; // 与上面的功能区分,只截图,并不会改变原先的渲染 // 高斯模糊缓存纹理(不要在渲染时动态创建,会消耗很多内存,这里先生成引用) public float blurSpread = 0.4f; // 模糊散值 public float blur_amount = 1f; // 模糊插值,0:初始效果,1:完全模糊效果 private int recurveNum = 1; // 当前迭代的渲染次数 public int recurveMaxNum = 1; // 最大迭代渲染次数 public int downSample = 4; // 降低采样率比率(降低采样率可以提高性能,但肯定会让效果变差) LuaFunction screenshot_callback; // 模糊截图回调 #endregion #region 双面模糊相关参数 const int DOWN_SAMPLE_PASS = 0; // 降采样pass const int UP_SAMPLE_PASS = 1; // 升采样pass public Shader dualBlurShader; // 双面模糊shader Material dual_blur_mat; // 双面模糊材质球 RenderTextureFormat format; #endregion #endregion private RenderTexture final_renderTexture; // 最终渲染出来的画面RT缓存 private RenderTexture temp_rt1; private RenderTexture temp_rt2; private bool final_rt_rendered = false; // 最终画面是否被渲染到了 // 检查资源 public bool GetSupported() { return CheckSupport(false); } public override bool CheckResources() { CheckSupport (false); if (!isSupported){ ReportAutoDisable (); return isSupported; } // 创建对应shader的材质球 fxaaMaterial = CheckShaderAndCreateMaterial (FXAAShader, fxaaMaterial); // FXAA效果 fastBloomMaterial = CheckShaderAndCreateMaterial (fastBloomShader, fastBloomMaterial); // Bloom效果(高斯模糊) dualBloomMaterial = CheckShaderAndCreateMaterial (dualBloomShader, dualBloomMaterial); // Bloom效果(双重模糊) blurMaterial = CheckShaderAndCreateMaterial (blurShader, blurMaterial); // 高斯模糊效果 dual_blur_mat = CheckShaderAndCreateMaterial (dualBlurShader, dual_blur_mat); // 双面模糊效果 return isSupported; } // 非迭代的material参数修改,统一做成接口调用以一次性修改,而不要在渲染逻辑内重复执行 // FXAA属性值应用 public void ApplyFXAAMaterialProperties() { if (fxaaMaterial != null) { fxaaMaterial.SetFloat("_ContrastThreshold", contrastThreshold); fxaaMaterial.SetFloat("_RelativeThreshold", relativeThreshold); fxaaMaterial.SetFloat("_SubpixelBlending", subpixelBlending); if (lowQuality) { fxaaMaterial.EnableKeyword("LOW_QUALITY"); } else { fxaaMaterial.DisableKeyword("LOW_QUALITY"); } } } // Bloom泛光属性值应用 public void ApplyBloomMaterialProperties() { bloomDowmSample = resolution == Resolution.Low ? 4 : 2; bloomBlurAmount = resolution == Resolution.Low ? 0.5f : 1.0f; passOffset = blurType == BlurType.Standard ? 0 : 2; // 双面模糊的参数直接设置 if (dualBloomMaterial) { dualBloomMaterial.SetFloat("_Threshold", threshold); dualBloomMaterial.SetFloat("_Intensity", intensity); } } // 高斯/双面模糊属性值应用 public void ApplyBlurMaterialProperties() { } // 根据当前的项目渲染顺序,预设的渲染顺序分别是 Bloom泛光效果 -> FXAA抗锯齿效果 -> 模糊效果(采集模糊背景用,很少用于即时渲染) // 将所有的效果整合成OnRenderImage中处理,不拆分,尽可能减少Graphics.Blit次数 void OnRenderImage(RenderTexture src, RenderTexture dest) { if (isSupported) // 支持后处理 { final_rt_rendered = false; final_renderTexture = RenderTexture.GetTemporary(src.width, src.height, 0, src.format); format = src.format; // 1.处理Bloom泛光效果 if (EnableBloom && fastBloomMaterial != null && dualBloomMaterial != null) { // 使用双面模糊算法来处理模糊部分 if(use_dual_bloom) { int width = src.width; int height = src.height; // 根据亮度阈值获取亮度图 temp_rt1 = RenderTexture.GetTemporary(width / bloomDowmSample, height / bloomDowmSample, 0, format); temp_rt2 = RenderTexture.GetTemporary(width / bloomDowmSample, height / bloomDowmSample, 0, format); Graphics.Blit(src, temp_rt1, dualBloomMaterial, DUAL_BLOOM_LUMINACE_PASS); for(int i = 0; i < blurIterations; i++) { dualBloomMaterial.SetFloat("_Offset", (1.0f + i * blurSize) * bloomBlurAmount); // 设置模糊扩散uv偏移 // 降采样渲染 Graphics.Blit(temp_rt1, temp_rt2, dualBloomMaterial, DUAL_BLOOM_DOWNSAMPLE_PASS); // 升采样渲染 Graphics.Blit(temp_rt2, temp_rt1, dualBloomMaterial, DUAL_BLOOM_UPSAMPLE_PASS); } dualBloomMaterial.SetTexture("_Bloom", temp_rt1); } // 使用高斯模糊算法处理模糊部分 else { fastBloomMaterial.SetVector("_Parameter", new Vector4 (blurSize * bloomBlurAmount, 0.0f, threshold, intensity)); // src.filterMode = FilterMode.Bilinear; int rtW = src.width / bloomDowmSample; int rtH = src.height / bloomDowmSample; // downsample temp_rt1 = RenderTexture.GetTemporary(rtW, rtH, 0, format); temp_rt2 = RenderTexture.GetTemporary(rtW, rtH, 0, format); // temp_rt1.filterMode = FilterMode.Bilinear; // temp_rt2.filterMode = FilterMode.Bilinear; Graphics.Blit(src, temp_rt1, fastBloomMaterial, 1); for(int i = 0; i < blurIterations; i++) { fastBloomMaterial.SetVector("_Parameter", new Vector4 (blurSize * bloomBlurAmount + (i*1.0f), 0.0f, threshold, intensity)); // vertical blur Graphics.Blit(temp_rt1, temp_rt2, fastBloomMaterial, 2 + passOffset); Graphics.Blit(temp_rt2, temp_rt1, fastBloomMaterial, 3 + passOffset); } fastBloomMaterial.SetTexture("_Bloom", temp_rt1); } ReleaseTemporary(temp_rt2); // 这边就判断是否使用FXAA if (EnableFXAA && fxaaMaterial != null) { temp_rt2 = RenderTexture.GetTemporary(src.width, src.height, 0, format); if(use_dual_bloom) // 使用双面模糊算法来处理合并部分 { Graphics.Blit(src, temp_rt2, dualBloomMaterial, DUAL_BLOOM_RENDER_PASS); } else { Graphics.Blit(src, temp_rt2, fastBloomMaterial, 0); } Graphics.Blit(temp_rt2, final_renderTexture, fxaaMaterial, fxaaPass); ReleaseTemporary(temp_rt2); } else { if(use_dual_bloom) // 使用双面模糊算法来处理合并部分 { Graphics.Blit(src, final_renderTexture, dualBloomMaterial, DUAL_BLOOM_RENDER_PASS); } else { Graphics.Blit(src, final_renderTexture, fastBloomMaterial, 0); } } final_rt_rendered = true; //source.filterMode = FilterMode.Point; ReleaseTemporary(temp_rt1); } else { // 当不适用Bloom的情况,处理FXAA抗锯齿效果 if (EnableFXAA && fxaaMaterial != null) { Graphics.Blit(src, final_renderTexture, fxaaMaterial, fxaaPass); final_rt_rendered = true; } } // 3.处理模糊需求 if (EnableBlur && blurMaterial != null && dual_blur_mat != null && (render_blur_effect || get_screen_shot)) { // 使用双面模糊算法 if(use_dual_blur){ int width = src.width; int height = src.height; recurveNum = 1; // 初始化迭代 // RT初始化 temp_rt1 = RenderTexture.GetTemporary(width / downSample, height / downSample, 0, format); if(get_screen_shot && !render_blur_effect) // 当只是渲染截图,就使用临时rt2 temp_rt2 = RenderTexture.GetTemporary(width / downSample, height / downSample, 0, format); while(recurveNum <= recurveMaxNum) { dual_blur_mat.SetFloat("_Offset", (1.0f + recurveNum * blurSpread) * blur_amount); // 设置模糊扩散uv偏移 if (get_screen_shot && !render_blur_effect) // 只渲染截屏 { // 降采样渲染 if(recurveNum == 1 && !final_rt_rendered) // 当首次降采样且最终RT没有处理过,就使用源RT Graphics.Blit(src, temp_rt1, dual_blur_mat, DOWN_SAMPLE_PASS); else Graphics.Blit(temp_rt2, temp_rt1, dual_blur_mat, DOWN_SAMPLE_PASS); // 升采样渲染 // 使用temp_rt2,不要影响最终画面 Graphics.Blit(temp_rt1, temp_rt2, dual_blur_mat, UP_SAMPLE_PASS); } else // 需要实时模糊 { // 降采样渲染 if(recurveNum == 1 && !final_rt_rendered) // 当首次降采样且最终RT没有处理过,就使用源RT Graphics.Blit(src, temp_rt1, dual_blur_mat, DOWN_SAMPLE_PASS); else Graphics.Blit(final_renderTexture, temp_rt1, dual_blur_mat, DOWN_SAMPLE_PASS); // 升采样渲染,输出模糊的final_renderTexture Graphics.Blit(temp_rt1, final_renderTexture, dual_blur_mat, UP_SAMPLE_PASS); } recurveNum ++; } RenderTexture.ReleaseTemporary(temp_rt1); // 释放临时RT // 仅当使用持续模糊渲染才会置为true,否则由之前的效果来决定 final_rt_rendered = final_rt_rendered || render_blur_effect; } else { recurveNum = 1; //缩小要模糊的图片,减少处理压力 int width = src.width / downSample; int height = src.height / downSample; // 由于高斯模糊需要将最终纹理降采样,所以这边做一个标志量 // 如果经过了上面的处理后还需要高斯模糊处理,则需要在迭代渲染过程中进行一次降采样缓存 bool need_redownSample = true; if(get_screen_shot && !render_blur_effect) // 当只是渲染截图,就使用临时rt2 { temp_rt2 = RenderTexture.GetTemporary(width, height, 0, format); Graphics.Blit(src, temp_rt2); need_redownSample = false; } else { if(!final_rt_rendered) { // 这边如果原RT未采样也要释放缓存,不然直接获取缓存会存在泄露问题 ReleaseTemporary(final_renderTexture); final_renderTexture = RenderTexture.GetTemporary(width, height, 0); // final_renderTexture.filterMode = FilterMode.Bilinear; Graphics.Blit(src, final_renderTexture); need_redownSample = false; } } temp_rt1 = RenderTexture.GetTemporary(width, height, 0); while(recurveNum <= recurveMaxNum){ recurveNum ++; blurMaterial.SetFloat("_BlurSize", (1.0f + recurveNum * blurSpread) * blur_amount); if(get_screen_shot && !render_blur_effect) { Graphics.Blit(temp_rt2, temp_rt1, blurMaterial, BLUR_PASS_HOR); if(need_redownSample) { temp_rt2 = RenderTexture.GetTemporary(width, height, 0, format); need_redownSample = false; } Graphics.Blit(temp_rt1, temp_rt2, blurMaterial, BLUR_PASS_VER); } else { Graphics.Blit(final_renderTexture, temp_rt1, blurMaterial, BLUR_PASS_HOR); if(need_redownSample) { ReleaseTemporary(final_renderTexture); final_renderTexture = RenderTexture.GetTemporary(width, height, 0); need_redownSample = false; } Graphics.Blit(temp_rt1, final_renderTexture, blurMaterial, BLUR_PASS_VER); } } RenderTexture.ReleaseTemporary(temp_rt1); // 仅当使用持续模糊渲染才会置为true,否则由之前的效果来决定 final_rt_rendered = final_rt_rendered || render_blur_effect; } } if(get_screen_shot && !render_blur_effect){ GetBlurScreenShot(); get_screen_shot = false; // 截图生成之后就停止运作 } if(final_rt_rendered) { Graphics.Blit(final_renderTexture, dest); } else { Graphics.Blit(src, dest); } RenderTexture.ReleaseTemporary(final_renderTexture); } else { Graphics.Blit(src, dest); // 不支持后处理,则直接输出最终画面 } } // 设置开始处理模糊效果的截图 public void RenderBlurScreenShot(float blur_amount, int downSample, LuaFunction func) { get_screen_shot = true; this.blur_amount = blur_amount; this.downSample = downSample; screenshot_callback = func; } void GetBlurScreenShot() { // RenderTexture temp_screen_shot = RenderTexture.GetTemporary(final_renderTexture.width, final_renderTexture.height, 0); // Graphics.Blit(final_renderTexture, temp_screen_shot); RenderTexture temp_screen_shot = RenderTexture.GetTemporary(temp_rt2.width, temp_rt2.height, 0); Graphics.Blit(temp_rt2, temp_screen_shot); if (screenshot_callback != null) { List list = new List(); list.Add(temp_screen_shot); object[] args = new object[] { list.ToArray() }; screenshot_callback.Call(args); screenshot_callback.Dispose(); screenshot_callback = null; } // 释放掉临时RT2 ReleaseTemporary(temp_rt2); } // 销毁渲染纹理缓存 public void ReleaseRenderTexCache() { if (!render_blur_effect) { ReleaseTemporary(final_renderTexture); } } void ReleaseTemporary(RenderTexture rt) { if (rt != null) { RenderTexture.ReleaseTemporary(rt); } } void OnDisable() { // // 销毁已有的材质球 // if (fastBloomMaterial) // DestroyImmediate (fastBloomMaterial); // if (dualBloomMaterial) // DestroyImmediate (dualBloomMaterial); // if (fxaaMaterial) // DestroyImmediate (fxaaMaterial); // if (blurMaterial) // DestroyImmediate (blurMaterial); // if (dual_blur_mat) // DestroyImmediate (dual_blur_mat); // ReleaseTemporary(final_renderTexture); // ReleaseTemporary(temp_rt1); // ReleaseTemporary(temp_rt2); } }