Unity的Camera类——视觉掌控与深度解析(上)

前言

摄像机在任何3D场景中都是至关重要的元素,尤其是在游戏和实时应用中。它定义了玩家和用户如何“看到”虚拟世界。Unity中的Camera类提供了一系列强大的工具,让开发者可以精细地控制渲染和视图。在本文中,我们将深入探索这个核心类的使用方式。


Camera的属性:

Camera.allCamerasCount : 返回场景中当前存在的摄像机数量。
Camera.allCameras: 返回场景中所有当前存在的摄像机的数组。
Camera.current: 返回当前渲染中的摄像机。
Camera.main: 返回标记为“MainCamera”的摄像机。

allCamerasCount

定义:
allCamerasCount 返回场景中当前存在的摄像机数量。
 
用途:
这个属性对于快速检查场景中摄像机的数量很有用,尤其是在某些摄像机可能在运行时被动态创建或销毁的场景。

示例:

int cameraCount = Camera.allCamerasCount;
Debug.Log("当前场景中的摄像机数量: " + cameraCount);

allCameras

定义:
allCameras 返回场景中所有当前存在的摄像机的数组。
 
用途:
这个属性在你需要对场景中的所有摄像机进行迭代或操作时非常有用。

注意:
数组的长度由 allCamerasCount 决定。
为了提高性能和减少垃圾回收,Unity不会为你创建一个新的摄像机数组。你需要先创建一个足够大的摄像机数组,然后将其传递给此属性,以填充所有的摄像机。

示例:

Camera[] cameras = new Camera[Camera.allCamerasCount];
Camera.allCameras.CopyTo(cameras, 0);

for(int i = 0; i < cameras.Length; i++)
{
    Debug.Log("摄像机名称: " + cameras[i].name);
}

这里首先创建了一个摄像机数组,大小为场景中摄像机的数量。然后使用 CopyTo 方法将所有的摄像机填充到我们的数组中。最后,我们迭代数组,打印出每个摄像机的名称。

在这个示例中,可以不用Camera.allCameras.CopyTo(cameras, 0),直接使用 cameras.Length,因为我们已经知道 cameras 数组的大小正好等于 Camera.allCamerasCount。

但在某些情境下,当你使用一个预先定义的、可能比实际摄像机数量大的数组时,知道实际填充了多少摄像机就很重要了。在这种情况下,使用返回的摄像机数量是有用的,以确保你只迭代了数组中实际存在的摄像机,避免出现null引用。


current

Camera.current 返回当前渲染中的摄像机。

 在大多数场景中,这是当前帧正在渲染的摄像机,但在执行某些特定的渲染函数(如 OnPreRender、OnPostRender)时,这是执行这些函数的摄像机。

用途: 在摄像机的特定渲染事件中,如 OnPreRender 和 OnPostRender,您可能需要知道哪个摄像机正在渲染,这时可以使用 Camera.current。

注意: 在大多数其他情况下,如果你尝试在常规的 Update() 或 Start() 函数中访问 Camera.current,它可能会返回 null,因为在这些函数中可能没有摄像机正在渲染。

示例:

void OnPostRender()
{
    // 当某个摄像机完成渲染后,输出其名称
    Debug.Log("刚刚完成渲染的摄像机: " + Camera.current.name);
}

总结,Camera.main 通常用于快速访问主摄像机,而 Camera.current 更多地用于特定的渲染上下文,以确定哪个摄像机正在进行渲染。


main

Camera.main 返回标记为“MainCamera”的摄像机。Unity编辑器中通常会有一个摄像机标记为"MainCamera",并且该摄像机的标签被设置为 "MainCamera"。

 
用途: 当您需要引用场景中的主摄像机时,Camera.main 是一个快捷方式。

注意事项: Camera.main 会查找带有 “MainCamera” 标签的摄像机,这会导致每次调用时都执行查找操作,这可能不是最高效的方式,特别是在频繁调用它的情况下。对于需要频繁访问主摄像机的情况,建议在初始化时缓存对该摄像机的引用。

示例:

