Unity学习之深度理解摄像机

一、摄像机参数详解

1. Layer Mask(层遮罩)

        通过分层将后期效果等限制在某一层处理。每个相机的 Layer Mask 包含的物体全部渲染完成后,才会进入下一个相机的渲染流程。若一个物体既在 A 相机遮罩下又在 B 相机遮罩下,会被重复渲染两次。

什么是Layer (层)

        Layer 是 Unity 中用于对游戏对象进行逻辑分组的机制,每个对象可以被分配到一个特定的层(共 32 层,默认提供 8 层,用户可自定义扩展到 32 层)。

        用途:控制摄像机渲染、物理碰撞检测、射线检测等。 

什么是 Layer Mask(层遮罩)?

        Layer Mask 是一个位掩码(Bitmask),用于选择摄像机应该渲染哪些层的对象。

        每个位对应一个层:例如,第 0 位对应 Layer 0,第 1 位对应 Layer 1,依此类推。

位掩码逻辑

        启用某层:将对应位设为 1,表示摄像机渲染该层的对象。

        禁用某层:将对应位设为 0,表示摄像机忽略该层的对象。

        示例

        若 Layer Mask 值为 7(二进制 00000111),则启用 Layer 0、1、2,禁用其他层。

        若 Layer Mask 值为 1024(二进制 00000000000000000000010000000000),则仅启用 Layer 10。

在 Inspector 中设置

        在摄像机的 Inspector 面板中,找到 Culling Mask 属性:

        下拉菜单:快速选择预设的层组合(如 EverythingNothing)。

        复选框列表:勾选 / 取消勾选具体的层。

代码中设置Layer Mask

using UnityEngine;

public class CameraLayerController : MonoBehaviour
{
    public Camera targetCamera;
    public LayerMask layerMask;

    void Start()
    {
        // 方法1:直接赋值
        targetCamera.cullingMask = layerMask;

        // 方法2:通过层名称设置(推荐,更直观)
        targetCamera.cullingMask = LayerMask.GetMask("Default", "UI");

        // 方法3:通过位运算设置
        // 启用 Layer 0 和 Layer 8
        targetCamera.cullingMask = (1 << 0) | (1 << 8);
    }
}

动态修改Layer Mask

// 启用 Layer 3
targetCamera.cullingMask |= (1 << 3);

// 禁用 Layer 5
targetCamera.cullingMask &= ~(1 << 5);

// 切换 Layer 7 的状态(开/关)
targetCamera.cullingMask ^= (1 << 7);

// 检查是否启用了 Layer 6
bool isLayer6Enabled = (targetCamera.cullingMask & (1 << 6)) != 0;

2. Depth(深度)

        决定相机渲染顺序,间接决定游戏对象渲染顺序。数值大的相机后渲染,会覆盖数值小的相机的部分内容。

什么是 Depth?

        Depth 是一个浮点数,表示摄像机的渲染优先级。数值越大,摄像机越晚渲染,其画面会覆盖在 Depth 较小的摄像机之上。

        渲染顺序:Unity 会按照摄像机的 Depth 值 从小到大依次渲染,即 Depth 为 0 的摄像机先渲染,Depth 为 1 的摄像机后渲染,后者的画面会叠加在前者之上。

与渲染层级的关系

        低 Depth 摄像机:先渲染,画面会被高 Depth 摄像机的内容遮挡。

        高 Depth 摄像机:后渲染,画面会覆盖在低 Depth 摄像机的内容之上。

代码设置depth:

using UnityEngine;

public class CameraDepthController : MonoBehaviour
{
    public Camera mainCamera;
    public Camera uiCamera;

    void Start()
    {
        // 设置主摄像机 Depth 为 0(默认值)
        mainCamera.depth = 0;
        
        // 设置 UI 摄像机 Depth 为 1,确保在主摄像机之后渲染
        uiCamera.depth = 1;
    }

    // 动态修改 Depth(如切换场景时调整层级)
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.U))
        {
            uiCamera.depth = 2; // 临时提高 UI 摄像机的渲染优先级
        }
    }
}

