#if CINEMACHINE_HDRP using UnityEngine; using UnityEngine.Rendering.HighDefinition; using UnityEngine.Rendering; namespace Unity.Cinemachine { internal class FocusDistance : CustomPass { static class Uniforms { internal const string _FocusDistanceParams = "_FocusDistanceParams"; internal const string _FocusDistanceOutput = "_FocusDistanceOutput"; internal const string FocusDistanceKeyword = "FOCUS_DISTANCE"; } [Tooltip("Stickier auto focus is more stable (less switching back and forth as tiny " + "grass blades cross the camera), but requires looking at a bigger uniform-ish area to switch focus to it.")] [Range(0, 1)] public float Stickiness = 0.4f; [Tooltip("Radius of the FocusDistance sensor in the center of the screen. A value of 1 would fill the screen. " + "It's recommended to keep this quite small. Default value is 0.02")] [Range(0, 1)] public float KernelRadius = 0.02f; [Tooltip("Depth tolerance for inclusion in the same depth bucket. Effectively the depth resolution.")] [Range(0, 1)] public float DepthTolerance = 0.02f; [Tooltip("Position on the screen of the depth sensor. (0, 0) is screen center.")] public Vector2 ScreenPosition; [Tooltip("Must be the FocusDistance compute shader.")] public ComputeShader ComputeShader; [Tooltip("The camera whose depth buffer will be checked.")] public Camera Camera; [Tooltip("If true, then the focus distance will be pushed to the camera's focusDistance field.")] public bool PushToCamera = true; /// Initialize this with the current focus distance, to be used as a default value public float CurrentFocusDistance; /// This holds the computed output. Clients can read it as desired public float ComputedFocusDistance { get; private set; } // Same As FocusDistance.compute struct FocusDistanceParams { public uint VoteBias; // 0...15 public float DepthTolerance; // 0.02 public float SampleRadius; // 0.02 public float SamplePosX; // 0 public float SamplePosY; // 0 public float DefaultFocusDistance; // current focus distance }; ComputeBuffer m_FocusDistanceParamsCB; FocusDistanceParams[] m_FocusDistanceParams = new FocusDistanceParams[1]; // Same As FocusDistance.compute struct FocusDistanceOutput { public float FocusDistance; } ComputeBuffer m_FocusDistanceOutputCB; FocusDistanceOutput[] m_FocusDistanceOutput = new FocusDistanceOutput[1]; // It can be used to configure render targets and their clear state. Also to create temporary render target textures. // When empty this render pass will render to the active camera render target. // You should never call CommandBuffer.SetRenderTarget. Instead call ConfigureTarget and ConfigureClear. // The render pipeline will ensure target setup and clearing happens in an performance manner. protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) { if (m_FocusDistanceParamsCB == null) m_FocusDistanceParamsCB = new ComputeBuffer(1, 6 * 4); // sizeof(FocusDistanceParams) if (m_FocusDistanceOutputCB == null) m_FocusDistanceOutputCB = new ComputeBuffer(1, 1 * 4); // sizeof(FocusDistanceOutput) } protected override void Cleanup() { if (m_FocusDistanceParamsCB != null) m_FocusDistanceParamsCB.Release(); m_FocusDistanceParamsCB = null; if (m_FocusDistanceOutputCB != null) m_FocusDistanceOutputCB.Release(); m_FocusDistanceOutputCB = null; } protected override void Execute(CustomPassContext ctx) { if (Camera == null || ComputeShader == null || ctx.hdCamera.camera != Camera) return; ctx.cmd.BeginSample(Uniforms.FocusDistanceKeyword); ctx.cmd.EnableShaderKeyword(Uniforms.FocusDistanceKeyword); m_FocusDistanceParams[0].VoteBias = (uint)Mathf.RoundToInt(Stickiness * 15.0f); m_FocusDistanceParams[0].DepthTolerance = DepthTolerance; m_FocusDistanceParams[0].SampleRadius = KernelRadius; m_FocusDistanceParams[0].SamplePosX = ScreenPosition.x; m_FocusDistanceParams[0].SamplePosY = ScreenPosition.y; m_FocusDistanceParams[0].DefaultFocusDistance = (PushToCamera || CurrentFocusDistance <= 0) ? Camera.focusDistance : CurrentFocusDistance; m_FocusDistanceParamsCB.SetData(m_FocusDistanceParams); ctx.cmd.SetComputeBufferParam(ComputeShader, 0, Uniforms._FocusDistanceParams, m_FocusDistanceParamsCB); ctx.cmd.SetComputeBufferParam(ComputeShader, 0, Uniforms._FocusDistanceOutput, m_FocusDistanceOutputCB); ctx.cmd.DispatchCompute(ComputeShader, 0, 1, 1, 1); ctx.cmd.SetGlobalBuffer(Uniforms._FocusDistanceOutput, m_FocusDistanceOutputCB); ctx.cmd.EndSample(Uniforms.FocusDistanceKeyword); // Read back the output when complete ctx.cmd.RequestAsyncReadback(m_FocusDistanceOutputCB, (req) => { if (m_FocusDistanceOutputCB != null && Camera != null) { m_FocusDistanceOutputCB.GetData(m_FocusDistanceOutput); ComputedFocusDistance = m_FocusDistanceOutput[0].FocusDistance; if (PushToCamera) Camera.focusDistance = ComputedFocusDistance; } }); } } } #endif