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类有了更深入的了解。如有任何疑问或需要进一步探讨,请在评论区留言,我会很乐意帮助!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/117157.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Java】小计 TCP UDP的区别

面向连接 TCP面向连接&#xff0c;需要连接&#xff0c;而UDP不需要建立连接 可靠性 TCP协议通过确认应答、连接管理、流量控制、拥塞控制来确保可靠性传输&#xff1b;UDP不保证可靠性传输。 性能 TCP传输效率慢&#xff0c;需要较多的资源开销&#xff0c;UDP传输效率快&am…

某马机房预约系统 C++项目(二) 完结

8.4、查看机房 8.4.1、添加机房信息 根据案例&#xff0c;我们还是先在computerRoom.txt中直接添加点数据 //几机房 机器数量 1 20 2 50 3 1008.4.2、机房类创建 ​ 同样我们在头文件下新建一个computerRoom.h文件 添加如下代码&#xff1a; #pragma once #include<i…

pnp单目相机标定测距

参考&#xff1a;opencv 单目相机pnp测距&#xff08;Cpp&#xff09;-CSDN博客

【python】文件和异常

文件和异常 实际开发中常常会遇到对数据进行持久化操作的场景&#xff0c;而实现数据持久化最直接简单的方式就是将数据保存到文件中。说到“文件”这个词&#xff0c;可能需要先科普一下关于文件系统的知识&#xff0c;但是这里我们并不浪费笔墨介绍这个概念&#xff0c;请大…

凉鞋的 Godot 笔记 204. 语句

204. 语句 在上一篇&#xff0c;我们接触了三种常见的类型&#xff0c;如下所示&#xff1a; 这样我们算是对变量进行了一个入门了。 其实我们除了变量&#xff0c;我们还接触了一个叫做语句的概念。 我们可以看下代码: extends Node# Called when the node enters the sce…

IDEA配置HTML和Thymeleaf热部署开发

IDEA配置HTML和Thymeleaf热部署开发 1.项目配置2. IDEA配置3. 使用 需求&#xff1a;现在我们在开发不分离项目的时候&#xff08;SpringBootThmeleaf&#xff09;经常会改动了类或者静态html文件就需要重启一下服务器&#xff0c; 这样不仅时间开销很大&#xff0c;而且经常重…

PageHelper基础知识

使用场景 便用mybatis&#xff0c;可以用 pagehelper 分页 。 maven依赖 <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>4.1.6</version> </dependency>PageHelper配…

webpack配置css-loader让scss文件支持模块化引入

1. webpack部分: 重点: modules: true, // 为false引入就是空对象, 无法使用 const path require(path) const webpack require(webpack) const webpackCommonConf require(./webpack.common.js) const { smart } require(webpack-merge) const { srcPath, distPath } r…

@Value注解的原理

1.AutowiredAnnotationBeanPostProcessor是主要逻辑类&#xff0c;基本逻辑同Autowird 2.PropertySourcesPlaceholderConfigurer中会将StringValueResolver添加到beanFactory 3.外部配置文件properties会加载到ConfigurableEnvironment中&#xff0c;具体逻辑是通过Applicati…

vscode中如何将cmd设置为默认终端

vscode中如何将cmd设置为默认终端&#xff1f;下面本篇文章给大家介绍一下vscode中设置默认终端为cmdPowerShelWSL等的方法&#xff0c;希望对需要的朋友有所协助&#xff01; 一、快捷键&#xff08;CtrlShiftP打开命令面板&#xff0c;输入select选择“SelectDefaultProfil …

mdadm命令详解及实验过程

mdadm命令详解及实验过程 ⼀.概念 mdadm是multiple devices admin的简称&#xff0c;它是Linux下的⼀款标准的软件 RAID 管理⼯具&#xff0c;作者是Neil Brown ⼆.特点 mdadm能够诊断、监控和收集详细的阵列信息 mdadm是⼀个单独集成化的程序⽽不是⼀些分散程序的集合&#…

Jupyter使用技巧-环境篇

不同于其他IDE&#xff0c;有时会出现找不到文件路径&#xff0c;通常是因为当前工作目录&#xff08;working directory&#xff09;不同所导致的。Jupyter Notebook 会在启动时选择一个初始的工作目录&#xff0c;而这个目录可能与你运行 .py 文件时所在的目录不同。 import…

SpringMVC系列-5 消息转换器

背景 SpringMVC系列的第五篇介绍消息转换器&#xff0c;本文讨论的消息转换指代调用Controller接口后&#xff0c;对结果进行转换处理的过程。 内容包括介绍自定义消息转换器、SpringMVC常见的消息转换器、Spring消息转换器工作原理等三部分。 本文以 SpringMVC系列-2 HTTP请求…

PHP 预定义超全局变量 笔记/练习

预定义超全局数组变量 $_FILES 练习在最后 其他练习跟在每条笔记后 概述 预定义&#xff1a;预定义变量是 PHP 已定义&#xff0c;可以直接使用超全局&#xff1a;作用域是全局&#xff0c;可以在脚本的任何地方&#xff08;包括函数内部、外部&#xff09;都可以进行访问 常…

关于集群和分布式部署

EJB的RPC是同步调用可实现分布式计算&#xff0c;是SessionBean和EntityBean用的&#xff0c;而JMS是异步调用。RMI&#xff0c;和webservice也可以实现分布式计算。 举例说明&#xff0c;假设我们的系统有三个EJB组件&#xff1a;人事、财务、销售&#xff0c;都是开放远程接口…

EGF中多项式exp的组合意义

EGF中多项式exp的组合意义 EGF一般用来处理多重集的排列问题&#xff0c;在其上可以定义多项式的exp运算&#xff0c;在处理一类问题的时候有独特的作用 我们考虑将n个有标号的元素分为k个非空无序集合的方案数&#xff0c;记其EGF为 F k F_{k} Fk​,再考虑 f i f_i fi​表示…

【Segment Anything Model】八:修改SAM源码做分类任务

🍉 博主微信 cvxiayixiao 🍓 【Segment Anything Model】计算机视觉检测分割任务专栏。 链接 🍑 【公开数据集预处理】特别是医疗公开数据集的接受和预处理,提供代码讲解。链接 🍈 【opencv+图像处理】opencv代码库讲解,结合图像处理知识,不仅仅是调库。链接 文章目…

使用GoogleNet网络实现花朵分类

一.数据集准备 新建一个项目文件夹GoogleNet&#xff0c;并在里面建立data_set文件夹用来保存数据集&#xff0c;在data_set文件夹下创建新文件夹"flower_data"&#xff0c;点击链接下载花分类数据集https://storage.googleapis.com/download.tensorflow.org/exampl…

前端 CSS 经典:clip、clip-path

1. clip 1.1 clip: auto | inherit | rect auto&#xff1a;默认&#xff0c;不裁剪 inherit&#xff1a;继承父级 clip 属性 rect&#xff1a;规则四边形裁剪 1.2 clip: rect(top, right, bottom, left) 注意&#xff1a; 1.裁剪只对 fixed 和 absolute 的元素有效。 2.top&…

HIVE-17824,删除hdfs分区信息,清理metastore元数据

当手动删除HDFS 分区数据时,但是并没有清理 Hive 中的分区元数据,删除操作无法自动更新hive分区表元数据。也就是从hdfs中删除大量分区数据,并没有执行如下命令: alter table drop partition commad 从hive 3.0.0开始可以使用MSCK的方法发现新分区或删除丢失的分区; MSCK [REPA…