3. Clear Flag(清除标志)

        决定相机渲染前对缓冲区的处理方式。常见的有 “Depth Only”,只清除深度,常用于 UI 渲染,将 UI 相机深度设为大于场景相机深度,可使 UI 显示在场景前方;“Don't Clear” 通常和自定义 Shader 一起使用,不清除之前缓冲区对应场景中的所有物体,并将之前场景与当前场景合并继续渲染。

什么是 Clear Flag?

        Clear Flag 是摄像机的一个属性,用于控制摄像机渲染时如何处理 颜色缓冲区(Color Buffer) 和 深度缓冲区(Depth Buffer)

        缓冲区作用

        颜色缓冲区:存储屏幕上每个像素的颜色值。

        深度缓冲区:存储每个像素的深度信息(Z 值),用于确定像素的前后遮挡关系。

为什么需要 Clear Flag?

        在多摄像机系统中,后渲染的摄像机会覆盖先渲染的摄像机内容。

        通过设置 Clear Flag,可以控制:是否保留之前摄像机的渲染结果。是否重置深度信息,影响后续摄像机的深度测试。

Clear Flag 的五种模式

Unity 提供了五种 Clear Flag 模式,每种模式对颜色缓冲区和深度缓冲区的处理方式不同:

Skybox(天空盒)

        效果

        颜色缓冲区:用当前场景的天空盒填充。

        深度缓冲区:完全清除(设置为最大值,即所有像素的深度值为 1)。

        适用场景

        主摄像机渲染游戏场景的背景。

        当需要显示天空盒作为背景时。

Solid Color(纯色)

        效果

        颜色缓冲区:用摄像机的 Background 颜色填充。

        深度缓冲区:完全清除(深度值为 1)。

        适用场景

        简单背景(如纯色加载界面)。

        替代天空盒,使用纯色背景。

Depth Only(仅深度)

        效果

        颜色缓冲区不清除,保留之前摄像机的颜色数据。

        深度缓冲区:完全清除(深度值为 1)。

        适用场景

        叠加渲染:需要在已有的画面上添加新元素(如 UI),但不覆盖原有颜色。

        分屏效果:多个摄像机共享颜色缓冲区,但各自管理深度。

Don't Clear(不清除)

        效果

        颜色缓冲区不清除,保留之前摄像机的颜色数据。

        深度缓冲区不清除,保留之前的深度数据。

        适用场景

        高效叠加渲染:多个摄像机共同构建一个完整画面(如多层特效叠加)。

        实现特定视觉效果(如拖尾、残影)。

Camera Clear Flags(摄像机清除标志)

        效果:继承自 CameraSettings 资源中的清除设置(Unity 2023+ 版本新增)。

        适用场景:使用可编程渲染管线(URP/HDRP)时,通过配置资源统一管理清除设置。

代码控制设置:

using UnityEngine;

public class CameraClearController : MonoBehaviour
{
    public Camera mainCamera;
    public Camera uiCamera;
    public Camera effectCamera;

    void Start()
    {
        // 设置主摄像机使用天空盒作为背景
        mainCamera.clearFlags = CameraClearFlags.Skybox;
        mainCamera.backgroundColor = Color.black; // 天空盒不可用时的备用颜色

        // 设置 UI 摄像机只清除深度,保留颜色
        uiCamera.clearFlags = CameraClearFlags.DepthOnly;
        
        // 设置特效摄像机不清除任何缓冲区
        effectCamera.clearFlags = CameraClearFlags.DontClear;
    }

    // 动态修改 Clear Flag(如切换场景时)
    void ChangeClearFlag(CameraClearFlags newFlags)
    {
        mainCamera.clearFlags = newFlags;
    }
}
4. 投影的方式

        模拟人眼视觉,物体近大远小,适用于 3D 游戏和真实场景渲染。

        视野(Field of View, FOV)

        含义:摄像机视角的垂直角度(单位:度),控制视野宽窄。

        效果

        数值越小(如 30°):视野越窄,物体放大(类似长焦镜头)。

        数值越大(如 90°):视野越宽,场景容纳更多内容,但物体缩小(类似广角镜头)。

        应用场景:FPS 游戏常设 60°~90° 以扩大视野;特写镜头可设小 FOV 突出主体。

        近裁剪平面(Near Clip Plane)

        含义:摄像机近处的裁剪距离,小于此距离的物体不会被渲染。

        注意:值过小可能导致近处物体 “穿帮”(如角色手部被裁剪);值过大可能遮挡近距离细节。

        远裁剪平面(Far Clip Plane)

        含义:摄像机远处的裁剪距离,大于此距离的物体不会被渲染。

        作用:减少渲染负担(优化性能),但需避免因设置过小导致远处物体 “消失”。

