using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; using UnityEngine.Rendering.RenderGraphModule; using UnityEngine.Rendering.RenderGraphModule.Util; public class TiltShiftFeature : ScriptableRendererFeature { [System.Serializable] public class Settings { [Tooltip("Vertical offset of the sharp focus band from screen centre. Positive = up.")] [Range(-0.4f, 0.4f)] public float centerOffset = 0f; [Tooltip("Height of the fully-sharp band as a fraction of screen height.")] [Range(0.01f, 0.8f)] public float focusWidth = 0.2f; [Tooltip("Distance over which blur fades in at each edge of the focus band.")] [Range(0.01f, 0.4f)] public float falloffRange = 0.15f; [Tooltip("Maximum blur radius in pixels at the extreme top and bottom.")] [Range(1f, 64f)] public float maxBlurRadius = 20f; [Tooltip("Samples per side per pass (higher = smoother blur, higher cost).")] [Range(2, 20)] public int sampleCount = 8; public RenderPassEvent injectionPoint = RenderPassEvent.AfterRenderingPostProcessing; } public Settings settings = new Settings(); TiltShiftPass _pass; Material _material; public override void Create() { var shader = Shader.Find("Custom/TiltShift"); if (shader == null) { Debug.LogWarning("[TiltShift] Shader 'Custom/TiltShift' not found. " + "Make sure TiltShift.shader is in the project."); return; } _material = CoreUtils.CreateEngineMaterial(shader); _pass = new TiltShiftPass(_material, settings); _pass.renderPassEvent = settings.injectionPoint; } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { if (_material == null || _pass == null) return; // Skip scene view and preview cameras to avoid visual noise. var camType = renderingData.cameraData.cameraType; if (camType == CameraType.Preview || camType == CameraType.Reflection) return; _pass.UpdateSettings(settings); renderer.EnqueuePass(_pass); } protected override void Dispose(bool disposing) { CoreUtils.Destroy(_material); } // ------------------------------------------------------------------------- class TiltShiftPass : ScriptableRenderPass { static readonly int s_CenterOffset = Shader.PropertyToID("_TiltShift_CenterOffset"); static readonly int s_FocusWidth = Shader.PropertyToID("_TiltShift_FocusWidth"); static readonly int s_FalloffRange = Shader.PropertyToID("_TiltShift_FalloffRange"); static readonly int s_MaxBlurRadius = Shader.PropertyToID("_TiltShift_MaxBlurRadius"); static readonly int s_SampleCount = Shader.PropertyToID("_TiltShift_SampleCount"); readonly Material _material; Settings _settings; public TiltShiftPass(Material material, Settings settings) { _material = material; _settings = settings; requiresIntermediateTexture = true; } public void UpdateSettings(Settings settings) => _settings = settings; public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) { var resourceData = frameData.Get(); if (resourceData.isActiveTargetBackBuffer) return; // Push current settings to the material. _material.SetFloat(s_CenterOffset, _settings.centerOffset); _material.SetFloat(s_FocusWidth, _settings.focusWidth); _material.SetFloat(s_FalloffRange, _settings.falloffRange); _material.SetFloat(s_MaxBlurRadius, _settings.maxBlurRadius); _material.SetInt(s_SampleCount, _settings.sampleCount); var source = resourceData.activeColorTexture; // Intermediate texture for horizontal-blur result. var desc = renderGraph.GetTextureDesc(source); desc.clearBuffer = false; desc.name = "TiltShift_Temp"; TextureHandle temp = renderGraph.CreateTexture(desc); // Final texture for vertical-blur result. desc.name = "TiltShift_Final"; TextureHandle final = renderGraph.CreateTexture(desc); // Pass 0 — horizontal blur: source → temp var paraH = new RenderGraphUtils.BlitMaterialParameters(source, temp, _material, 0); renderGraph.AddBlitPass(paraH, passName: "TiltShift_H"); // Pass 1 — vertical blur: temp → final var paraV = new RenderGraphUtils.BlitMaterialParameters(temp, final, _material, 1); renderGraph.AddBlitPass(paraV, passName: "TiltShift_V"); // Redirect the camera colour so subsequent passes see our result. resourceData.cameraColor = final; } } }