////////////////////////////////////////////////////// // MK Glow URP Renderer Feature // // // // Created by Michael Kremmel // // www.michaelkremmel.de // // Copyright © 2021 All rights reserved. // ////////////////////////////////////////////////////// using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; using System; namespace MK.Glow.URP { public class MKGlowRendererFeature : ScriptableRendererFeature, MK.Glow.ISettings { ///////////////////////////////////////////////////////////////////////////////////////////// // Renderer Feature Only Properties ///////////////////////////////////////////////////////////////////////////////////////////// #if UNITY_EDITOR public bool showEditorMainBehavior = true; public bool showEditorBloomBehavior; public bool showEditorLensSurfaceBehavior; public bool showEditorLensFlareBehavior; public bool showEditorGlareBehavior; #endif public enum Workmode { [UnityEngine.InspectorName("Post-Process Volumes")] PostProcessVolumes = 0, [UnityEngine.InspectorName("Global")] Global = 1 }; public Workmode workmode = Workmode.PostProcessVolumes; //Main public bool allowGeometryShaders = true; public bool allowComputeShaders = true; public RenderPriority renderPriority = RenderPriority.Balanced; public DebugView debugView = MK.Glow.DebugView.None; public Quality quality = MK.Glow.Quality.High; public AntiFlickerMode antiFlickerMode = AntiFlickerMode.Balanced; public Workflow workflow = MK.Glow.Workflow.Threshold; public LayerMask selectiveRenderLayerMask = -1; [Range(-1f, 1f)] public float anamorphicRatio = 0f; [Range(0f, 1f)] public float lumaScale = 0.5f; [Range(0f, 1f)] public float blooming = 0f; //Bloom [MK.Glow.MinMaxRange(0, 10)] public MinMaxRange bloomThreshold = new MinMaxRange(1.25f, 10f); [Range(1f, 10f)] public float bloomScattering = 7f; public float bloomIntensity = 1f; //LensSurface public bool allowLensSurface = false; public Texture2D lensSurfaceDirtTexture; public float lensSurfaceDirtIntensity = 2.5f; public Texture2D lensSurfaceDiffractionTexture; public float lensSurfaceDiffractionIntensity = 2.0f; //LensFlare public bool allowLensFlare = false; public LensFlareStyle lensFlareStyle = LensFlareStyle.Average; [Range(0f, 25f)] public float lensFlareGhostFade = 10.0f; public float lensFlareGhostIntensity = 1.0f; [MK.Glow.MinMaxRange(0, 10)] public MinMaxRange lensFlareThreshold = new MinMaxRange(1.3f, 10f); [Range(0f, 8f)] public float lensFlareScattering = 5f; public Texture2D lensFlareColorRamp; [Range(-100f, 100f)] public float lensFlareChromaticAberration = 53f; [Range(1, 4)] public int lensFlareGhostCount = 3; [Range(-1f, 1f)] public float lensFlareGhostDispersal = 0.6f; [Range(0f, 25f)] public float lensFlareHaloFade = 2f; public float lensFlareHaloIntensity = 1.0f; [Range(0f, 1f)] public float lensFlareHaloSize = 0.4f; //Glare public bool allowGlare = false; [Range(0.0f, 1.0f)] public float glareBlend = 0.33f; public float glareIntensity = 1f; [Range(0.0f, 360.0f)] public float glareAngle = 0f; [MK.Glow.MinMaxRange(0, 10)] public MinMaxRange glareThreshold = new MinMaxRange(1.25f, 10f); [Range(1, 4)] public int glareStreaks = 4; public GlareStyle glareStyle = GlareStyle.DistortedCross; [Range(0.0f, 4.0f)] public float glareScattering = 2f; //Sample0 [Range(0f, 10f)] public float glareSample0Scattering = 5f; [Range(0f, 360f)] public float glareSample0Angle = 0f; public float glareSample0Intensity = 1f; [Range(-5f, 5f)] public float glareSample0Offset = 0f; //Sample1 [Range(0f, 10f)] public float glareSample1Scattering = 5f; [Range(0f, 360f)] public float glareSample1Angle = 45f; public float glareSample1Intensity = 1f; [Range(-5f, 5f)] public float glareSample1Offset = 0f; //Sample0 [Range(0f, 10f)] public float glareSample2Scattering = 5f; [Range(0f, 360f)] public float glareSample2Angle = 90f; public float glareSample2Intensity = 1f; [Range(-5f, 5f)] public float glareSample2Offset = 0f; //Sample0 [Range(0f, 10f)] public float glareSample3Scattering = 5f; [Range(0f, 360f)] public float glareSample3Angle = 135f; public float glareSample3Intensity = 1f; [Range(-5f, 5f)] public float glareSample3Offset = 0f; ///////////////////////////////////////////////////////////////////////////////////////////// // Settings ///////////////////////////////////////////////////////////////////////////////////////////// public bool GetAllowGeometryShaders() { return false; } public bool GetAllowComputeShaders() { return false; } public RenderPriority GetRenderPriority() { return renderPriority; } public MK.Glow.DebugView GetDebugView() { return debugView; } public MK.Glow.Quality GetQuality() { return quality; } public MK.Glow.AntiFlickerMode GetAntiFlickerMode() { return antiFlickerMode; } public MK.Glow.Workflow GetWorkflow() { return workflow; } public LayerMask GetSelectiveRenderLayerMask() { return selectiveRenderLayerMask; } public float GetAnamorphicRatio() { return anamorphicRatio; } public float GetLumaScale() { return lumaScale; } public float GetBlooming() { return blooming; } //Bloom public MK.Glow.MinMaxRange GetBloomThreshold() { return bloomThreshold; } public float GetBloomScattering() { return bloomScattering; } public float GetBloomIntensity() { return bloomIntensity; } //LensSurface public bool GetAllowLensSurface() { return allowLensSurface; } public Texture2D GetLensSurfaceDirtTexture() { return lensSurfaceDirtTexture; } public float GetLensSurfaceDirtIntensity() { return lensSurfaceDirtIntensity; } public Texture2D GetLensSurfaceDiffractionTexture() { return lensSurfaceDiffractionTexture; } public float GetLensSurfaceDiffractionIntensity() { return lensSurfaceDiffractionIntensity; } //LensFlare public bool GetAllowLensFlare() { return allowLensFlare; } public LensFlareStyle GetLensFlareStyle() { return lensFlareStyle; } public float GetLensFlareGhostFade() { return lensFlareGhostFade; } public float GetLensFlareGhostIntensity() { return lensFlareGhostIntensity; } public MK.Glow.MinMaxRange GetLensFlareThreshold() { return lensFlareThreshold; } public float GetLensFlareScattering() { return lensFlareScattering; } public Texture2D GetLensFlareColorRamp() { return lensFlareColorRamp; } public float GetLensFlareChromaticAberration() { return lensFlareChromaticAberration; } public int GetLensFlareGhostCount() { return lensFlareGhostCount; } public float GetLensFlareGhostDispersal() { return lensFlareGhostDispersal; } public float GetLensFlareHaloFade() { return lensFlareHaloFade; } public float GetLensFlareHaloIntensity() { return lensFlareHaloIntensity; } public float GetLensFlareHaloSize() { return lensFlareHaloSize; } public void SetLensFlareGhostFade(float fade) { lensFlareGhostFade = fade; } public void SetLensFlareGhostCount(int count) { lensFlareGhostCount = count; } public void SetLensFlareGhostDispersal(float dispersal) { lensFlareGhostDispersal = dispersal; } public void SetLensFlareHaloFade(float fade) { lensFlareHaloFade = fade; } public void SetLensFlareHaloSize(float size) { lensFlareHaloSize = size; } //Glare public bool GetAllowGlare() { return allowGlare; } public float GetGlareBlend() { return glareBlend; } public float GetGlareIntensity() { return glareIntensity; } public float GetGlareAngle() { return glareAngle; } public MK.Glow.MinMaxRange GetGlareThreshold() { return glareThreshold; } public int GetGlareStreaks() { return glareStreaks; } public void SetGlareStreaks(int count) { glareStreaks = count; } public float GetGlareScattering() { return glareScattering; } public GlareStyle GetGlareStyle() { return glareStyle; } //Sample0 public float GetGlareSample0Scattering() { return glareSample0Scattering; } public float GetGlareSample0Angle() { return glareSample0Angle; } public float GetGlareSample0Intensity() { return glareSample0Intensity; } public float GetGlareSample0Offset() { return glareSample0Offset; } public void SetGlareSample0Scattering(float scattering) { glareSample0Scattering = scattering; } public void SetGlareSample0Angle(float angle) { glareSample0Angle = angle; } public void SetGlareSample0Intensity(float intensity) { glareSample0Intensity = intensity; } public void SetGlareSample0Offset(float offset) { glareSample0Offset = offset; } //Sample1 public float GetGlareSample1Scattering() { return glareSample1Scattering; } public float GetGlareSample1Angle() { return glareSample1Angle; } public float GetGlareSample1Intensity() { return glareSample1Intensity; } public float GetGlareSample1Offset() { return glareSample1Offset; } public void SetGlareSample1Scattering(float scattering) { glareSample1Scattering = scattering; } public void SetGlareSample1Angle(float angle) { glareSample1Angle = angle; } public void SetGlareSample1Intensity(float intensity) { glareSample1Intensity = intensity; } public void SetGlareSample1Offset(float offset) { glareSample1Offset = offset; } //Sample2 public float GetGlareSample2Scattering() { return glareSample2Scattering; } public float GetGlareSample2Angle() { return glareSample2Angle; } public float GetGlareSample2Intensity() { return glareSample2Intensity; } public float GetGlareSample2Offset() { return glareSample2Offset; } public void SetGlareSample2Scattering(float scattering) { glareSample2Scattering = scattering; } public void SetGlareSample2Angle(float angle) { glareSample2Angle = angle; } public void SetGlareSample2Intensity(float intensity) { glareSample2Intensity = intensity; } public void SetGlareSample2Offset(float offset) { glareSample2Offset = offset; } //Sample3 public float GetGlareSample3Scattering() { return glareSample3Scattering; } public float GetGlareSample3Angle() { return glareSample3Angle; } public float GetGlareSample3Intensity() { return glareSample3Intensity; } public float GetGlareSample3Offset() { return glareSample3Offset; } public void SetGlareSample3Scattering(float scattering) { glareSample3Scattering = scattering; } public void SetGlareSample3Angle(float angle) { glareSample3Angle = angle; } public void SetGlareSample3Intensity(float intensity) { glareSample3Intensity = intensity; } public void SetGlareSample3Offset(float offset) { glareSample3Offset = offset; } class MKGlowRenderPass : ScriptableRenderPass, MK.Glow.ICameraData { private MK.Glow.URP.MKGlow _mKGlowVolumeComponent = null; private MK.Glow.URP.MKGlow mKGlowVolumeComponent { get { _mKGlowVolumeComponent = VolumeManager.instance.stack.GetComponent(); return _mKGlowVolumeComponent; } } internal Effect effect = new Effect(); internal ScriptableRenderer scriptableRenderer; internal MK.Glow.ISettings settingsRendererFeature = null; internal Workmode workmode = Workmode.PostProcessVolumes; private RenderTarget sourceRenderTarget, destinationRenderTarget; private RenderingData _renderingData; private MK.Glow.ISettings _settingsPostProcessingVolume = null; private MK.Glow.ISettings _activeSettings = null; private RenderTextureDescriptor _sourceDescriptor; private readonly int _rendererBufferID = Shader.PropertyToID("_MKGlowScriptableRendererOutput"); private readonly string _profilerName = "MKGlow"; private Material _copyMaterial; private readonly Shader _copyShader = Shader.Find("Hidden/MK/Glow/URP/Copy"); private readonly int _mainTexID = Shader.PropertyToID("_MainTex"); public MKGlowRenderPass() { this.renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing; this._copyMaterial = new Material(_copyShader) { hideFlags = HideFlags.HideAndDontSave }; } public void Dispose() { PipelineExtensions.Destroy(_copyMaterial); } public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor) { _sourceDescriptor = cameraTextureDescriptor; } #if UNITY_2021_3_OR_NEWER public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData) { base.OnCameraSetup(cmd, ref renderingData); #if UNITY_2022_1_OR_NEWER sourceRenderTarget.renderTargetIdentifier = scriptableRenderer.cameraColorTargetHandle; #else sourceRenderTarget.renderTargetIdentifier = scriptableRenderer.cameraColorTarget; #endif } #endif private static Mesh _fullScreenMesh; public static Mesh fullscreenMesh { get { if (_fullScreenMesh != null) return _fullScreenMesh; float topV = 1.0f; float bottomV = 0.0f; _fullScreenMesh = new Mesh { name = "Fullscreen Quad" }; _fullScreenMesh.SetVertices(new List { new Vector3(-1.0f, -1.0f, 0.0f), new Vector3(-1.0f, 1.0f, 0.0f), new Vector3(1.0f, -1.0f, 0.0f), new Vector3(1.0f, 1.0f, 0.0f) }); _fullScreenMesh.SetUVs(0, new List { new Vector2(0.0f, bottomV), new Vector2(0.0f, topV), new Vector2(1.0f, bottomV), new Vector2(1.0f, topV) }); _fullScreenMesh.SetIndices(new[] { 0, 1, 2, 2, 1, 3 }, MeshTopology.Triangles, 0, false); _fullScreenMesh.UploadMeshData(true); return _fullScreenMesh; } } public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) { if(workmode == Workmode.PostProcessVolumes) { if(mKGlowVolumeComponent == null || renderingData.cameraData.camera == null) return; if(!mKGlowVolumeComponent.IsActive()) return; } else { if(renderingData.cameraData.camera == null) return; } _settingsPostProcessingVolume = mKGlowVolumeComponent; _activeSettings = workmode == Workmode.PostProcessVolumes ? _settingsPostProcessingVolume : settingsRendererFeature; CommandBuffer cmd = CommandBufferPool.Get(_profilerName); _renderingData = renderingData; #if !UNITY_2021_3_OR_NEWER sourceRenderTarget.renderTargetIdentifier = scriptableRenderer.cameraColorTarget; #endif destinationRenderTarget.identifier = _rendererBufferID; #if UNITY_2018_2_OR_NEWER destinationRenderTarget.renderTargetIdentifier = new RenderTargetIdentifier(destinationRenderTarget.identifier, 0, CubemapFace.Unknown, -1); #else destinationRenderTarget.renderTargetIdentifier = new RenderTargetIdentifier(destinationRenderTarget.identifier); #endif cmd.GetTemporaryRT(destinationRenderTarget.identifier, _sourceDescriptor, FilterMode.Bilinear); cmd.SetGlobalTexture(_mainTexID, sourceRenderTarget.renderTargetIdentifier); cmd.SetRenderTarget(destinationRenderTarget.renderTargetIdentifier, 0, CubemapFace.Unknown, -1); cmd.DrawMesh(fullscreenMesh, Matrix4x4.identity, _copyMaterial, 0, 0); effect.Build(destinationRenderTarget, sourceRenderTarget, _activeSettings, cmd, this, renderingData.cameraData.camera); cmd.ReleaseTemporaryRT(destinationRenderTarget.identifier); context.ExecuteCommandBuffer(cmd); cmd.Clear(); CommandBufferPool.Release(cmd); } ///////////////////////////////////////////////////////////////////////////////////////////// // Camera Data ///////////////////////////////////////////////////////////////////////////////////////////// public int GetCameraWidth() { return _renderingData.cameraData.cameraTargetDescriptor.width; } public int GetCameraHeight() { return _renderingData.cameraData.cameraTargetDescriptor.height; } public bool GetStereoEnabled() { #if UNITY_2020_2_OR_NEWER return _renderingData.cameraData.xrRendering; #else return _renderingData.cameraData.isStereoEnabled; #endif } public float GetAspect() { return _renderingData.cameraData.camera.aspect; } public Matrix4x4 GetWorldToCameraMatrix() { return _renderingData.cameraData.camera.worldToCameraMatrix; } public bool GetOverwriteDescriptor() { return false; } public UnityEngine.Rendering.TextureDimension GetOverwriteDimension() { return UnityEngine.Rendering.TextureDimension.Tex2D; } public int GetOverwriteVolumeDepth() { return 1; } public bool GetTargetTexture() { return _renderingData.cameraData.camera.targetTexture != null ? true : false; } } private MKGlowRenderPass _mkGlowRenderPass; private readonly string _componentName = "MKGlow"; public override void Create() { _mkGlowRenderPass = new MKGlowRenderPass(); _mkGlowRenderPass.effect.Enable(RenderPipeline.SRP); _mkGlowRenderPass.settingsRendererFeature = this; } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { name = _componentName; _mkGlowRenderPass.workmode = workmode; _mkGlowRenderPass.ConfigureInput(ScriptableRenderPassInput.Color); if(workmode == Workmode.Global) { _mkGlowRenderPass.scriptableRenderer = renderer; renderer.EnqueuePass(_mkGlowRenderPass); } else { if(renderingData.cameraData.postProcessEnabled) { _mkGlowRenderPass.scriptableRenderer = renderer; renderer.EnqueuePass(_mkGlowRenderPass); } } } protected override void Dispose(bool disposing) { _mkGlowRenderPass?.effect.Disable(); _mkGlowRenderPass?.Dispose(); _mkGlowRenderPass = null; } } }