5.  正交投影(Orthographic)

        无近大远小效果,物体尺寸始终一致,适用于 2D 游戏、UI 界面或工程制图。

        尺寸(Size)

        含义:正交投影下,摄像机视野高度的一半(单位:世界单位)。

        计算示例:若 Size=5,摄像机垂直方向可见范围为 10 个单位(如 Y 轴 - 5 到 + 5)。

        应用场景:2D 平台游戏中,通过调整 Size 控制屏幕显示的关卡高度。

6. Viewport Rect(视口矩形)

        含义:指定摄像机画面在屏幕上的显示区域(归一化坐标,范围 0~1)。

        参数Width/Height:画面的宽度和高度占比(如 Width=0.5 表示占据屏幕宽度的一半)。

        X/Y:画面左下角在屏幕中的位置(如 X=0.5,Y=0.5 表示右半屏)。

        应用场景:分屏游戏中,设置多个摄像机的 Viewport Rect 实现多玩家画面分割。

7. 目标纹理(Target Texture)

        含义:将摄像机画面渲染到指定的 RenderTexture(而非屏幕),用于实现镜像、小地图等效果。

        用法:创建 RenderTexture 并赋值给此参数,通过材质引用该纹理显示画面。

8. 渲染路径(Rendering Path)

        含义:指定摄像机使用的渲染管线(如 Forward、Deferred、Legacy 等)。

        作用:不同渲染路径对光照、阴影的处理方式不同,影响性能和效果。

        提示:现代项目常用Universal Render Pipeline(URP)High Definition Render Pipeline(HDRP),需在项目设置中配置。

二、摄像机遮挡剔除

1. 设置场景

        确定场景中所有要设定为静态遮挡物 (Static Occluder)(这些游戏对象不会移动,但会阻挡后面的游戏对象)和静态被遮挡物 (Static Occludee)(这些游戏对象不会移动,但会被静态遮挡物遮挡)。一个游戏对象可以同时是静态遮挡物和静态被遮挡物。

适合作为静态遮挡物的游戏对象包括中型到大型的实体游戏对象(例如墙壁或建筑物)。

确定了要设定为静态遮挡物和静态被遮挡物的游戏对象之后,便可以设置场景。

选择要设定为静态遮挡物的所有游戏对象。

        1. 在 Inspector 窗口中,打开 Static Editor Flags 下拉菜单,然后选择 Occluder Static

        2. 选择要设定为静态被遮挡物的所有游戏对象。

        3. 在 Inspector 窗口中,打开 Static Editor Flags 下拉菜单,然后选择 Occludee Static

        4. 将一个摄像机添加到场景中并选择该摄像机,或者选择一个现有摄像机。

        5. 在 Inspector 窗口中,确保启用了摄像机的 Occlusion Culling 属性。

2. 烘焙数据

 

--- 在顶部菜单中,选择 Window > Rendering > Occlusion Culling 以打开 Occlusion Culling 窗口

--- 选择 Bake 选项卡。

--- 在 Inspector 窗口的右下角,按 Bake 按钮。Unity 会生成遮挡剔除数据,将数据另存为项目中的资源,并将该资源与当前场景关联。

