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

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系统化教学与最佳实践分享。

相关推荐
在世修行2 小时前
从零打造 C# 工业视觉检测系统(四):海康威视 MVS SDK 入门与相机枚举实战
数码相机·c#·视觉检测
玖玥拾3 小时前
Unity3D RPG 入门项目(二)移动 Bug 修复、环绕相机、NPC 任务系统、UI 穿透处理
unity·游戏引擎
m0_749492225 小时前
2026平价运动相机选择指南:科美锐(Komery)PX3与另外两款相机的功能取舍拆解
数码相机
沐梯子5 小时前
AI视频工具大战升级!PixPix首发接入MiniMaxH3大战Seedance
数码相机
c#上位机5 小时前
线扫相机的帧触发
数码相机
Yasin Chen5 小时前
Unity UGUI RectMask2D 原理
unity·游戏引擎
hashiqimiya6 小时前
unity---unity编译手机apk人物角色移动
unity·游戏引擎
LONGZETECH7 小时前
传统实训 vs 仿真实训:从技术架构拆解新能源汽车教学的范式之变
c语言·3d·unity·架构·汽车
深视智能科技7 小时前
PIV高速成像系统如何同步?相机、激光与数据传输配置指南
数码相机