// 获取主摄像机并设置其背景颜色为红色
Camera.main.backgroundColor = Color.red;

Camera的方法:

Camera.CalculateProjectionMatrixFromPhysicalProperties: 为物理基础的摄像机制作投影矩阵
Camera.FieldOfViewToFocalLength: 将摄像机的视场(FOV)转换为焦距。
Camera.FocalLengthToFieldOfView: 将摄像机的焦距转换为视场。
Camera.HorizontalToVerticalFieldOfView: 将水平视场转换为垂直视场。
Camera.VerticalToHorizontalFieldOfView: 将垂直视场转换为水平视场。
Camera.GetAllCameras: 返回场景中所有激活的摄像机。
Camera.SetupCurrent: 用于设置当前的主摄像机。

Camera.CalculateProjectionMatrixFromPhysicalProperties

是 Unity 中的一个相对较新的方法,它为物理基础的摄像机制作投影矩阵。这是在更高级的摄像机模拟和增强现实(AR)应用中非常有用的,其中物理属性(如传感器大小和焦距)对于精确的摄像机建模是必要的。

定义:

public static void CalculateProjectionMatrixFromPhysicalProperties(out Matrix4x4 output, float focalLength, Vector2 sensorSize, Vector2 lensShift, float nearClip, float farClip, GateFitParameters gateFitParameters = default(GateFitParameters));

参数说明:

  • output: 方法完成后,计算得到的 Matrix4x4 投影矩阵将被放入此参数中。
  • focalLength:摄像机的焦距。这定义了摄像机镜头和图像传感器之间的距离。
  • sensorSize:图像传感器的尺寸。例如,一个典型的35mm全画幅传感器的尺寸为36x24。
  • lensShift: 透镜偏移。这表示透镜从中心位置的偏移量。
  • nearClip 和 farClip: 这些定义了摄像机的前、后裁剪平面。
  • gateFitParameters:一个定义如何在摄像机的可见区域或显示分辨率中适应内容的结构。

示例:
假设您正在为一个 AR 应用程序创建一个摄像机模型,并且您有关于该摄像机的物理属性的数据。您可以使用这些数据和 CalculateProjectionMatrixFromPhysicalProperties 方法来生成一个更准确的投影矩阵,如下所示:

Camera cam = GetComponent<Camera>();

float focalLength = 50f; // 50mm
Vector2 sensorSize = new Vector2(36, 24); // 35mm全画幅传感器
Vector2 lensShift = new Vector2(0, 0); // 无偏移
float nearClip = 0.1f;
float farClip = 1000f;

Matrix4x4 projMatrix;
Camera.CalculateProjectionMatrixFromPhysicalProperties(out projMatrix, focalLength, sensorSize, lensShift, nearClip, farClip);
cam.projectionMatrix = projMatrix;

在这个示例中,摄像机的投影矩阵现在是基于其物理属性,为特定应用提供了更真实的摄像机模拟。


当你深入摄影和计算机图形学时,焦距和视场(Field of View,简称FOV)之间的关系变得至关重要。它们之间的关系定义了摄像机如何看待世界。在Unity中,FieldOfViewToFocalLengthFocalLengthToFieldOfView 这两个方法就是为了转换这两个值。

Camera.FieldOfViewToFocalLength

这个方法将摄像机的视场(FOV)转换为焦距。给定一个特定的视场和传感器的大小,这个方法可以告诉你需要什么焦距的镜头来获得那个特定的视场。

定义:

public static extern float FieldOfViewToFocalLength(float fieldOfView, float sensorSize);

参数说明:

  • fieldOfView: 摄像机的视场,通常在垂直方向上测量,以度为单位。
  • sensorSize: 图像传感器的尺寸。
  • 返回值是焦距,以毫米为单位。

示例:
如果我们知道我们想要一个45度的视场,我们可以找出需要的焦距:

float desiredFOV = 45f;
float sensorHeight = 24f; // 35mm全画幅传感器的尺寸为36x24,这里只使用高度24mm。
float focalLengthRequired = Camera.FieldOfViewToFocalLength(desiredFOV, sensorHeight);
Debug.Log("所需焦距:" + focalLengthRequired + "mm");