3. 对动态游戏对象使用遮挡剔除

        游戏对象可以是静态的,也可以是动态的(非静态)。静态游戏对象和动态游戏对象在 Unity 的遮挡剔除系统中的行为不同:

        Unity 可以将静态游戏对象作为静态遮挡物和/或静态被遮挡物烘焙到遮挡剔除数据中。

        Unity 无法将动态游戏对象烘焙到遮挡剔除数据中。动态游戏对象可以在运行时充当被遮挡物,而不能充当遮挡物。

        要确定动态游戏对象是否充当被遮挡物,可以在任何类型的渲染器组件上设置 Dynamic Occlusion 属性。启用 Dynamic Occlusion 后,渲染器在摄像机的视图中被静态遮挡物 (Static Occluder) 阻挡时,Unity 会剔除渲染器。禁用 Dynamic Occlusion 后,渲染器在摄像机的视图中被静态遮挡物 (Static Occluder) 阻挡时,Unity 不会剔除渲染器。

        默认情况下会启用 Dynamic Occlusion。为了获得特定的效果,例如在墙后的角色周围绘制轮廓,可能需要禁用 Dynamic Occlusion。

        如果确定 Unity 绝对不应该将遮挡剔除应用于特定的游戏对象,则可以禁用 Dynamic Occlusion 以减少运行时计算并降低 CPU 使用率。虽然这些计算对每个游戏对象的影响很小,但如果规模足够大,减少这些计算可能有利于提高性能。

三、多摄像机应用案例

1. 多视角切换

案例描述:在游戏中允许玩家在俯视图视图和第一人称视图等不同角度视图之间切换,以满足不同的游戏需求和操作体验。默认情况下,摄像机会用它所渲染的画面覆盖整个屏幕,因此同一时刻只能看到一个摄像机的画面(即 depth 属性具有最高值的摄像机)。通过在脚本中禁用一个摄像机并启用另一个,即可从一个摄像机的镜头“切”到另一个,从而得到场景的多个镜头画面。

实现方式:创建多个不同视角的摄像机,如一个用于俯视图的摄像机和一个用于第一人称视角的摄像机。默认情况下,只启用一个摄像机进行渲染(可见摄像机是 Depth 属性值最大的相机)。通过脚本控制,当玩家触发切换视角的操作时,禁用当前摄像机并启用目标视角的摄像机。

using UnityEngine;

public class ExampleScript : MonoBehaviour {
    public Camera firstPersonCamera;
    public Camera overheadCamera;

    // 调用此函数可禁用 FPS 摄像机,
    // 并启用高架摄像机。
    public void ShowOverheadView() {
        firstPersonCamera.enabled = false;
        overheadCamera.enabled = true;
    }
    
    // 调用此函数可启用 FPS 摄像机,
    // 并禁用高架摄像机。
    public void ShowFirstPersonView() {
        firstPersonCamera.enabled = true;
        overheadCamera.enabled = false;
    }
}
2. UI 与场景分离渲染

案例描述:一个 3D 游戏项目中,用一个 3D 摄像机(透视视角)渲染场景,一个 UI 摄像机(正交视角)渲染 UI 元素,使 UI 永远位于场景上层,如《明日方舟》主界面,右边 3D 转动部分用 3D 摄像机渲染,左上角固定 Button 用 UI 摄像机渲染。

实现方式:创建两个摄像机,分别设置为 3D 摄像机和 UI 摄像机。将 3D 摄像机的 Clear Flags 设为 SkyBox/Solid Color,Depth 值小于 UI 摄像机;UI 摄像机的 Clear Flags 设为 Depth Only,Depth 值大于 3D 摄像机。同时,将 UI 摄像机和 3D 场景摄像机分别设置为不同的渲染层(Layer),并在各自的 Culling Mask 中只勾选需要渲染的层,确保渲染互不干扰。

3. 分屏显示

案例描述:实现左右分屏或多宫格分屏效果,比如在多人游戏中为每个玩家提供独立视角。

实现方式:新建多个相机,在相机的 Inspector 窗口中,通过修改 “Output -> Viewport Rect” 属性来设置每个相机在屏幕上的显示区域。也可以在代码中动态设置,如var cameraData = camera.GetUniversalAdditionalCameraData(); cameraData.rect = new Rect(0.5f, 0f, 0.5f, 1f);实现右半屏显示。

示例 :基本的多屏设置和控制功能

using UnityEngine;

public class MultiScreenController : MonoBehaviour
{
    [Header("屏幕设置")]
    [Tooltip("激活的屏幕数量")]
    public int activeScreenCount = 2;

