Unity基础:Camera相机组件详解------透视投影与正交投影的区别

大家好,欢迎回到Unity教程系列。前面我们学习了如何创建GameObject、操作Transform、应用材质和纹理。但有一个根本性的问题我们还没有深入讨论:玩家到底看到什么画面?
答案就在Camera组件上。Camera(相机)是场景中玩家的"眼睛"------它决定了什么内容被渲染到屏幕上、从哪个角度观看、用什么样的投影方式展现。如果没有Camera,你在Scene视图中精心搭建的一切,玩家都看不到。
这篇文章我会把Camera组件的每一个重要属性都讲清楚,特别是很多人容易混淆的透视投影和正交投影。同时也会涉及多相机管理、渲染层级、以及一些实用的相机脚本。
一、Camera的核心概念
1.1 Camera是什么
💡 Camera是一个Unity组件,它定义了一个从特定位置和方向观察场景的"视口"。游戏运行时,Camera组件将3D场景"拍摄"下来,渲染到屏幕上。
每个Unity新场景默认都包含一个Main Camera(主相机),它的Tag被自动设置为"MainCamera"。这是Unity识别"哪个相机是主要相机"的标志。
1.2 Camera的核心属性速览
在Inspector中,Camera组件的属性面板包含以下关键区域:
- Clear Flags:决定背景如何绘制
- Background:背景颜色(仅在特定Clear Flags下生效)
- Culling Mask:选择渲染哪些Layer的物体
- Projection:投影模式(Perspective / Orthographic)
- Field of View / Size:视野角或正交大小
- Clipping Planes:近远裁剪面
- Viewport Rect:视口矩形(用于分屏等)
- Depth:相机深度(多相机渲染顺序)
- Target Texture:渲染目标纹理
- Allow HDR / Allow MSAA 等高级选项
每个属性都有它的用武之地,我们逐一深入了解。
二、投影模式------透视与正交
2.1 透视投影(Perspective Projection)
💡 透视投影模拟了人眼和真实相机的视觉效果:近大远小。距离相机越远的物体看起来越小,平行的线条在远处汇聚于一个消失点。
透视投影的核心参数是 Field of View(FOV,视野角),单位是度。FOV越大,相机"看到"的范围越广,但画面边缘的透视变形越强。FOV越小,视野越窄,但画面更"平",类似长焦镜头的效果。
FOV的典型值:
- 60°:标准视角,接近人眼的舒适视野范围。大多数第三人称游戏使用这个值
- 90°:广角视角,第一人称射击游戏的常见选择。视野更广但边缘会有明显变形
- 30-45°:窄视角,类似望远镜或狙击镜效果
- 120°+:超广角,鱼眼效果,一般不用于常规游戏
📝 在3D游戏中使用透视投影是默认选择。任何有"空间深度感"需求的游戏都应该使用透视投影。
2.2 正交投影(Orthographic Projection)
💡 正交投影没有透视效果:无论物体距离相机多远,它们的大小在屏幕上保持不变。平行的线条在投影后仍然平行。
正交投影的核心参数是 Size,它定义了相机垂直方向能看到多少单位(世界空间中的距离)。例如Size=5意味着相机能看到垂直方向上5个单位(屏幕上从中心往上5单位,往下5单位,总共10单位的高度)。
正交投影的典型应用场景:
- 2D游戏:绝大多数2D游戏使用正交投影
- 小地图/雷达图:小地图不需要透视效果
- 等角视角游戏(Isometric):比如模拟城市、农场经营等
- UI系统的World Space Canvas:当Canvas渲染模式为World Space时
- 建筑可视化:展示平面图和立面图
- 策略游戏:如即时战略游戏的俯视视角
2.3 透视与正交的对比
| 特性 | 透视投影 | 正交投影 |
|---|---|---|
| 视觉效果 | 近大远小,有深度感 | 等大等距,无透视变形 |
| 核心参数 | Field of View (FOV) | Size |
| 适用场景 | 3D游戏(第一/第三人称) | 2D游戏、小地图、策略游戏 |
| 平行线表现 | 向消失点汇聚 | 保持平行 |
| 性能 | 相同 | 相同(渲染开销无差别) |
2.4 代码中切换投影模式
csharp
using UnityEngine;
public class CameraProjectionSwitcher : MonoBehaviour
{
private Camera cam;
void Start()
{
cam = GetComponent<Camera>();
}
// 切换到透视模式
public void SwitchToPerspective(float fov = 60f)
{
cam.orthographic = false;
cam.fieldOfView = fov;
}
// 切换到正交模式
public void SwitchToOrthographic(float size = 5f)
{
cam.orthographic = true;
cam.orthographicSize = size;
}
// 按P键切换投影模式(演示用)
void Update()
{
if (Input.GetKeyDown(KeyCode.P))
{
cam.orthographic = !cam.orthographic;
if (cam.orthographic)
{
cam.orthographicSize = 5f;
Debug.Log("切换到正交投影");
}
else
{
cam.fieldOfView = 60f;
Debug.Log("切换到透视投影");
}
}
}
}
三、Clear Flags------背景清除方式
3.1 Clear Flags的四种模式
Clear Flags决定了在渲染当前帧之前,如何清除屏幕上已有的内容:
Skybox(天空盒) :默认选项。渲染之前用天空盒(Skybox)填充背景。天空盒是一个全景立方体贴图,模拟天空和环境。你可以在 Window > Rendering > Lighting 中设置场景的天空盒材质。
Solid Color(纯色):用单一颜色填充背景。颜色由Background属性指定。适合不需要天空的室内场景,或者2D游戏。
Depth Only(仅深度):不清除颜色缓冲区,只清除深度缓冲区。主要用于多相机叠加场景------后渲染的相机画面叠加在之前相机画面上。
Don't Clear(不清除):完全不清理任何缓冲区。这会导致当前帧叠加在上一帧之上,产生残影效果(通常不是你想要的结果,除非有特殊需求)。
3.2 Clear Flags的实用场景
多相机HUD叠加:用一个相机渲染3D场景(Skybox),另一个相机的Clear Flags设为Depth Only来渲染UI层------这样UI就会叠加在3D场景上。
画中画效果:小地图相机Clear Flags设为Depth Only,画面叠加在主画面上。
csharp
// 设置Clear Flags的代码
Camera cam = GetComponent<Camera>();
cam.clearFlags = CameraClearFlags.Skybox;
cam.clearFlags = CameraClearFlags.SolidColor;
cam.backgroundColor = Color.black;
cam.clearFlags = CameraClearFlags.Depth;
cam.clearFlags = CameraClearFlags.Nothing;
四、Culling Mask------渲染层级遮罩
4.1 Culling Mask的作用
💡 Culling Mask让你可以选择性地渲染场景中的物体。它基于GameObject的Layer来决定:只有属于特定Layer的物体才会被这个相机渲染。
这是一个极其有用的功能。通过它你可以:
- 让UI相机只渲染UI层的物体,不渲染场景中的3D模型
- 让武器相机只渲染武器层,避免武器穿透墙壁
- 让小地图相机排除特效层,保持地图清爽
- 在一个场景中制作多个视图(如后视镜),每个视图显示不同的内容
4.2 设置Culling Mask
在Inspector中,点击Camera组件的Culling Mask下拉框,你会看到所有Layer的列表。勾选的Layer会被渲染,未勾选的会被忽略。
代码中设置:
csharp
// 只渲染Default层
cam.cullingMask = 1 << LayerMask.NameToLayer("Default");
// 渲染Default和UI层
cam.cullingMask = (1 << LayerMask.NameToLayer("Default"))
| (1 << LayerMask.NameToLayer("UI"));
// 排除UI层(渲染除UI之外的所有层)
cam.cullingMask = ~(1 << LayerMask.NameToLayer("UI"));
// 渲染所有层
cam.cullingMask = -1;
4.3 多相机场景的典型设置
场景结构:
├── Main Camera
│ ├── Depth: 0
│ ├── Culling Mask: Everything except "UI" and "Weapon"
│ └── Clear Flags: Skybox
├── Weapon Camera
│ ├── Depth: 1
│ ├── Culling Mask: Only "Weapon"
│ └── Clear Flags: Depth Only
└── UI Camera
├── Depth: 2
├── Culling Mask: Only "UI"
└── Clear Flags: Depth Only
这样设置后,武器永远渲染在所有场景物体的前面(不会穿墙),UI永远在最上层。
五、Clipping Planes------裁剪面
5.1 Near和Far的作用
Camera有两个裁剪面:
- Near Clipping Plane(近裁剪面):距离相机多近的物体开始被裁剪。比Near更近的物体不渲染。
- Far Clipping Plane(远裁剪面):距离相机多远的物体被裁剪。比Far更远的物体不渲染。
这两个平面之间的区域就是渲染范围(Frustum,视锥体)。
5.2 设置裁剪面的注意事项
⚠️ Near不应设置得太小(建议不小于0.01)。Near太小时,深度缓冲区(Z-Buffer)的精度会下降,远处的物体可能出现"Z-Fighting"闪烁现象。
Far则不应设置得过大。Far太大会使深度缓冲区的精度分散在太大的范围上。对于大多数游戏场景,Far设为500-1000就足够了。大型开放世界可以考虑动态调整Far值。
csharp
cam.nearClipPlane = 0.1f;
cam.farClipPlane = 1000f;
六、Viewport Rect------视口矩形
6.1 视口矩形的作用
Viewport Rect定义了相机画面在屏幕上的区域。它的值使用归一化坐标(0到1):
- X和Y:视口左下角的位置(0=左/下边缘,1=右/上边缘)
- W和H:视口的宽度和高度(相对于全屏的比例)
6.2 分屏游戏
Viewport Rect最经典的应用是本地分屏多人游戏:
csharp
// 玩家1:屏幕左半部分(上半)
Camera player1Cam = player1Camera;
player1Cam.rect = new Rect(0, 0.5f, 1, 0.5f);
// 玩家2:屏幕左半部分(下半)
Camera player2Cam = player2Camera;
player2Cam.rect = new Rect(0, 0, 1, 0.5f);
或四人分屏:
csharp
// 四人分屏
player1Cam.rect = new Rect(0, 0.5f, 0.5f, 0.5f); // 左上
player2Cam.rect = new Rect(0.5f, 0.5f, 0.5f, 0.5f); // 右上
player3Cam.rect = new Rect(0, 0, 0.5f, 0.5f); // 左下
player4Cam.rect = new Rect(0.5f, 0, 0.5f, 0.5f); // 右下
6.3 小地图/后视镜
csharp
// 右下角小地图(占屏幕1/4)
Camera minimapCam = minimapCamera;
minimapCam.rect = new Rect(0.75f, 0.75f, 0.25f, 0.25f);
七、Depth和Target Texture
7.1 Camera Depth
当场景中有多个相机时,Depth值决定了它们的渲染顺序。Depth值越大的相机越后渲染,因此会显示在最上层。
csharp
mainCam.depth = 0; // 先渲染(底层)
uiCam.depth = 1; // 后渲染(叠加在上面)
7.2 Target Texture------渲染到纹理
默认情况下,相机把画面渲染到屏幕上。但你可以设置Target Texture,让相机渲染到一张纹理(RenderTexture)上。这个纹理可以被用在其他地方------比如UI中的RawImage、材质上的贴图等。
典型应用:
- 小地图系统(用一个俯视相机渲染到RenderTexture,在UI上显示)
- 监控摄像头画面
- 镜面反射效果
- 传送门效果
csharp
public RenderTexture renderTexture;
Camera minimapCam;
void Start()
{
minimapCam.targetTexture = renderTexture;
}
八、实用相机脚本
8.1 第三人称相机跟随
csharp
using UnityEngine;
public class ThirdPersonCamera : MonoBehaviour
{
public Transform target;
public Vector3 offset = new Vector3(0, 3, -5);
public float smoothSpeed = 5f;
public float rotationSpeed = 3f;
void LateUpdate()
{
if (target == null) return;
// 目标位置
Vector3 desiredPosition = target.position + target.TransformDirection(offset);
// 平滑移动
Vector3 smoothedPosition = Vector3.Lerp(
transform.position,
desiredPosition,
smoothSpeed * Time.deltaTime
);
transform.position = smoothedPosition;
// 看向目标
Quaternion targetRotation = Quaternion.LookRotation(target.position - transform.position);
transform.rotation = Quaternion.Slerp(
transform.rotation,
targetRotation,
rotationSpeed * Time.deltaTime
);
}
}
8.2 鼠标右键旋转视角
csharp
using UnityEngine;
public class MouseLookCamera : MonoBehaviour
{
public float mouseSensitivity = 2f;
public Transform playerBody;
private float xRotation = 0f;
void Start()
{
Cursor.lockState = CursorLockMode.Locked;
}
void Update()
{
float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity;
float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity;
// 垂直旋转(相机上下看)
xRotation -= mouseY;
xRotation = Mathf.Clamp(xRotation, -90f, 90f);
transform.localRotation = Quaternion.Euler(xRotation, 0f, 0f);
// 水平旋转(角色左右转)
playerBody.Rotate(Vector3.up * mouseX);
}
}
九、本篇总结
✅ 本文核心知识点回顾:
- Camera是场景中玩家的"眼睛",决定渲染内容和视觉效果
- 透视投影(Perspective)模拟真实视觉,有近大远小效果;正交投影(Orthographic)无透视变形
- Clear Flags控制背景清除方式:Skybox、Solid Color、Depth Only、Don't Clear
- Culling Mask通过Layer选择性渲染物体,是多相机系统的核心机制
- Viewport Rect在屏幕上划分区域,用于分屏和小地图
- Target Texture让相机渲染到纹理,用于各种特殊效果
- 多相机通过Depth控制渲染顺序,实现分层渲染
📝 下一篇文章将学习Light灯光组件------Directional Light、Point Light和Spotlight的使用。
动手任务 :
① 创建一个透视相机和一个正交相机,切换观察场景效果的区别
② 尝试用Culling Mask只渲染特定Layer的物体
③ 写一个简单的第三人称相机跟随脚本
④ 用多相机实现一个画面叠加效果
关于作者:拥有多年Unity开发经验的游戏开发者,专注Unity系统化教学与最佳实践分享。