Unity3D中摄像机的FOV和Camera.ScreenToWorldPoint方法的关系
Table of Contents
请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com
在Unity3D编程中经常会碰到以下的问题:
比如一开始的FOV为60度,后来设置为22度,可以通过怎样的公式,使得使用22度FOV对应生成的3D位置点,通过这个公式,可以转换到其位置点和使用60度生成的一样呢
当从一个FOV为60度的视角切换到22度的视角时,使用Camera.ScreenToWorldPoint
函数生成的3D位置点会发生变化,因为FOV直接影响视锥体的宽度和高度。要解决这个问题,需要通过调整视锥体的缩放因子,来抵消FOV变化带来的位置偏移。
问题分析
当FOV变化时,屏幕坐标对应的世界坐标会随之缩放。假设有一个屏幕坐标screenPos
,在FOV为60度时通过Camera.ScreenToWorldPoint
生成一个3D位置点P60
。当FOV变为22度时,假设生成的3D位置点为P22
。我们需要找到一个转换公式,使得P22
转换回P60
。
FOV与世界坐标的关系
视锥体的高度(在深度为z处)与FOV通过以下公式相关:
$$ Heightz=2×z×tan(FOV2)\text{Height}_z = 2 \times z \times \tan\left(\frac{FOV}{2}\right)Heightz=2×z×tan(2FOV) $$
其中:
- Height_z :在深度为z处的视锥体高度。
- FOV:垂直视野角度。
- z:深度(从摄像机到目标点的距离)。
视锥体的宽度和高度成比例地变化,具体公式为:
$$ Widthz=Heightz×Aspect Ratio\text{Width}_z = \text{Height}_z \times \text{Aspect Ratio}Widthz=Heightz×Aspect Ratio $$
对于两个不同的FOV(如60度和22度),可以计算出它们对应的视锥体高度比值。为了让FOV为22度时生成的世界坐标与FOV为60度时生成的一致,需要应用这个比值进行缩放。
缩放因子的推导
在FOV为60度时的视锥体高度:
$$ \mathrm{Heigth}_{60}=2\times \mathrm{z}\times \tan \left( \frac{60^o}{2} \right) $$
在FOV为22度时的视锥体高度:
$$ \mathrm{Heigth}_{22}=2\times \mathrm{z}\times \tan \left( \frac{22^o}{2} \right) $$
缩放因子: 需要将FOV为22度时的世界坐标缩放到FOV为60度的视锥体高度,因此缩放因子是这两个视锥体高度的比值:
$$ ScaleFactor=\frac{Height_{60}}{Height_{22}}=\frac{\tan \left( 30^{o} \right)}{\tan \left( 11^{o} \right)} $$
可以计算出这个缩放因子:
$$ \mathrm{ScaleFactor}=\frac{0.5774}{0.1944}\approx 2.97 $$
公式应用
在FOV为22度时生成的3D世界坐标P22
可以通过如下公式转换为FOV为60度时的3D世界坐标P60
:
$$ \mathrm{P_{60}}=\mathrm{P_{22}}\times \mathrm{ScaleFactor} $$
其中ScaleFactor为大约2.97。
实际代码实现
下面的代码展示了如何将FOV为22度生成的3D位置点,转换为FOV为60度的等效位置点:
using UnityEngine;
public class FOVConversion : MonoBehaviour
{
public Camera mainCamera;
public float depth = 10f; // 距离摄像机的深度
private float scaleFactor;
void Start()
{
// 计算缩放因子
float fov60 = 60f;
float fov22 = 22f;
scaleFactor = Mathf.Tan(fov60 * Mathf.Deg2Rad / 2) /
Mathf.Tan(fov22 * Mathf.Deg2Rad / 2);
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Vector3 mousePos = Input.mousePosition;
// 使用22度FOV生成世界坐标
Vector3 worldPos22 = ScreenToWorldWithFOV(mousePos, 22f, depth);
// 将22度FOV生成的坐标转换为60度FOV等效坐标
Vector3 worldPos60 = worldPos22 * scaleFactor;
Debug.Log("World Position at FOV 22: " + worldPos22);
Debug.Log("Equivalent World Position at FOV 60: " + worldPos60);
}
}
Vector3 ScreenToWorldWithFOV(Vector3 screenPos, float fov, float z)
{
// 设置摄像机的FOV
mainCamera.fieldOfView = fov;
// 使用ScreenToWorldPoint进行转换
Vector3 worldPos = mainCamera.ScreenToWorldPoint(
new Vector3(screenPos.x, screenPos.y, z));
return worldPos;
}
}
总结
- FOV的影响:较大的FOV会使视锥体的宽度和高度增加,导致相同的屏幕坐标映射到较远的世界坐标。较小的FOV则会使视锥体变窄,映射到较近的世界坐标。
- 缩放因子:通过计算两个FOV视锥体的高度比值,可以得到一个缩放因子,将小FOV生成的世界坐标转换为大FOV生成的等效世界坐标。