using System; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEngine.Serialization; using UnityEngine.Assertions; namespace UnityEngine.Rendering.Universal { /// /// Holds information about whether to override certain camera rendering options from the render pipeline asset. /// When set to Off option will be disabled regardless of what is set on the pipeline asset. /// When set to On option will be enabled regardless of what is set on the pipeline asset. /// When set to UsePipelineSetting value set in the . /// public enum CameraOverrideOption { /// /// Use this to disable regardless of what is set on the pipeline asset. /// Off, /// /// Use this to enable regardless of what is set on the pipeline asset. /// On, /// /// Use this to choose the setting set on the pipeline asset. /// [InspectorName("Use settings from Render Pipeline Asset")] UsePipelineSettings, } /// /// Options to control the renderer override. /// This enum is no longer in use. /// [Obsolete("Renderer override is no longer used, renderers are referenced by index on the pipeline asset.")] public enum RendererOverrideOption { /// /// Use this to choose a custom override. /// Custom, /// /// Use this to choose the setting set on the pipeline asset. /// UsePipelineSettings, } /// /// Holds information about the post-processing anti-aliasing mode. /// When set to None no post-processing anti-aliasing pass will be performed. /// When set to Fast a fast approximated anti-aliasing pass will render when resolving the camera to screen. /// When set to SubpixelMorphologicalAntiAliasing SMAA pass will render when resolving the camera to screen. /// You can choose the SMAA quality by setting . /// public enum AntialiasingMode { /// /// Use this to have no post-processing anti-aliasing pass performed. /// [InspectorName("No Anti-aliasing")] None, /// /// Use this to have a fast approximated anti-aliasing pass rendered when resolving the camera to screen /// [InspectorName("Fast Approximate Anti-aliasing (FXAA)")] FastApproximateAntialiasing, /// /// Use this to have a SubpixelMorphologicalAntiAliasing SMAA pass rendered when resolving the camera to screen /// You can choose the SMAA quality by setting . /// [InspectorName("Subpixel Morphological Anti-aliasing (SMAA)")] SubpixelMorphologicalAntiAliasing, /// /// Use this to have a temporal anti-aliasing pass rendered when resolving camera to screen. /// [InspectorName("Temporal Anti-aliasing (TAA)")] TemporalAntiAliasing, } /// /// Holds information about the render type of a camera. Options are Base or Overlay. /// Base rendering type allows the camera to render to either the screen or to a texture. /// Overlay rendering type allows the camera to render on top of a previous camera output, thus compositing camera results. /// public enum CameraRenderType { /// /// Use this to select the base camera render type. /// Base rendering type allows the camera to render to either the screen or to a texture. /// Base, /// /// Use this to select the overlay camera render type. /// Overlay rendering type allows the camera to render on top of a previous camera output, thus compositing camera results. /// Overlay, } /// /// Controls SubpixelMorphologicalAntiAliasing SMAA anti-aliasing quality. /// public enum AntialiasingQuality { /// /// Use this to select the low SubpixelMorphologicalAntiAliasing SMAA quality /// Low, /// /// Use this to select the medium SubpixelMorphologicalAntiAliasing SMAA quality /// Medium, /// /// Use this to select the high SubpixelMorphologicalAntiAliasing SMAA quality /// High } /// /// Contains extension methods for Camera class. /// public static class CameraExtensions { /// /// Universal Render Pipeline exposes additional rendering data in a separate component. /// This method returns the additional data component for the given camera or create one if it doesn't exist yet. /// /// /// The UniversalAdditionalCameraData for this camera. /// public static UniversalAdditionalCameraData GetUniversalAdditionalCameraData(this Camera camera) { var gameObject = camera.gameObject; bool componentExists = gameObject.TryGetComponent(out var cameraData); if (!componentExists) cameraData = gameObject.AddComponent(); return cameraData; } /// /// Returns the VolumeFrameworkUpdateMode set on the camera. /// /// /// public static VolumeFrameworkUpdateMode GetVolumeFrameworkUpdateMode(this Camera camera) { UniversalAdditionalCameraData cameraData = camera.GetUniversalAdditionalCameraData(); return cameraData.volumeFrameworkUpdateMode; } /// /// Sets the VolumeFrameworkUpdateMode for the camera. /// /// /// public static void SetVolumeFrameworkUpdateMode(this Camera camera, VolumeFrameworkUpdateMode mode) { UniversalAdditionalCameraData cameraData = camera.GetUniversalAdditionalCameraData(); if (cameraData.volumeFrameworkUpdateMode == mode) return; bool requiredUpdatePreviously = cameraData.requiresVolumeFrameworkUpdate; cameraData.volumeFrameworkUpdateMode = mode; // We only update the local volume stacks for cameras set to ViaScripting. // Otherwise it will be updated in every frame. // We also check the previous value to make sure we're not updating when // switching between Camera ViaScripting and the URP Asset set to ViaScripting if (requiredUpdatePreviously && !cameraData.requiresVolumeFrameworkUpdate) camera.UpdateVolumeStack(cameraData); } /// /// Updates the volume stack for this camera. /// This function will only update the stack when the camera has VolumeFrameworkUpdateMode set to ViaScripting /// or when it set to UsePipelineSettings and the update mode on the Render Pipeline Asset is set to ViaScripting. /// /// public static void UpdateVolumeStack(this Camera camera) { UniversalAdditionalCameraData cameraData = camera.GetUniversalAdditionalCameraData(); camera.UpdateVolumeStack(cameraData); } /// /// Updates the volume stack for this camera. /// This function will only update the stack when the camera has ViaScripting selected or if /// the camera is set to UsePipelineSettings and the Render Pipeline Asset is set to ViaScripting. /// /// /// public static void UpdateVolumeStack(this Camera camera, UniversalAdditionalCameraData cameraData) { Assert.IsNotNull(cameraData, "cameraData can not be null when updating the volume stack."); // We only update the local volume stacks for cameras set to ViaScripting. // Otherwise it will be updated in the frame. if (cameraData.requiresVolumeFrameworkUpdate) return; // Create stack for camera if (cameraData.volumeStack == null) cameraData.GetOrCreateVolumeStack(); camera.GetVolumeLayerMaskAndTrigger(cameraData, out LayerMask layerMask, out Transform trigger); VolumeManager.instance.Update(cameraData.volumeStack, trigger, layerMask); } /// /// Destroys the volume stack for this camera. /// /// public static void DestroyVolumeStack(this Camera camera) { UniversalAdditionalCameraData cameraData = camera.GetUniversalAdditionalCameraData(); camera.DestroyVolumeStack(cameraData); } /// /// Destroys the volume stack for this camera. /// /// /// public static void DestroyVolumeStack(this Camera camera, UniversalAdditionalCameraData cameraData) { if (cameraData == null || cameraData.volumeStack == null) return; cameraData.volumeStack = null; } /// /// Returns the mask and trigger assigned for volumes on the camera. /// /// /// /// /// internal static void GetVolumeLayerMaskAndTrigger(this Camera camera, UniversalAdditionalCameraData cameraData, out LayerMask layerMask, out Transform trigger) { // Default values when there's no additional camera data available layerMask = 1; // "Default" trigger = camera.transform; if (cameraData != null) { layerMask = cameraData.volumeLayerMask; trigger = (cameraData.volumeTrigger != null) ? cameraData.volumeTrigger : trigger; } else if (camera.cameraType == CameraType.SceneView) { // Try to mirror the MainCamera volume layer mask for the scene view - do not mirror the target var mainCamera = Camera.main; UniversalAdditionalCameraData mainAdditionalCameraData = null; if (mainCamera != null && mainCamera.TryGetComponent(out mainAdditionalCameraData)) { layerMask = mainAdditionalCameraData.volumeLayerMask; } trigger = (mainAdditionalCameraData != null && mainAdditionalCameraData.volumeTrigger != null) ? mainAdditionalCameraData.volumeTrigger : trigger; } } } static class CameraTypeUtility { static string[] s_CameraTypeNames = Enum.GetNames(typeof(CameraRenderType)).ToArray(); public static string GetName(this CameraRenderType type) { int typeInt = (int)type; if (typeInt < 0 || typeInt >= s_CameraTypeNames.Length) typeInt = (int)CameraRenderType.Base; return s_CameraTypeNames[typeInt]; } } /// /// Class containing various additional camera data used by URP. /// [DisallowMultipleComponent] [RequireComponent(typeof(Camera))] [ExecuteAlways] // NOTE: This is required to get calls to OnDestroy() always. Graphics resources are released in OnDestroy(). [URPHelpURL("universal-additional-camera-data")] public class UniversalAdditionalCameraData : MonoBehaviour, ISerializationCallbackReceiver, IAdditionalData { const string k_GizmoPath = "Packages/com.unity.render-pipelines.universal/Editor/Gizmos/"; const string k_BaseCameraGizmoPath = k_GizmoPath + "Camera_Base.png"; const string k_OverlayCameraGizmoPath = k_GizmoPath + "Camera_Base.png"; const string k_PostProcessingGizmoPath = k_GizmoPath + "Camera_PostProcessing.png"; [FormerlySerializedAs("renderShadows"), SerializeField] bool m_RenderShadows = true; [SerializeField] CameraOverrideOption m_RequiresDepthTextureOption = CameraOverrideOption.UsePipelineSettings; [SerializeField] CameraOverrideOption m_RequiresOpaqueTextureOption = CameraOverrideOption.UsePipelineSettings; [SerializeField] CameraRenderType m_CameraType = CameraRenderType.Base; [SerializeField] List m_Cameras = new List(); [SerializeField] int m_RendererIndex = -1; [SerializeField] LayerMask m_VolumeLayerMask = 1; // "Default" [SerializeField] Transform m_VolumeTrigger = null; [SerializeField] VolumeFrameworkUpdateMode m_VolumeFrameworkUpdateModeOption = VolumeFrameworkUpdateMode.UsePipelineSettings; [SerializeField] bool m_RenderPostProcessing = false; [SerializeField] AntialiasingMode m_Antialiasing = AntialiasingMode.None; [SerializeField] AntialiasingQuality m_AntialiasingQuality = AntialiasingQuality.High; [SerializeField] bool m_StopNaN = false; [SerializeField] bool m_Dithering = false; [SerializeField] bool m_ClearDepth = true; [SerializeField] bool m_AllowXRRendering = true; [SerializeField] bool m_AllowHDROutput = true; [SerializeField] bool m_UseScreenCoordOverride; [SerializeField] Vector4 m_ScreenSizeOverride; [SerializeField] Vector4 m_ScreenCoordScaleBias; [NonSerialized] Camera m_Camera; // Deprecated: [FormerlySerializedAs("requiresDepthTexture"), SerializeField] bool m_RequiresDepthTexture = false; [FormerlySerializedAs("requiresColorTexture"), SerializeField] bool m_RequiresColorTexture = false; [HideInInspector] [SerializeField] float m_Version = 2; // These persist over multiple frames [NonSerialized] MotionVectorsPersistentData m_MotionVectorsPersistentData = new MotionVectorsPersistentData(); // The URP camera history texture manager. Persistent per camera textures. [NonSerialized] internal UniversalCameraHistory m_History = new UniversalCameraHistory(); [SerializeField] internal TemporalAA.Settings m_TaaSettings = TemporalAA.Settings.Create(); /// /// The serialized version of the class. Used for upgrading. /// public float version => m_Version; static UniversalAdditionalCameraData s_DefaultAdditionalCameraData = null; internal static UniversalAdditionalCameraData defaultAdditionalCameraData { get { if (s_DefaultAdditionalCameraData == null) s_DefaultAdditionalCameraData = new UniversalAdditionalCameraData(); return s_DefaultAdditionalCameraData; } } #if UNITY_EDITOR internal new Camera camera #else internal Camera camera #endif { get { if (!m_Camera) { gameObject.TryGetComponent(out m_Camera); } return m_Camera; } } void Start() { // Need to ensure correct behavoiur for overlay cameras settings their clear flag to nothing. // This can't be done in the upgrade since the camera component can't be retrieved in the deserialization phase. // OnValidate ensure future cameras won't have this issue. if (m_CameraType == CameraRenderType.Overlay) camera.clearFlags = CameraClearFlags.Nothing; } /// /// Controls if this camera should render shadows. /// public bool renderShadows { get => m_RenderShadows; set => m_RenderShadows = value; } /// /// Controls if a camera should render depth. /// The depth is available to be bound in shaders as _CameraDepthTexture. /// /// public CameraOverrideOption requiresDepthOption { get => m_RequiresDepthTextureOption; set => m_RequiresDepthTextureOption = value; } /// /// Controls if a camera should copy the color contents of a camera after rendering opaques. /// The color texture is available to be bound in shaders as _CameraOpaqueTexture. /// public CameraOverrideOption requiresColorOption { get => m_RequiresOpaqueTextureOption; set => m_RequiresOpaqueTextureOption = value; } /// /// Returns the camera renderType. /// . /// public CameraRenderType renderType { get => m_CameraType; set => m_CameraType = value; } /// /// Returns the camera stack. Only valid for Base cameras. /// Will return null if it is not a Base camera. /// . /// public List cameraStack { get { if (renderType != CameraRenderType.Base) { var camera = gameObject.GetComponent(); Debug.LogWarning(string.Format("{0}: This camera is of {1} type. Only Base cameras can have a camera stack.", camera.name, renderType)); return null; } if (!scriptableRenderer.SupportsCameraStackingType(CameraRenderType.Base)) { var camera = gameObject.GetComponent(); Debug.LogWarning(string.Format("{0}: This camera has a ScriptableRenderer that doesn't support camera stacking. Camera stack is null.", camera.name)); return null; } return m_Cameras; } } internal void UpdateCameraStack() { #if UNITY_EDITOR Undo.RecordObject(this, "Update camera stack"); #endif int prev = m_Cameras.Count; m_Cameras.RemoveAll(cam => cam == null); int curr = m_Cameras.Count; int removedCamsCount = prev - curr; if (removedCamsCount != 0) { Debug.LogWarning(name + ": " + removedCamsCount + " camera overlay" + (removedCamsCount > 1 ? "s" : "") + " no longer exists and will be removed from the camera stack."); } } /// /// If true, this camera will clear depth value before rendering. Only valid for Overlay cameras. /// public bool clearDepth { get => m_ClearDepth; } /// /// Returns true if this camera needs to render depth information in a texture. /// If enabled, depth texture is available to be bound and read from shaders as _CameraDepthTexture after rendering skybox. /// public bool requiresDepthTexture { get { if (m_RequiresDepthTextureOption == CameraOverrideOption.UsePipelineSettings) { return UniversalRenderPipeline.asset.supportsCameraDepthTexture; } else { return m_RequiresDepthTextureOption == CameraOverrideOption.On; } } set { m_RequiresDepthTextureOption = (value) ? CameraOverrideOption.On : CameraOverrideOption.Off; } } /// /// Returns true if this camera requires to color information in a texture. /// If enabled, color texture is available to be bound and read from shaders as _CameraOpaqueTexture after rendering skybox. /// public bool requiresColorTexture { get { if (m_RequiresOpaqueTextureOption == CameraOverrideOption.UsePipelineSettings) { return UniversalRenderPipeline.asset.supportsCameraOpaqueTexture; } else { return m_RequiresOpaqueTextureOption == CameraOverrideOption.On; } } set { m_RequiresOpaqueTextureOption = (value) ? CameraOverrideOption.On : CameraOverrideOption.Off; } } /// /// Returns the that is used to render this camera. /// public ScriptableRenderer scriptableRenderer { get { if (UniversalRenderPipeline.asset is null) return null; if (!UniversalRenderPipeline.asset.ValidateRendererData(m_RendererIndex)) { int defaultIndex = UniversalRenderPipeline.asset.m_DefaultRendererIndex; Debug.LogWarning( $"Renderer at index {m_RendererIndex.ToString()} is missing for camera {camera.name}, falling back to Default Renderer. {UniversalRenderPipeline.asset.m_RendererDataList[defaultIndex].name}", UniversalRenderPipeline.asset); return UniversalRenderPipeline.asset.GetRenderer(defaultIndex); } return UniversalRenderPipeline.asset.GetRenderer(m_RendererIndex); } } /// /// Use this to set this Camera's current to one listed on the Render Pipeline Asset. Takes an index that maps to the list on the Render Pipeline Asset. /// /// The index that maps to the RendererData list on the currently assigned Render Pipeline Asset public void SetRenderer(int index) { m_RendererIndex = index; } /// /// Returns the selected scene-layers affecting this camera. /// public LayerMask volumeLayerMask { get => m_VolumeLayerMask; set => m_VolumeLayerMask = value; } /// /// Returns the Transform that acts as a trigger for Volume blending. /// public Transform volumeTrigger { get => m_VolumeTrigger; set => m_VolumeTrigger = value; } /// /// Returns the selected mode for Volume Frame Updates. /// internal VolumeFrameworkUpdateMode volumeFrameworkUpdateMode { get => m_VolumeFrameworkUpdateModeOption; set => m_VolumeFrameworkUpdateModeOption = value; } /// /// Returns true if this camera requires the volume framework to be updated every frame. /// public bool requiresVolumeFrameworkUpdate { get { if (m_VolumeFrameworkUpdateModeOption == VolumeFrameworkUpdateMode.UsePipelineSettings) { return UniversalRenderPipeline.asset.volumeFrameworkUpdateMode != VolumeFrameworkUpdateMode.ViaScripting; } return m_VolumeFrameworkUpdateModeOption == VolumeFrameworkUpdateMode.EveryFrame; } } /// /// Container for volume stacks in order to reuse stacks and avoid /// creating new ones every time a new camera is instantiated. /// private static List s_CachedVolumeStacks; /// /// Returns the current volume stack used by this camera. /// public VolumeStack volumeStack { get => m_VolumeStack; set { // If the volume stack is being removed, // add it back to the list so it can be reused later if (value == null && m_VolumeStack != null && m_VolumeStack.isValid) { if (s_CachedVolumeStacks == null) s_CachedVolumeStacks = new List(4); s_CachedVolumeStacks.Add(m_VolumeStack); } m_VolumeStack = value; } } VolumeStack m_VolumeStack = null; /// /// Tries to retrieve a volume stack from the container /// and creates a new one if that fails. /// internal void GetOrCreateVolumeStack() { // Try first to reuse a volume stack if (s_CachedVolumeStacks != null && s_CachedVolumeStacks.Count > 0) { int index = s_CachedVolumeStacks.Count - 1; var stack = s_CachedVolumeStacks[index]; s_CachedVolumeStacks.RemoveAt(index); if (stack.isValid) volumeStack = stack; } // Create a new stack if was not possible to reuse an old one if (volumeStack == null) volumeStack = VolumeManager.instance.CreateStack(); } /// /// Returns true if this camera should render post-processing. /// public bool renderPostProcessing { get => m_RenderPostProcessing; set => m_RenderPostProcessing = value; } /// /// Returns the current anti-aliasing mode used by this camera. /// . /// public AntialiasingMode antialiasing { get => m_Antialiasing; set => m_Antialiasing = value; } /// /// Returns the current anti-aliasing quality used by this camera. /// . /// public AntialiasingQuality antialiasingQuality { get => m_AntialiasingQuality; set => m_AntialiasingQuality = value; } /// /// Returns the current temporal anti-aliasing settings used by this camera. /// public ref TemporalAA.Settings taaSettings { get { return ref m_TaaSettings; } } /// /// Returns the URP camera history texture read access. /// Used to register requests and to read the existing history textures by external systems. /// public ICameraHistoryReadAccess history => m_History; // Returns the URP camera history texture manager with full access for internal systems. // NOTE: Only the pipeline should write/render history textures. Should be kept internal. // // The history is camera specific. The UniversalAdditionalCameraData is the URP specific camera (data). // Therefore it owns the UniversalCameraHistory. The history should follow the camera lifetime. internal UniversalCameraHistory historyManager => m_History; /// /// Motion data that persists over a frame. /// internal MotionVectorsPersistentData motionVectorsPersistentData => m_MotionVectorsPersistentData; /// /// Reset post-process history. /// public bool resetHistory { get => m_TaaSettings.resetHistoryFrames != 0; set { m_TaaSettings.resetHistoryFrames += value ? 1 : 0; m_MotionVectorsPersistentData.Reset(); // Reset the jitter period for consistent test results. // Not technically history, but this is here to avoid adding testing only public API. m_TaaSettings.jitterFrameCountOffset = -Time.frameCount; } } /// /// Returns true if this camera should automatically replace NaN/Inf in shaders by a black pixel to avoid breaking some effects. /// public bool stopNaN { get => m_StopNaN; set => m_StopNaN = value; } /// /// Returns true if this camera applies 8-bit dithering to the final render to reduce color banding /// public bool dithering { get => m_Dithering; set => m_Dithering = value; } /// /// Returns true if this camera allows render in XR. /// public bool allowXRRendering { get => m_AllowXRRendering; set => m_AllowXRRendering = value; } /// /// Returns true if the camera uses Screen Coordinates Override. /// public bool useScreenCoordOverride { get => m_UseScreenCoordOverride; set => m_UseScreenCoordOverride = value; } /// /// Screen size used when Screen Coordinates Override is active. /// public Vector4 screenSizeOverride { get => m_ScreenSizeOverride; set => m_ScreenSizeOverride = value; } /// /// Transform applied to screen coordinates when Screen Coordinates Override is active. /// public Vector4 screenCoordScaleBias { get => m_ScreenCoordScaleBias; set => m_ScreenCoordScaleBias = value; } /// /// Returns true if this camera allows outputting to HDR displays. /// public bool allowHDROutput { get => m_AllowHDROutput; set => m_AllowHDROutput = value; } /// public void OnBeforeSerialize() { } /// public void OnAfterDeserialize() { if (version <= 1) { m_RequiresDepthTextureOption = (m_RequiresDepthTexture) ? CameraOverrideOption.On : CameraOverrideOption.Off; m_RequiresOpaqueTextureOption = (m_RequiresColorTexture) ? CameraOverrideOption.On : CameraOverrideOption.Off; m_Version = 2; } } /// public void OnValidate() { if (m_CameraType == CameraRenderType.Overlay && m_Camera != null) { m_Camera.clearFlags = CameraClearFlags.Nothing; } } /// public void OnDrawGizmos() { string gizmoName = ""; Color tint = Color.white; if (m_CameraType == CameraRenderType.Base) { gizmoName = k_BaseCameraGizmoPath; } else if (m_CameraType == CameraRenderType.Overlay) { gizmoName = k_OverlayCameraGizmoPath; } #if UNITY_2019_2_OR_NEWER #if UNITY_EDITOR if (Selection.activeObject == gameObject) { // Get the preferences selection color tint = SceneView.selectedOutlineColor; } #endif if (!string.IsNullOrEmpty(gizmoName)) { Gizmos.DrawIcon(transform.position, gizmoName, true, tint); } if (renderPostProcessing) { Gizmos.DrawIcon(transform.position, k_PostProcessingGizmoPath, true, tint); } #else if (renderPostProcessing) { Gizmos.DrawIcon(transform.position, k_PostProcessingGizmoPath); } Gizmos.DrawIcon(transform.position, gizmoName); #endif } /// public void OnDestroy() { //You cannot call scriptableRenderer here. If you where not in URP, this will actually create the renderer. //This can occurs in cross pipeline but also on Dedicated Server where the gfx device do not run. (UUM-75237) //Use GetRawRenderer() instead. m_Camera.DestroyVolumeStack(this); if (camera.cameraType != CameraType.SceneView) GetRawRenderer()?.ReleaseRenderTargets(); m_History?.Dispose(); m_History = null; } ScriptableRenderer GetRawRenderer() { if (UniversalRenderPipeline.asset is null) return null; ReadOnlySpan renderers = UniversalRenderPipeline.asset.renderers; if (renderers == null || renderers.IsEmpty) return null; if (m_RendererIndex >= renderers.Length || m_RendererIndex < 0) return null; return renderers[m_RendererIndex]; } } }