Camera.FocalLengthToFieldOfView

这个方法将摄像机的焦距转换为视场。如果你有一个特定的镜头和你想知道它在特定的传感器上会提供多大的视场,这个方法会很有用。

定义:

public static extern float FocalLengthToFieldOfView(float focalLength, float sensorSize);

参数说明:

  • focalLength: 镜头的焦距,以毫米为单位。
  • sensorSize: 图像传感器的尺寸,同上。
  • 返回值是摄像机的视场,以度为单位。

示例:
假设我们有一个50mm的镜头和一个35mm的全幅传感器,我们想知道这个组合的视场是多少:

float focalLength = 50f; // 50mm焦距
float sensorHeight = 24f; // 35mm的全幅传感器的高度为24mm
float fov = Camera.FocalLengthToFieldOfView(focalLength, sensorHeight);
Debug.Log("视场: " + fov + " 度");

我们通常使用垂直视场(Vertical Field of View)来描述摄像机的视场。但在某些情况下,基于摄像机的长宽比(Aspect Ratio),我们可能需要转换为水平视场(Horizontal Field of View)。这就是Camera.HorizontalToVerticalFieldOfView和Camera.VerticalToHorizontalFieldOfView这两个方法的应用场景。

Camera.HorizontalToVerticalFieldOfView

这个方法将水平视场转换为垂直视场,基于给定的摄像机的长宽比。

定义:

public static extern float HorizontalToVerticalFieldOfView(float horizontalFieldOfView, float aspectRatio);

参数说明:

  • horizontalFOV: 需要转换的水平视场,通常以度为单位。
  • aspectRatio:摄像机的长宽比,通常是摄像机视窗的宽度除以高度。

示例:

float hFOV = 60.0f;
float aspect = 16.0f/9.0f; //假设16:9的宽高比
float vFOV = Camera.HorizontalToVerticalFieldOfView(hFOV, aspect);
Debug.Log("垂直视场:" + vFOV);

Camera.VerticalToHorizontalFieldOfView

这个方法是上一个方法的逆操作,它将垂直视场转换为水平视场。

定义:

public static extern float VerticalToHorizontalFieldOfView(float verticalFieldOfView, float aspectRatio);

参数说明:

  • verticalFOV: 需要转换的垂直视场,通常以度为单位。
  • aspectRatio: 摄像机的长宽比。

示例:

float vFOV = 45.0f;
float aspect = 16.0f/9.0f;
float hFOV = Camera.VerticalToHorizontalFieldOfView(vFOV, aspect);
Debug.Log("水平视场: " + hFOV);

Camera.GetAllCameras

这个方法返回场景中所有激活的摄像机。它的使用场景包括但不限于处理多摄像机渲染,识别特定的摄像机,或对所有摄像机进行统一设置。

定义:

public static int GetAllCameras(Camera[] cameras)

cameras: 这是一个摄像机数组,用于存储场景中所有当前激活的摄像机。
返回值是实际填入数组的摄像机数。需要注意的是,提供的数组大小应该至少等于Camera.allCamerasCount,否则不会存储所有摄像机。

示例:

Camera[] allCameras = new Camera[Camera.allCamerasCount];
int filledCount = Camera.GetAllCameras(allCameras);
foreach(Camera cam in allCameras)
{
    Debug.Log(cam.name);
}

Camera.SetupCurrent

这个方法用于设置当前的主摄像机。当一个摄像机被设置为当前摄像机时,它将替代Camera.main。

定义:

 public static extern void SetupCurrent(Camera cur);

cur: 要设置为当前摄像机的摄像机实例。
这个方法对于动态切换主摄像机非常有用,例如在游戏中从一个摄像机视角切换到另一个摄像机视角。

示例:

public Camera alternateCamera; // 假设定义一个分配的Camera 
void SwitchToAlternateCamera()
{
    Camera.SetupCurrent(alternateCamera);
}