    [Tooltip("主摄像机(屏幕0)")]
    public Camera mainCamera;

    [Tooltip("辅助摄像机数组")]
    public Camera[] auxiliaryCameras;

    [Header("布局设置")]
    [Tooltip("是否水平排列屏幕")]
    public bool isHorizontalLayout = true;

    [Tooltip("自定义屏幕矩形设置")]
    public Rect[] customScreenRects;

    // 启动时初始化多屏显示
    void Start()
    {
        InitializeScreens();
    }

    // 初始化屏幕显示
    void InitializeScreens()
    {
        // 确保不超过系统支持的最大屏幕数
        int maxScreens = Display.displays.Length;
        activeScreenCount = Mathf.Clamp(activeScreenCount, 1, maxScreens);

        Debug.Log($"系统检测到 {maxScreens} 个屏幕,激活 {activeScreenCount} 个屏幕");

        // 激活主屏幕(屏幕0)
        Display.displays[0].Activate();

        // 设置主摄像机
        if (mainCamera != null)
        {
            mainCamera.targetDisplay = 0;
            mainCamera.rect = GetScreenRect(0);
        }

        // 激活并设置辅助屏幕
        for (int i = 1; i < activeScreenCount; i++)
        {
            Display.displays[i].Activate();
            
            // 设置对应的摄像机
            if (i - 1 < auxiliaryCameras.Length && auxiliaryCameras[i - 1] != null)
            {
                auxiliaryCameras[i - 1].targetDisplay = i;
                auxiliaryCameras[i - 1].rect = GetScreenRect(i);
            }
        }
    }

    // 获取屏幕的显示矩形
    Rect GetScreenRect(int screenIndex)
    {
        // 如果提供了自定义矩形,则使用自定义设置
        if (customScreenRects != null && customScreenRects.Length > screenIndex)
        {
            return customScreenRects[screenIndex];
        }

        // 自动计算矩形
        if (isHorizontalLayout)
        {
            // 水平排列
            float width = 1f / activeScreenCount;
            return new Rect(width * screenIndex, 0, width, 1);
        }
        else
        {
            // 垂直排列
            float height = 1f / activeScreenCount;
            return new Rect(0, height * screenIndex, 1, height);
        }
    }

    // 切换屏幕布局(水平/垂直)
    public void ToggleLayout()
    {
        isHorizontalLayout = !isHorizontalLayout;
        UpdateCameraRects();
    }

    // 更新所有摄像机的显示矩形
    void UpdateCameraRects()
    {
        if (mainCamera != null)
        {
            mainCamera.rect = GetScreenRect(0);
        }

        for (int i = 1; i < activeScreenCount; i++)
        {
            if (i - 1 < auxiliaryCameras.Length && auxiliaryCameras[i - 1] != null)
            {
                auxiliaryCameras[i - 1].rect = GetScreenRect(i);
            }
        }
    }
}    
4. 小地图与后视镜效果

案例描述:在游戏中实现小地图功能,让玩家了解全局地形;或在赛车游戏中实现后视镜效果,让玩家观察后方情况。

实现方式:创建一个用于渲染小地图或后视镜内容的摄像机,将其渲染结果渲染到 RenderTexture 上。然后将 RenderTexture 赋给对应的 UI 元素(如小地图的 Texture)或模型(如后视镜的面片)。需设置好摄像机的 Culling Mask,使其只渲染需要显示在小地图或后视镜中的物体。

5. 多相机体感应用

案例描述:在使用多台体感相机的项目中,如需要同步设备获取多视角人体动作数据的场景。

实现方式:以 Azure Kinect插件为例,首先连接多台相机到主机电脑并验证设备连接和通信。然后利用插件中的 MultiCameraSetup 场景进行校准,在 “层次结构” 中创建所需的传感器接口对象,设置其位置和旋转,并启用 KinectManager 组件的 “同步多摄像头帧” 设置进行自动校准。校准完成后可手动调整传感器的旋转和位置,最后通过启用 “使用多摄像机配置” 设置来测试校准文件的质量。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值