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)
;
}
}
}