在这个示例中,SwitchToAlternateCamera 方法会将主摄像机从当前摄像机切换到alternateCamera。


Camera的事件:

Camera.onPostRender: 当摄像机完成渲染后会触发此事件。
Camera.onPreRender: 在摄像机开始渲染场景内容之前被触发的事件。
Camera.onPreCull: 它在摄像机开始裁剪阶段之前被触发的事件。

Camera.onPostRender

当摄像机完成渲染后会触发此事件。它允许开发者在摄像机渲染场景之后,但在显示最终渲染结果之前执行自定义的操作。

这在一些特定的应用场景中是非常有用的,比如当你想在摄像机渲染的结果上添加一些后处理效果或执行某些额外的绘图操作。

使用方式:

  1. 你可以给 onPostRender 添加一个自定义的事件处理方法。
  2. 这个事件处理方法必须拥有一个 Camera 类型的参数。

示例:
假设你想在每次摄像机渲染后在控制台打印一条消息:

void OnEnable()
{
    Camera.onPostRender += PostRenderAction;
}

void OnDisable()
{
    Camera.onPostRender -= PostRenderAction;
}

void PostRenderAction(Camera cam)
{
    Debug.Log("相机完成渲染: "+ cam.name);
}

在上述示例中:

  1. OnEnable 方法中,我们给 onPostRender 事件添加了一个名为 PostRenderAction 的方法。
  2. OnDisable 方法中,我们从 onPostRender 事件中移除了这个方法,确保不会引发不必要的回调。
  3. PostRenderAction方法是实际的事件处理方法,当摄像机完成渲染后会执行这个方法。这里,我们简单地打印了一个消息,表示哪个摄像机完成了渲染。

Camera.onPreRender

该事件在摄像机开始渲染场景内容之前被触发。它允许开发者在摄像机开始渲染前执行一些预先的操作或调整。例如,你可能想要修改摄像机的参数、改变物体的位置或状态、或执行其他任何需要在渲染前完成的任务。

此事件在某些特定的应用场景中非常有用,比如你想动态调整摄像机的参数或在渲染开始之前应用某些效果。

使用方式:

  1. 你可以给 onPreRender 添加一个自定义的事件处理方法。
  2. 这个事件处理方法必须拥有一个 Camera 类型的参数。

示例:
假设你在渲染开始前想要在控制台打印一条消息:

void OnEnable()
{
    Camera.onPreRender += PreRenderAction;
}

void OnDisable()
{
    Camera.onPreRender -= PreRenderAction;
}

void PreRenderAction(Camera cam)
{
    Debug.Log("即将开始用的相机渲染:"+ cam.name);
}

在上述示例中:

  1. 在 OnEnable 方法中,我们给 onPreRender 事件添加了一个名为 PreRenderAction 的方法。
  2. 在 OnDisable 方法中,我们从 onPreRender 事件中移除了这个方法,以防止不必要的回调或可能导致的错误。
  3. PreRenderAction 方法是实际的事件处理方法,当摄像机即将开始渲染时会执行这个方法。在这里,我们简单地打印了一个消息,表示哪个摄像机即将开始渲染。

Camera.onPreCull

它在摄像机开始裁剪阶段之前被触发。裁剪(culling)阶段是一个渲染流水线的部分,其中引擎决定哪些对象不在摄像机的视野内,因此可以被安全地忽略或“裁剪”掉,不参与后续的渲染过程。这是一个性能优化步骤,确保引擎仅渲染对玩家可见的对象。

通过使用 Camera.onPreCull 事件,开发者可以在裁剪过程开始之前进行一些操作或调整。

使用方式与用法可以参考上述两个事件,这里就不举例了,基本一致的。


在下一篇文章,我们会继续深度解析剩下的内容,包括Camera中枚举的使用等等。

希望你在这篇文章中对Unity的Camera类有了更深入的了解。如有任何疑问或需要进一步探讨,请在评论区留言,我会很乐意帮助!

你可能感兴趣的:(Unity3d,1024程序员节,unity,游戏引擎,unity3d)