using UnityEngine; using System; namespace Unity.Cinemachine { /// /// Describes the FOV and clip planes for a camera. This generally mirrors the Unity Camera's /// lens settings, and will be used to drive the Unity camera when the vcam is active. /// [Serializable] public struct LensSettings { /// /// This is the camera vertical field of view in degrees. Display will be in vertical degress, unless the /// associated camera has its FOV axis setting set to Horizontal, in which case display will /// be in horizontal degress. Internally, it is always vertical degrees. /// For cinematic people, a 50mm lens on a super-35mm sensor would equal a 19.6 degree FOV. /// [Tooltip("This setting controls the Field of View or Local Length of the lens, depending " + "on whether the camera mode is physical or nonphysical. Field of View can be either horizontal " + "or vertical, depending on the setting in the Camera component.")] public float FieldOfView; /// /// When using an orthographic camera, this defines the half-height, in world /// co-ordinates, of the camera view. /// [Tooltip("When using an orthographic camera, this defines the half-height, in world " + "coordinates, of the camera view.")] public float OrthographicSize; /// /// The near clip plane for this LensSettings /// [Tooltip("This defines the near region in the renderable range of the camera frustum. " + "Raising this value will stop the game from drawing things near the camera, which " + "can sometimes come in handy. Larger values will also increase your shadow resolution.")] public float NearClipPlane; /// /// The far clip plane for this LensSettings /// [Tooltip("This defines the far region of the renderable range of the camera frustum. Typically " + "you want to set this value as low as possible without cutting off desired distant objects")] public float FarClipPlane; /// /// The dutch (tilt) to be applied to the camera. In degrees /// [Tooltip("Camera Z roll, or tilt, in degrees.")] public float Dutch; /// /// This enum controls how the Camera settings are driven. Some settings /// can be pulled from the main camera, or pushed to it, depending on these values. /// public enum OverrideModes { /// Perspective/Ortho, IsPhysical /// will not be changed in Unity Camera. This is the default setting. None = 0, /// Orthographic projection mode will be pushed to the Unity Camera Orthographic, /// Perspective projection mode will be pushed to the Unity Camera Perspective, /// A physically-modeled Perspective projection type will be pushed /// to the Unity Camera Physical } /// /// Allows you to select a different camera mode to apply to the Camera component /// when Cinemachine activates this Virtual Camera. /// [Tooltip("Allows you to select a different camera mode to apply to the Camera component " + "when Cinemachine activates this Virtual Camera.")] public OverrideModes ModeOverride; /// These are settings that are used only if IsPhysicalCamera is true. [Serializable] [Tooltip("These are settings that are used only if IsPhysicalCamera is true")] public struct PhysicalSettings { /// How the image is fitted to the sensor if the aspect ratios differ [Tooltip("How the image is fitted to the sensor if the aspect ratios differ")] public Camera.GateFitMode GateFit; /// This is the actual size of the image sensor (in mm). [SensorSizeProperty] [Tooltip("This is the actual size of the image sensor (in mm)")] public Vector2 SensorSize; /// Position of the gate relative to the film back [Tooltip("Position of the gate relative to the film back")] public Vector2 LensShift; /// Distance from the camera lens at which focus is sharpest. /// The Depth of Field Volume override uses this value if you set FocusDistanceMode to Camera [Tooltip("Distance from the camera lens at which focus is sharpest. The Depth of Field Volume " + "override uses this value if you set FocusDistanceMode to Camera")] public float FocusDistance; /// The sensor sensitivity (ISO) [Tooltip("The sensor sensitivity (ISO)")] public int Iso; /// The exposure time, in seconds [Tooltip("The exposure time, in seconds")] public float ShutterSpeed; /// The aperture number, in f-stop [Tooltip("The aperture number, in f-stop")] [Range(Camera.kMinAperture, Camera.kMaxAperture)] public float Aperture; /// The number of diaphragm blades [Tooltip("The number of diaphragm blades")] [Range(Camera.kMinBladeCount, Camera.kMaxBladeCount)] public int BladeCount; /// Maps an aperture range to blade curvature [Tooltip("Maps an aperture range to blade curvature")] [MinMaxRangeSlider(Camera.kMinAperture, Camera.kMaxAperture)] public Vector2 Curvature; /// The strength of the "cat-eye" effect on bokeh (optical vignetting) [Tooltip("The strength of the \"cat-eye\" effect on bokeh (optical vignetting)")] [Range(0, 1)] public float BarrelClipping; /// Stretches the sensor to simulate an anamorphic look. Positive values distort /// the camera vertically, negative values distore the camera horizontally [Tooltip("Stretches the sensor to simulate an anamorphic look. Positive values distort the " + "camera vertically, negative values distort the camera horizontally")] [Range(-1, 1)] public float Anamorphism; } /// /// The physical settings of the lens, valid only when camera is set to Physical mode. /// public PhysicalSettings PhysicalProperties; bool m_OrthoFromCamera; bool m_PhysicalFromCamera; float m_AspectFromCamera; #if UNITY_EDITOR // Needed for knowing how to display FOV (horizontal or vertical) // This should really be a global Unity setting, but for now there is no better way than this! Camera m_SourceCamera; internal bool UseHorizontalFOV { get { if (m_SourceCamera == null) return false; var p = new UnityEditor.SerializedObject(m_SourceCamera).FindProperty("m_FOVAxisMode"); return p != null && p.intValue == (int)Camera.FieldOfViewAxis.Horizontal; } } #endif /// /// This is set every frame by the virtual camera, based on the value found in the /// currently associated Unity camera. /// Do not set this property. Instead, use the ModeOverride field to set orthographic mode. /// public bool Orthographic => ModeOverride == OverrideModes.Orthographic || (ModeOverride == OverrideModes.None && m_OrthoFromCamera); /// /// This property will be true if the camera mode is set, either directly or /// indirectly, to Physical Camera /// Do not set this property. Instead, use the ModeOverride field to set physical mode. /// public bool IsPhysicalCamera => ModeOverride == OverrideModes.Physical || (ModeOverride == OverrideModes.None && m_PhysicalFromCamera); /// /// For physical cameras, this is the Sensor aspect. /// For nonphysical cameras, this is the screen aspect pulled from the camera, if any. /// public float Aspect => IsPhysicalCamera ? PhysicalProperties.SensorSize.x / PhysicalProperties.SensorSize.y : m_AspectFromCamera; /// Default Lens Settings public static LensSettings Default => new () { FieldOfView = 40f, OrthographicSize = 10f, NearClipPlane = 0.1f, FarClipPlane = 5000f, Dutch = 0, ModeOverride = OverrideModes.None, PhysicalProperties = new () { SensorSize = new Vector2(21.946f, 16.002f), GateFit = Camera.GateFitMode.Horizontal, FocusDistance = 10, LensShift = Vector2.zero, Iso = 200, ShutterSpeed = 0.005f, Aperture = 16, BladeCount = 5, Curvature = new Vector2(2, 11), BarrelClipping = 0.25f, Anamorphism = 0, }, m_AspectFromCamera = 1 }; /// /// Creates a new LensSettings, copying the values from the /// supplied Camera /// /// The Camera from which the FoV, near /// and far clip planes will be copied. /// The LensSettings as extracted from the supplied Camera public static LensSettings FromCamera(Camera fromCamera) { LensSettings lens = Default; if (fromCamera != null) { lens.PullInheritedPropertiesFromCamera(fromCamera); lens.FieldOfView = fromCamera.fieldOfView; lens.OrthographicSize = fromCamera.orthographicSize; lens.NearClipPlane = fromCamera.nearClipPlane; lens.FarClipPlane = fromCamera.farClipPlane; if (lens.IsPhysicalCamera) { lens.FieldOfView = Camera.FocalLengthToFieldOfView( Mathf.Max(0.01f, fromCamera.focalLength), fromCamera.sensorSize.y); lens.PhysicalProperties.SensorSize = fromCamera.sensorSize; lens.PhysicalProperties.LensShift = fromCamera.lensShift; lens.PhysicalProperties.GateFit = fromCamera.gateFit; lens.PhysicalProperties.FocusDistance = fromCamera.focusDistance; lens.PhysicalProperties.Iso = fromCamera.iso; lens.PhysicalProperties.ShutterSpeed = fromCamera.shutterSpeed; lens.PhysicalProperties.Aperture = fromCamera.aperture; lens.PhysicalProperties.BladeCount = fromCamera.bladeCount; lens.PhysicalProperties.Curvature = fromCamera.curvature; lens.PhysicalProperties.BarrelClipping = fromCamera.barrelClipping; lens.PhysicalProperties.Anamorphism = fromCamera.anamorphism; } } return lens; } /// /// In the event that there is no camera mode override, camera mode is driven /// by the Camera's state. /// /// The Camera from which we will take the info public void PullInheritedPropertiesFromCamera(Camera camera) { if (ModeOverride == OverrideModes.None) { m_OrthoFromCamera = camera.orthographic; m_PhysicalFromCamera = camera.usePhysicalProperties; } m_AspectFromCamera = camera.aspect; #if UNITY_EDITOR m_SourceCamera = camera; // hack because of missing Unity API to get horizontal or vertical fov mode #endif } /// /// Copy the properties controlled by camera mode. If ModeOverride is None, then /// some internal state information must be transferred. /// /// The LensSettings from which we will take the info public void CopyCameraMode(ref LensSettings fromLens) { ModeOverride = fromLens.ModeOverride; if (ModeOverride == OverrideModes.None) { m_OrthoFromCamera = fromLens.Orthographic; m_PhysicalFromCamera = fromLens.IsPhysicalCamera; } m_AspectFromCamera = fromLens.m_AspectFromCamera; } /// /// Linearly blends the fields of two LensSettings and returns the result /// /// The LensSettings to blend from /// The LensSettings to blend to /// The interpolation value. Internally clamped to the range [0,1] /// Interpolated settings public static LensSettings Lerp(LensSettings lensA, LensSettings lensB, float t) { t = Mathf.Clamp01(t); // non-lerpable settings taken care of here if (t < 0.5f) { var blendedLens = lensA; blendedLens.Lerp(lensB, t); return blendedLens; } else { var blendedLens = lensB; blendedLens.Lerp(lensA, 1 - t); return blendedLens; } } /// /// Lerp the interpolatable values. Values that can't be interpolated remain intact. /// /// The lens containing the values to combine with this one /// The weight of LensB's values. public void Lerp(in LensSettings lensB, float t) { FarClipPlane = Mathf.Lerp(FarClipPlane, lensB.FarClipPlane, t); NearClipPlane = Mathf.Lerp(NearClipPlane, lensB.NearClipPlane, t); FieldOfView = Mathf.Lerp(FieldOfView, lensB.FieldOfView, t); OrthographicSize = Mathf.Lerp(OrthographicSize, lensB.OrthographicSize, t); Dutch = Mathf.Lerp(Dutch, lensB.Dutch, t); PhysicalProperties.SensorSize = Vector2.Lerp(PhysicalProperties.SensorSize, lensB.PhysicalProperties.SensorSize, t); PhysicalProperties.LensShift = Vector2.Lerp(PhysicalProperties.LensShift, lensB.PhysicalProperties.LensShift, t); PhysicalProperties.FocusDistance = Mathf.Lerp(PhysicalProperties.FocusDistance, lensB.PhysicalProperties.FocusDistance, t); PhysicalProperties.Iso = Mathf.RoundToInt(Mathf.Lerp((float)PhysicalProperties.Iso, (float)lensB.PhysicalProperties.Iso, t)); PhysicalProperties.ShutterSpeed = Mathf.Lerp(PhysicalProperties.ShutterSpeed, lensB.PhysicalProperties.ShutterSpeed, t); PhysicalProperties.Aperture = Mathf.Lerp(PhysicalProperties.Aperture, lensB.PhysicalProperties.Aperture, t); PhysicalProperties.BladeCount = Mathf.RoundToInt(Mathf.Lerp(PhysicalProperties.BladeCount, lensB.PhysicalProperties.BladeCount, t));; PhysicalProperties.Curvature = Vector2.Lerp(PhysicalProperties.Curvature, lensB.PhysicalProperties.Curvature, t); PhysicalProperties.BarrelClipping = Mathf.Lerp(PhysicalProperties.BarrelClipping, lensB.PhysicalProperties.BarrelClipping, t); PhysicalProperties.Anamorphism = Mathf.Lerp(PhysicalProperties.Anamorphism, lensB.PhysicalProperties.Anamorphism, t); } /// Make sure lens settings are sane. Call this from OnValidate(). public void Validate() { FarClipPlane = Mathf.Max(FarClipPlane, NearClipPlane + 0.001f); FieldOfView = Mathf.Clamp(FieldOfView, 0.01f, 179f); PhysicalProperties.SensorSize.x = Mathf.Max(PhysicalProperties.SensorSize.x, 0.1f); PhysicalProperties.SensorSize.y = Mathf.Max(PhysicalProperties.SensorSize.y, 0.1f); PhysicalProperties.FocusDistance = Mathf.Max(PhysicalProperties.FocusDistance, 0.01f); if (m_AspectFromCamera == 0) m_AspectFromCamera = 1; PhysicalProperties.ShutterSpeed = Mathf.Max(0, PhysicalProperties.ShutterSpeed); PhysicalProperties.Aperture = Mathf.Clamp(PhysicalProperties.Aperture, Camera.kMinAperture, Camera.kMaxAperture); PhysicalProperties.BladeCount = Mathf.Clamp(PhysicalProperties.BladeCount, Camera.kMinBladeCount, Camera.kMaxBladeCount); PhysicalProperties.BarrelClipping = Mathf.Clamp01(PhysicalProperties.BarrelClipping); PhysicalProperties.Curvature.x = Mathf.Clamp(PhysicalProperties.Curvature.x, Camera.kMinAperture, Camera.kMaxAperture); PhysicalProperties.Curvature.y = Mathf.Clamp(PhysicalProperties.Curvature.y, PhysicalProperties.Curvature.x, Camera.kMaxAperture); PhysicalProperties.Anamorphism = Mathf.Clamp(PhysicalProperties.Anamorphism, -1, 1); } /// /// Compare two lens settings objects for approximate equality /// /// First LensSettings /// Second Lens Settings /// True if the two lenses are approximately equal public static bool AreEqual(ref LensSettings a, ref LensSettings b) { return Mathf.Approximately(a.NearClipPlane, b.NearClipPlane) && Mathf.Approximately(a.FarClipPlane, b.FarClipPlane) && Mathf.Approximately(a.OrthographicSize, b.OrthographicSize) && Mathf.Approximately(a.FieldOfView, b.FieldOfView) && Mathf.Approximately(a.Dutch, b.Dutch) && Mathf.Approximately(a.PhysicalProperties.LensShift.x, b.PhysicalProperties.LensShift.x) && Mathf.Approximately(a.PhysicalProperties.LensShift.y, b.PhysicalProperties.LensShift.y) && Mathf.Approximately(a.PhysicalProperties.SensorSize.x, b.PhysicalProperties.SensorSize.x) && Mathf.Approximately(a.PhysicalProperties.SensorSize.y, b.PhysicalProperties.SensorSize.y) && a.PhysicalProperties.GateFit == b.PhysicalProperties.GateFit && Mathf.Approximately(a.PhysicalProperties.FocusDistance, b.PhysicalProperties.FocusDistance) && Mathf.Approximately(a.PhysicalProperties.Iso, b.PhysicalProperties.Iso) && Mathf.Approximately(a.PhysicalProperties.ShutterSpeed, b.PhysicalProperties.ShutterSpeed) && Mathf.Approximately(a.PhysicalProperties.Aperture, b.PhysicalProperties.Aperture) && a.PhysicalProperties.BladeCount == b.PhysicalProperties.BladeCount && Mathf.Approximately(a.PhysicalProperties.Curvature.x, b.PhysicalProperties.Curvature.x) && Mathf.Approximately(a.PhysicalProperties.Curvature.y, b.PhysicalProperties.Curvature.y) && Mathf.Approximately(a.PhysicalProperties.BarrelClipping, b.PhysicalProperties.BarrelClipping) && Mathf.Approximately(a.PhysicalProperties.Anamorphism, b.PhysicalProperties.Anamorphism) ; } } }