using UnityEngine.Rendering; namespace UnityEngine.PostProcessing { using DebugMode = BuiltinDebugViewsModel.Mode; public sealed class DepthOfFieldComponent : PostProcessingComponentRenderTexture { static class Uniforms { internal static readonly int _DepthOfFieldTex = Shader.PropertyToID("_DepthOfFieldTex"); internal static readonly int _DepthOfFieldCoCTex = Shader.PropertyToID("_DepthOfFieldCoCTex"); internal static readonly int _Distance = Shader.PropertyToID("_Distance"); internal static readonly int _LensCoeff = Shader.PropertyToID("_LensCoeff"); internal static readonly int _MaxCoC = Shader.PropertyToID("_MaxCoC"); internal static readonly int _RcpMaxCoC = Shader.PropertyToID("_RcpMaxCoC"); internal static readonly int _RcpAspect = Shader.PropertyToID("_RcpAspect"); internal static readonly int _MainTex = Shader.PropertyToID("_MainTex"); internal static readonly int _CoCTex = Shader.PropertyToID("_CoCTex"); internal static readonly int _TaaParams = Shader.PropertyToID("_TaaParams"); internal static readonly int _DepthOfFieldParams = Shader.PropertyToID("_DepthOfFieldParams"); } const string k_ShaderString = "Hidden/Post FX/Depth Of Field"; public override bool active { get { return model.enabled && !context.interrupted; } } public override DepthTextureMode GetCameraFlags() { return DepthTextureMode.Depth; } RenderTexture m_CoCHistory; // Height of the 35mm full-frame format (36mm x 24mm) const float k_FilmHeight = 0.024f; float CalculateFocalLength() { var settings = model.settings; if (!settings.useCameraFov) return settings.focalLength / 1000f; float fov = context.camera.fieldOfView * Mathf.Deg2Rad; return 0.5f * k_FilmHeight / Mathf.Tan(0.5f * fov); } float CalculateMaxCoCRadius(int screenHeight) { // Estimate the allowable maximum radius of CoC from the kernel // size (the equation below was empirically derived). float radiusInPixels = (float)model.settings.kernelSize * 4f + 6f; // Applying a 5% limit to the CoC radius to keep the size of // TileMax/NeighborMax small enough. return Mathf.Min(0.05f, radiusInPixels / screenHeight); } bool CheckHistory(int width, int height) { return m_CoCHistory != null && m_CoCHistory.IsCreated() && m_CoCHistory.width == width && m_CoCHistory.height == height; } RenderTextureFormat SelectFormat(RenderTextureFormat primary, RenderTextureFormat secondary) { if (SystemInfo.SupportsRenderTextureFormat(primary)) return primary; if (SystemInfo.SupportsRenderTextureFormat(secondary)) return secondary; return RenderTextureFormat.Default; } public void Prepare(RenderTexture source, Material uberMaterial, bool antialiasCoC, Vector2 taaJitter, float taaBlending) { var settings = model.settings; var colorFormat = RenderTextureFormat.DefaultHDR; var cocFormat = SelectFormat(RenderTextureFormat.R8, RenderTextureFormat.RHalf); // Avoid using R8 on OSX with Metal. #896121, https://goo.gl/MgKqu6 #if (UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX) && !UNITY_2017_1_OR_NEWER if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal) cocFormat = SelectFormat(RenderTextureFormat.RHalf, RenderTextureFormat.Default); #endif // Material setup var f = CalculateFocalLength(); var s1 = Mathf.Max(settings.focusDistance, f); var aspect = (float)source.width / source.height; var coeff = f * f / (settings.aperture * (s1 - f) * k_FilmHeight * 2); var maxCoC = CalculateMaxCoCRadius(source.height); var material = context.materialFactory.Get(k_ShaderString); material.SetFloat(Uniforms._Distance, s1); material.SetFloat(Uniforms._LensCoeff, coeff); material.SetFloat(Uniforms._MaxCoC, maxCoC); material.SetFloat(Uniforms._RcpMaxCoC, 1f / maxCoC); material.SetFloat(Uniforms._RcpAspect, 1f / aspect); // CoC calculation pass var rtCoC = context.renderTextureFactory.Get(context.width, context.height, 0, cocFormat, RenderTextureReadWrite.Linear); Graphics.Blit(null, rtCoC, material, 0); if (antialiasCoC) { // CoC temporal filter pass material.SetTexture(Uniforms._CoCTex, rtCoC); var blend = CheckHistory(context.width, context.height) ? taaBlending : 0f; material.SetVector(Uniforms._TaaParams, new Vector3(taaJitter.x, taaJitter.y, blend)); var rtFiltered = RenderTexture.GetTemporary(context.width, context.height, 0, cocFormat); Graphics.Blit(m_CoCHistory, rtFiltered, material, 1); context.renderTextureFactory.Release(rtCoC); if (m_CoCHistory != null) RenderTexture.ReleaseTemporary(m_CoCHistory); m_CoCHistory = rtCoC = rtFiltered; } // Downsampling and prefiltering pass var rt1 = context.renderTextureFactory.Get(context.width / 2, context.height / 2, 0, colorFormat); material.SetTexture(Uniforms._CoCTex, rtCoC); Graphics.Blit(source, rt1, material, 2); // Bokeh simulation pass var rt2 = context.renderTextureFactory.Get(context.width / 2, context.height / 2, 0, colorFormat); Graphics.Blit(rt1, rt2, material, 3 + (int)settings.kernelSize); // Postfilter pass Graphics.Blit(rt2, rt1, material, 7); // Give the results to the uber shader. uberMaterial.SetVector(Uniforms._DepthOfFieldParams, new Vector3(s1, coeff, maxCoC)); if (context.profile.debugViews.IsModeActive(DebugMode.FocusPlane)) { uberMaterial.EnableKeyword("DEPTH_OF_FIELD_COC_VIEW"); context.Interrupt(); } else { uberMaterial.SetTexture(Uniforms._DepthOfFieldTex, rt1); uberMaterial.SetTexture(Uniforms._DepthOfFieldCoCTex, rtCoC); uberMaterial.EnableKeyword("DEPTH_OF_FIELD"); } context.renderTextureFactory.Release(rt2); } public override void OnDisable() { if (m_CoCHistory != null) RenderTexture.ReleaseTemporary(m_CoCHistory); m_CoCHistory = null; } } }