Unity Camera.cullingMask 使用指南:Layer 精準控制、XR 多視圖與性能提升
關鍵詞:Unity、Camera、Culling Mask、Layer 控制、XR 渲染分離、UI 顯隱、性能優化
特別說明:
本文為近期項目所遇問題的總結,僅純文字記錄,截圖不便分享。見諒!
文章目錄
- Unity Camera.cullingMask 使用指南:Layer 精準控制、XR 多視圖與性能提升
- 業務故事:一場“UI 不該出現在眼鏡里”的現場事故
- 一、故事起源:展廳演示的第一天
- 二、事故現場:UI “失控”現身
- 三、初步排查與手動修復
- 四、團隊決策:封裝一鍵Layer控制工具
- 技術實踐指南:Unity 相機 Culling Mask 控制
- 1. 背景與動機
- 2. Culling Mask 原理解析
- 2.1 定義
- 2.2 示例代碼
- 3. 實現目標與功能設計
- 3.1 設計要求
- 4. 完整腳本實現
- 5. 示例調用與調試方法
- 5.1 示例代碼
- 5.2 編輯器拓展建議
- 6. 應用場景分析
- 6.1 XR 項目中 UI 分離顯示
- 6.2 開發調試隔離
- 6.3 Portal 鏡頭或切換效果
- 7. 擴展功能建議
- 7.1 支持 LayerMask 類型參數
- 7.2 添加排除層功能
- 8. 總結與最佳實踐
- 9. 參考資料
業務故事:一場“UI 不該出現在眼鏡里”的現場事故
“當你的UI界面在VR眼鏡里擋住了用戶視野,卻在PC上毫無問題時,你會怎么做?”
一、故事起源:展廳演示的第一天
展館模型真實還原,觀眾佩戴 VR 頭顯后,可漫游各個展區;同時,操作員在 PC 端可通過鼠標實時監控視角、觸發高亮或切屏指令。
為了滿足雙端顯示需求,我們配置了三臺相機:
- PC 主相機:渲染環境、UI、調試信息;
- VR 頭顯相機:只渲染環境與玩家視角;
- UI 專用相機:渲染所有 Canvas 元素,包含菜單、指南、調試層。
二、事故現場:UI “失控”現身
內部測試環境一切正常:PC 端界面完整,VR 端只見清爽的展示,無多余遮擋。
客戶演示當天,他戴上頭顯,第一反應卻是:“這 UI 是怎么回事?我眼前都是菜單,根本看不到展品!”
現場:
- VR 端 UI 堆疊、遮擋用戶視野;
- 點擊操作失效(實際上點擊走的是 PC 相機);
- 用戶無法退出 UI,只能取下頭顯。
我們一看,果然:VR 相機和 UI 相機都在渲染相同的 UI Layer,“打包”給了頭顯。
三、初步排查與手動修復
打開 Unity Inspector,發現:
XRCamera.cullingMask = Everything
UICamera.cullingMask = Everything
兩臺相機都在渲染所有 Layer。
于是,測試人員反復手動:
- 取消 XRCamera 的 UI Layer;
- 勾選 UICamera 的 UI Layer;
- 切場景、切模式,效果斷斷續續,易出錯;
手動操作效率低,且在頻繁切換場景時容易遺漏。
四、團隊決策:封裝一鍵Layer控制工具
為避免 “手動取消勾選” 的痛苦,我們決定封裝一個可復用、一鍵切換的 Culling Mask 控制組件,滿足:
- 只渲染指定 Layer
- 恢復渲染所有 Layer
- 回退到上一次狀態
- 自動識別 Camera.main
- 支持多 Layer 名稱輸入
一旦寫出工具,任何相機都可在 Inspector 或運行時,通過簡短代碼切換渲染范圍。
上述故事純屬虛構,下面開始正文。
技術實踐指南:Unity 相機 Culling Mask 控制
1. 背景與動機
在 Unity 項目開發中,尤其是涉及 多層級 UI 管理、XR/VR 分視圖渲染、動態視角切換與性能調試 時,我們經常需要靈活控制相機渲染對象的范圍。
本質上,我們希望控制:某個 Camera 渲染哪些 Layer。
常見應用如:
- XR 項目中:僅 PC 屏幕顯示 UI,VR 頭顯中不顯示;
- Portal 或多視角切換場景:主相機與子視角相機渲染不同對象;
- 開發調試模式:僅在 Debug 模式下顯示 Gizmo 層;
- 鏡頭特效系統:僅渲染特定對象做后處理。
這些都離不開對 Unity 相機的 Culling Mask
的精細控制。
2. Culling Mask 原理解析
2.1 定義
Unity 中的每個 Camera 都有一個 Culling Mask 屬性,它是一個 32 位的位掩碼(bitmask),用于表示該相機應該 “看到” 哪些 Layer。
Camera.cullingMask: int → 每一位代表一個 Layer(最多支持 32 個)
2.2 示例代碼
// 只顯示 UI 層
camera.cullingMask = 1 << LayerMask.NameToLayer("UI");// 同時顯示多個層(使用按位或)
camera.cullingMask = (1 << Layer1) | (1 << Layer2);// 使用 LayerMask 工具方法
camera.cullingMask = LayerMask.GetMask("Default", "UI", "GuideLayer");
3. 實現目標與功能設計
我們封裝一個組件 CameraCullingMaskController
,具備以下功能:
方法名 | 功能描述 | ||
---|---|---|---|
`ApplyLayersOnly("UI | Default")` | 僅顯示指定 Layer(多個以“ | ”分隔) |
RestoreEverything() | 恢復顯示所有 Layer(即 LayerMask.All) | ||
RestoreOriginal() | 回退到上一次設置前的原始狀態 |
3.1 設計要求
- 可自動識別并使用
Camera.main
; - 支持傳入多個 Layer 名;
- Layer 合法性校驗與報錯提示;
- 可擴展性好,便于嵌入 XR 項目。
4. 完整腳本實現
using UnityEngine;namespace XRCoreRuntime.Runtime.Component
{/// <summary>/// 控制 Camera 的 CullingMask 顯示特定 Layer 或恢復狀態/// </summary>public class CameraCullingMaskController : MonoBehaviour{[Header("指定相機,未指定則使用 Camera.main")]public Camera? mainCam;private int originalCullingMask;public void ApplyLayersOnly(string targetLayers){if (string.IsNullOrEmpty(targetLayers)) {Debug.LogWarning("傳入的 Layer 字符串為空!");return;}mainCam ??= Camera.main;if (mainCam == null) {Debug.LogWarning("Camera 未找到!");return;}originalCullingMask = mainCam.cullingMask;var names = targetLayers.Split('|');int mask = 0;foreach (var name in names){int layer = LayerMask.NameToLayer(name.Trim());if (layer == -1){Debug.LogError($"Layer \"{name}\" 不存在!");continue;}mask |= 1 << layer;}if (mask == 0){Debug.LogWarning("未能解析出有效 Layer!");return;}mainCam.cullingMask = mask;}public void RestoreEverything(){mainCam ??= Camera.main;if (mainCam != null)mainCam.cullingMask = ~0;}public void RestoreOriginal(){mainCam ??= Camera.main;if (mainCam != null)mainCam.cullingMask = originalCullingMask;}}
}
5. 示例調用與調試方法
5.1 示例代碼
var ctrl = GetComponent<CameraCullingMaskController>();// 顯示 UI 和 GuideLayer
ctrl.ApplyLayersOnly("UI|GuideLayer");// 恢復所有
ctrl.RestoreEverything();// 回退到原狀態
ctrl.RestoreOriginal();
5.2 編輯器拓展建議
可通過 CustomEditor
創建按鈕,直接點擊控制:
@startuml
actor Developer
participant InspectorButton
participant CameraCullingMaskControllerDeveloper -> InspectorButton : 點擊 "顯示 UI"
InspectorButton -> CameraCullingMaskController : ApplyLayersOnly("UI")Developer -> InspectorButton : 點擊 "恢復全部"
InspectorButton -> CameraCullingMaskController : RestoreEverything()
@enduml
6. 應用場景分析
6.1 XR 項目中 UI 分離顯示
場景:希望 UI 僅顯示在 PC 上,頭顯中不顯示。
做法:
// VR 主相機只渲染環境與默認層
ctrl.ApplyLayersOnly("Environment|Default");
// UI 相機只渲染 UI
ctrl.ApplyLayersOnly("UI");
6.2 開發調試隔離
將調試工具放入 DebugLayer
,僅調試時開啟:
ctrl.ApplyLayersOnly("Default|DebugLayer");
6.3 Portal 鏡頭或切換效果
用多臺 Camera 渲染不同視角,組合效果類似:
主相機:環境 + 玩家
鏡頭相機:僅渲染特效層
7. 擴展功能建議
7.1 支持 LayerMask 類型參數
將 ApplyLayersOnly(string)
改為 ApplyLayersOnly(LayerMask mask)
,便于在編輯器中使用 MaskField
。
7.2 添加排除層功能
增加方法:
public void ApplyExcludeLayers(string excludeLayers);
使用位操作方式:
mainCam.cullingMask = ~LayerMask.GetMask("Debug", "UI");
8. 總結與最佳實踐
本文從業務事故切入,結合實際項目需求,詳盡講解了 Unity 中相機 Culling Mask 的使用方法。通過封裝 CameraCullingMaskController
,實現了:
- 動態 Layer 渲染控制;
- UI 顯示隔離;
- XR 多視圖調度;
- Portal 場景優化;
- 性能剔除帶來的幀率提升。
技術的價值在于解決實際問題。
下次再遇到“UI 不該出現在眼鏡里”的煩惱時,試試這套工具,讓每個相機都“只看”自己應該看的內容。
9. 參考資料
-
Unity 官方文檔
- Camera.cullingMask
- LayerMask
從最初的“UI 失靈”到完整的 Culling Mask 控制方案,我們體會到:
一行代碼的變化,能讓整個系統更加健壯、可維護,也讓開發效率成倍提升。
希望這篇文章,既讓你在項目演示不再手忙腳亂,也為日后更復雜的多相機、多視圖場景打下堅實基礎。