投影貼花開發指南
功能概述
貼花投影(Decal Projector)把一張 2D 圖片像幻燈機一樣投射到 3DGS 點雲表面。投影儀是世界空間中一個固定的透視視錐,落在視錐內的點雲被貼花顏色覆蓋,視錐外的點雲不受影響。貼花是全局着色狀態,對整個 3DGS 場景具有一致的着色效果。
在基礎投影之上可選開啟遮擋剔除。默認貼花是 X 光式的:由於 3DGS 本身並沒有表面的概念,貼花會穿透打到被前方物體遮擋的背面,並穿透整個 3DGS 場景;開啟遮擋剔除後,只有正對投影儀的前表面被染色,被擋住的背面與被遮場景不再染色,從而得到真實的幻燈機效果。
說明:
貼花與遮擋剔除僅在 PC / Mac(URP 管線) 下生效。在移動端或 VR 平台調用相關接口會被 SDK 忽略,並打印一條警告日誌。無需在業務側做平台判斷,但也不要期望在這些平台看到效果。
步驟 1:創建場景與遊戲對象
在 Unity 中創建一個全新的場景,Unity 默認會創建 Main Camera 和 Directional Light。
設置 Main Camera
在 Hierarchy 窗口中選擇 Main Camera,在 Inspector 窗口中按下圖設置其屬性。

設置 Main Camera 的屬性
創建 LCCManager
在 Hierarchy 窗口空白處單擊鼠標右鍵,在彈出的菜單中選擇 Create Empty,創建一個空遊戲對象,命名為 LCCManager,將其 Transform 歸一,並通過 Add Component 為其添加 LCCManager 腳本。

創建 LCCManager 遊戲對象並添加 LCCManager 腳本
創建 LCCRenderer
在 Hierarchy 窗口空白處單擊鼠標右鍵,選擇 Create Empty,創建一個空遊戲對象,命名為 LCCRenderer,將其 Transform 設置為 Rotation.x = -90、Scale.x = -1。

創建 LCCRenderer 並設置 Transform(Rotation.x = -90、Scale.x = -1)
注意:
LCCRenderer 遊戲對象的 Transform 設置(Rotation.x = -90、Scale.x = -1)是與 LCC 數據座標系的對齊。關於座標系對齊的詳細說明,參見 Unity SDK 簡介 中的「LCC 數據座標系」。
創建 Projector
在 Hierarchy 窗口空白處單擊鼠標右鍵,選擇 Create Empty,創建一個空遊戲對象,命名為 Projector。創建該空對象的目的是使用其 Transform 組件來控制投影儀的位置、方向等屬性;將其關聯到腳本代碼後,可在運行時調整投影儀屬性。投影儀沿其 +Z 方向投影,其屬性可根據需要設置。

創建 Projector 空遊戲對象作為投影儀(+Z 為投影方向)
步驟 2:添加 Render Feature
- 在 Unity 菜單欄中選擇 File → Project Settings,在左側選擇 Graphics,確認當前使用的 Default Render Pipeline Asset。本示例為 URP(Universal Render Pipeline Asset)。

在 Graphics 設置中確認 Default Render Pipeline Asset
點擊該 Asset,在 Project 窗口中定位到對應的資源文件,找到與之關聯的 Renderer(本示例為
URP_Renderer.asset)。選中該 Renderer 資源文件,在 Inspector 窗口中點擊 Add Renderer Feature,添加 Decal Depth Feature。

在 URP Renderer 中添加 Decal Depth Feature
注意:
Decal Depth Feature 是遮擋剔除的前提。若只使用基礎投影(X 光式貼花),可不添加;但要開啟遮擋剔除,則必須先在 URP Renderer Asset 中註冊該 Feature,否則遮擋深度圖不會生成,遮擋不生效。
步驟 3:編寫腳本
新建一個 C# 腳本,命名為 Decal.cs。下面的完整代碼模擬接入方視角,覆蓋全部 7 個貼花相關方法(5 個基礎投影 + 2 個遮擋剔除),可直接複製到工程使用。
using LCCCore;
using UnityEngine;
/// <summary>
/// Decal 貼花投影 —— 開發者最小使用示例。
///
/// 本腳本模擬"接入方腳本",只調用 LCCManager 的 public API,不觸碰 SDK 內部。
/// 覆蓋全部 7 個貼花 API:
/// 基礎投影(5) SetDecalEnabled / SetDecalTexture / SetDecalProjection / SetDecalOpacity / SetDecalEdgeFade
/// 遮擋剔除(2) SetDecalOcclusion / SetDecalOcclusionSettings
///
/// 使用步驟:
/// 1) 新建空 GameObject,掛上本腳本;
/// 2) Inspector 裡把場景中的 LCCManager 拖到 m_manager;
/// 3) m_filePath 填一個有效 LCC 場景路徑;
/// 4) m_decalTexture 拖入要投影的圖片(建議在導入設置裡勾 Read/Write);
/// 5) m_projector 拖入一個空物體當投影儀(留空則用本物體),它的 +Z 朝向即投影方向;
/// 6) Play。場景加載完成後貼花自動開啟,拖動投影儀即可實時看到貼花打在點雲上。
///
/// 平台說明:貼花與遮擋剔除僅在 PC/Mac(URP)生效,移動端/VR 調用會被 SDK 忽略並打印 LogWarning。
/// </summary>
public class Decal : MonoBehaviour
{
[Header("必填")]
[Tooltip("場景中的 LCCManager 實例")]
public LCCManager m_manager;
[Tooltip("有效的 LCC 場景路徑")]
public string m_filePath;
[Tooltip("要投影的貼花圖片。RGB=顏色, A=貼花自身遮罩")]
public Texture2D m_decalTexture;
[Header("投影儀 (沿 +Z 方向投影)")]
[Tooltip("投影儀位姿。留空則用本物體 transform")]
public Transform m_projector;
[Range(1f, 179f)] public float m_fov = 60f;
[Tooltip("投影寬高比; <=0 時按貼花圖片寬高比自動計算")]
public float m_aspect = 0f;
public float m_near = 0.1f;
public float m_far = 100f;
[Header("貼花外觀")]
[Range(0f, 1f)] public float m_opacity = 1.0f;
[Range(0f, 0.5f)][Tooltip("邊緣羽化寬度(UV 比例)。0 = 硬邊")]
public float m_edgeFade = 0.1f;
[Tooltip("每幀刷新投影,便於運行時拖動投影儀 / 改參數看效果")]
public bool m_updateEveryFrame = true;
[Header("遮擋剔除 (僅 PC/Mac)")]
[Tooltip("開啟後被前方遮擋的背面/被擋點雲不再被貼花染色(幻燈機效果),關閉則 X 光穿透")]
public bool m_occlusion = false;
[Range(0f, 1f)][Tooltip("opacity 閾值: 僅 opacity ≥ 此值的 splat 參與深度圖,濾浮點/霧狀點。0=全部參與")]
public float m_occlusionCutoff = 0f;
[Range(0f, 0.5f)][Tooltip("相對深度容差。過小→近面自遮擋; 過大→漏擋穿透。默認 0.05")]
public float m_occlusionBias = 0.05f;
[Range(0, 16)][Tooltip("方形足跡半徑上限(px),去穿透漏染主旋鈕。0=單紋素,默認 2")]
public int m_occlusionFootprint = 2;
[Tooltip("深度圖分辨率(px, 2 的冪 256~4096)。調低補掠射面空洞,默認 1024")]
public int m_occlusionResolution = 1024;
// ── 運行時狀態 ──
private bool m_loaded;
private bool m_lastOcclusion;
private float m_lastCutoff = float.NaN;
private float m_lastBias = float.NaN;
private int m_lastFootprint = -1;
private int m_lastResolution = -1;
void Start()
{
if (m_manager == null || string.IsNullOrEmpty(m_filePath))
{
Debug.LogError("[DecalDemo] m_manager 或 m_filePath 未賦值。");
enabled = false;
return;
}
// 1) 初始化渲染管線為 PC 單場景編輯模式
m_manager.SetPlatformType(PlatformType.PC);
m_manager.SwitchRenderPass(ActiveRenderMode.SingleRender);
m_manager.SetEditorMode(true);
// 2) 加載點雲場景,完成後再開啟貼花(貼花是全局狀態,場景就緒後設置最穩妥)
var renderer = m_manager.GetRender(this.transform);
Debug.Log($"[DecalDemo] 開始加載: {m_filePath}");
renderer.Load(m_filePath,
_ => { /* 進度回調,示例不使用 */ },
OnSceneLoaded);
}
private void OnSceneLoaded()
{
Debug.Log("[DecalDemo] 加載完成,開啟貼花投影。");
// ── 基礎投影:5 個 API ──
m_manager.SetDecalTexture(m_decalTexture); // 貼花內容
m_manager.SetDecalOpacity(m_opacity); // 整體不透明度
m_manager.SetDecalEdgeFade(m_edgeFade); // 邊緣羽化
ApplyProjection(); // 投影儀位姿(內部 SetDecalProjection)
m_manager.SetDecalEnabled(true); // 開啟貼花 (_DECAL keyword)
// ── 遮擋剔除:2 個 API ── 必須在 ApplyProjection 之後(深度圖需有效投影儀 VP)
m_manager.SetDecalOcclusionSettings(BuildOcclusionSettings());
m_manager.SetDecalOcclusion(m_occlusion);
CacheOcclusionState();
m_loaded = true;
}
/// <summary>
/// 僅演示使用,實際項目中,為提高性能,不需要在 Update() 方法中每幀調用
/// SetDecalProjection / SetDecalOpacity / SetDecalEdgeFade 等,僅在參數變化時調用相應 API 即可。
/// </summary>
void Update()
{
if (!m_loaded) return;
// 遮擋開關運行時切換
if (m_occlusion != m_lastOcclusion)
{
m_manager.SetDecalOcclusion(m_occlusion);
m_lastOcclusion = m_occlusion;
}
// 遮擋質量參數任一變化 → 一次性下發結構體
if (!Mathf.Approximately(m_occlusionCutoff, m_lastCutoff) ||
!Mathf.Approximately(m_occlusionBias, m_lastBias) ||
m_occlusionFootprint != m_lastFootprint ||
m_occlusionResolution != m_lastResolution)
{
m_manager.SetDecalOcclusionSettings(BuildOcclusionSettings());
CacheOcclusionState();
}
// 每幀刷新投影儀 / 外觀,方便實時調試
if (!m_updateEveryFrame) return;
m_manager.SetDecalOpacity(m_opacity);
m_manager.SetDecalEdgeFade(m_edgeFade);
ApplyProjection();
}
/// <summary>用當前投影儀位姿與透視參數構建投影矩陣。</summary>
private void ApplyProjection()
{
Transform p = m_projector != null ? m_projector : this.transform;
float aspect = m_aspect > 0f
? m_aspect
: (m_decalTexture != null ? (float)m_decalTexture.width / m_decalTexture.height : 1f);
m_manager.SetDecalProjection(p.position, p.rotation, m_fov, aspect, m_near, m_far);
}
/// <summary>把 4 項遮擋參數彙集為結構體。</summary>
private DecalOcclusionSettings BuildOcclusionSettings()
{
return new DecalOcclusionSettings
{
opacityCutoff = m_occlusionCutoff,
depthBias = m_occlusionBias,
footprintMaxPx = m_occlusionFootprint,
resolution = m_occlusionResolution,
};
}
private void CacheOcclusionState()
{
m_lastOcclusion = m_occlusion;
m_lastCutoff = m_occlusionCutoff;
m_lastBias = m_occlusionBias;
m_lastFootprint = m_occlusionFootprint;
m_lastResolution = m_occlusionResolution;
}
// ── 便捷調試入口 (Inspector 右鍵) ──
[ContextMenu("Decal On")] private void DecalOn() { m_manager?.SetDecalEnabled(true); }
[ContextMenu("Decal Off")] private void DecalOff() { m_manager?.SetDecalEnabled(false); }
[ContextMenu("Occlusion On")] private void OcclusionOn() { m_occlusion = true; m_manager?.SetDecalOcclusion(true); m_lastOcclusion = true; }
[ContextMenu("Occlusion Off")] private void OcclusionOff() { m_occlusion = false; m_manager?.SetDecalOcclusion(false); m_lastOcclusion = false; }
}
注意:
貼花是全局着色狀態,需要在 LCC 場景加載完成後再設置(即在 onComplete回調中設置)。
示例把
SetDecalTexture/SetDecalProjection/SetDecalEnabled放在Load完成回調(OnSceneLoaded)裡,避免點雲尚未就緒時設置。遮擋剔除相關的
SetDecalOcclusionSettings/SetDecalOcclusion必須在ApplyProjection(即SetDecalProjection)之後調用,因為遮擋深度圖需要有效的投影儀視圖投影矩陣。
步驟 4:掛載腳本與屬性設置
- 將
Decal.cs腳本掛載到 Hierarchy 窗口中的 LCCRenderer 遊戲對象上。 - 在 Inspector 窗口中,將 LCCManager 遊戲對象拖動到腳本的 Manager 屬性上。
- 將 Projector 遊戲對象拖動到腳本的 Projector 屬性上。
- 在腳本的 File Path 屬性中填寫 LCC 文件的路徑地址,並將貼花紋理圖片拖到 Decal Texture 屬性上。
- 其他屬性參數可根據需要調整,各屬性含義見下表。

Decal 腳本的 Inspector 屬性設置
| 字段 | 含義 | 默認值 |
|---|---|---|
| Manager | 場景中的 LCCManager 實例 | 必填 |
| File Path | 有效的 LCC 場景路徑 | 必填 |
| Decal Texture | 要投影的貼花圖片,RGB 為顏色、A 為貼花自身遮罩 | 必填 |
| Projector | 投影儀位姿,+Z 為投影方向;留空則用本物體 Transform | 空 |
| Fov | 投影儀透視視角 | 60 |
| Aspect | 投影寬高比;小於等於 0 時按貼花圖片寬高比自動計算 | 0(自動) |
| Near / Far | 投影儀視錐近 / 遠裁剪面 | 0.1 / 100 |
| Opacity | 貼花整體不透明度 | 1 |
| Edge Fade | 邊緣羽化寬度(UV 比例),0 為硬邊 | 0.1 |
| Update Every Frame | 每幀刷新投影,便於運行時拖動投影儀或改參數 | 勾選 |
| Occlusion | 是否開啟遮擋剔除 | 關 |
| Occlusion Cutoff | opacity 閾值,僅 opacity 不低於該值的 splat 參與深度圖 | 0 |
| Occlusion Bias | 相對深度容差 | 0.05 |
| Occlusion Footprint | 方形足跡半徑上限(px),去穿透漏染主旋鈕 | 2 |
| Occlusion Resolution | 遮擋深度圖分辨率(px,2 的冪 256~4096) | 1024 |
步驟 5:運行效果
在 Unity 中點擊 Play 按鈕,場景加載完成後貼花會自動開啟。當勾選 Update Every Frame 時,運行過程中可直接在 Scene 視圖拖動投影儀對象,實時看到貼花在 3DGS 表面移動;調整 Opacity、Edge Fade 等參數也會即時生效。腳本組件的右鍵菜單還提供 Decal On/Off、Occlusion On/Off 四個快捷入口,便於單獨驗證開關效果。

運行後貼花投影到 3DGS 表面(基礎投影,X 光式穿透)

開啟遮擋剔除後,只有正對投影儀的前表面被染色(幻燈機效果)
常見問題:關閉兼容模式
如果 Unity Console 窗口報錯且沒有出現貼花效果,請關閉兼容模式:通過 Edit → Project Settings → Graphics 打開設置面板,選擇 URP 選項卡,不要勾選 Render Graph → Compatibility Mode (Render Graph Disabled) 後的多選框。

不要勾選 Render Graph 兼容模式(Render Graph Disabled)
遮擋剔除與調參
遮擋剔除需在設置好投影儀之後再開啟。示例在場景加載完成回調裡先調用 SetDecalProjection,再調用 SetDecalOcclusionSettings 與 SetDecalOcclusion,以保證遮擋深度圖拿到有效的投影儀視圖投影矩陣。運行時把 Inspector 的 Occlusion 勾選打開,即可看到穿透塊被剔除、只保留正對投影儀的前表面。
前提:
開啟遮擋剔除前,URP Renderer Asset 中必須已註冊 Decal Depth Feature(本工程已作為子資源寫入
URP_Renderer.asset)。若未註冊,遮擋深度圖不會生成,遮擋不會生效。
遮擋質量由四個參數控制,通過一個結構體一次性下發。四者相互正交,按下表調節:
| 參數 | 作用 | 默認 | 調節建議 |
|---|---|---|---|
opacityCutoff | 過濾漂浮 / 霧狀 splat,避免它們把遮擋面拉近 | 0 | 前景出現塊狀噪點時適度調大;調太大會漏染 |
depthBias | 相對深度容差,按距離吸收點雲表面厚度 | 0.05 | 近面出現自遮擋噪點時調大;薄遮擋物漏擋時調小 |
footprintMaxPx | 前表面近深度按世界尺度蓋滿稀疏縫隙,根治穿透漏染(主旋鈕) | 2 | 穿透塊殘留時調大;輪廓過遮擋時調小,0 為單紋素 |
resolution | 遮擋深度圖分辨率 | 1024 | 掠射面出現成片漏染小方塊時調低(如 512 / 256),代價是遮擋邊界略糙 |
關鍵步驟
實現投影貼花的關鍵步驟:
- 場景中創建 Projector 空對象作為投影儀,其 +Z 朝向即投影方向。
- 若需要遮擋剔除,確保在 URP Renderer 中添加了 Decal Depth Feature 渲染特性。
- 在 LCC 場景加載完成後依次設置貼花紋理、外觀、投影儀位姿,再開啟貼花(
SetDecalEnabled(true))。 - 遮擋剔除的
SetDecalOcclusionSettings/SetDecalOcclusion需在SetDecalProjection之後調用。
貼花相關 API
以下方法均位於 LCCManager 門面類,僅 PC / Mac(URP 管線)生效,移動端 / VR 調用會被忽略並打印警告。
SetDecalEnabled
// 返回值:void
public void SetDecalEnabled(bool enable)
開關貼花投影。開啟時會把當前緩存的不透明度 / 邊緣羽化參數推到 GPU 並啟用 _DECAL shader keyword。貼花是世界固定的全局狀態,對所有相機一致。需另外調用 SetDecalTexture 與 SetDecalProjection 設置內容與投影儀位姿。
| 參數 | 類型 | 說明 |
|---|---|---|
enable | bool | 是否開啟貼花投影 |
調用示例:
m_manager.SetDecalEnabled(true);
m_manager.SetDecalTexture(decalTex);
m_manager.SetDecalProjection(projectorPos, projectorRot, 60f, 1.778f, 0.1f, 20f);
說明:
- 關閉時僅禁用
_DECALkeyword,不清空已緩存的紋理 / 參數。
SetDecalTexture
// 返回值:void
public void SetDecalTexture(Texture2D tex)
設置貼花紋理。RGB 為顏色,A 為貼花自身遮罩,會與全局不透明度、邊緣羽化相乘。
| 參數 | 類型 | 說明 |
|---|---|---|
tex | Texture2D | 貼花紋理 |
調用示例:
m_manager.SetDecalTexture(logoTex);
說明:
- 傳入
null會清空全局紋理引用。
SetDecalProjection
// 返回值:void
public void SetDecalProjection(Vector3 pos, Quaternion rot, float fov, float aspect, float near, float far)
設置貼花投影儀的位姿與透視參數,投影儀沿其 forward(+Z)方向投影,視錐內的點被貼花覆蓋。
| 參數 | 類型 | 說明 |
|---|---|---|
pos | Vector3 | 投影儀世界座標 |
rot | Quaternion | 投影儀朝向 |
fov | float | 垂直 FOV(度),鉗制 [1, 179] |
aspect | float | 寬高比,最小 1e-4 |
near | float | 近裁剪面,最小 1e-3 |
far | float | 遠裁剪面,最小 near + 1e-3 |
調用示例:
m_manager.SetDecalProjection(new Vector3(0, 10, 0), Quaternion.Euler(90, 0, 0), 60f, 1.778f, 0.1f, 20f);
說明:
- 若已開啟
SetDecalOcclusion(true),本方法會同步把新的 VP / view 轉發給Decal Depth Feature。
SetDecalOpacity
// 返回值:void
public void SetDecalOpacity(float opacity)
設置貼花整體不透明度,立即推到 GPU 並緩存,供下次 SetDecalEnabled(true) 時複用。
| 參數 | 類型 | 說明 |
|---|---|---|
opacity | float | 貼花整體不透明度,範圍 [0, 1] |
調用示例:
m_manager.SetDecalOpacity(0.8f);
說明:
- 範圍
[0, 1],超出自動鉗制。
SetDecalEdgeFade
// 返回值:void
public void SetDecalEdgeFade(float edgeFade)
設置貼花邊緣羽化寬度。0 為硬邊,值越大邊緣過渡越柔和。
| 參數 | 類型 | 說明 |
|---|---|---|
edgeFade | float | 邊緣羽化寬度,UV 空間比例,範圍 [0, 0.5) |
調用示例:
m_manager.SetDecalEdgeFade(0.1f);
說明:
- 範圍
[0, 0.5),超出自動鉗制。
SetDecalOcclusion
// 返回值:void
public void SetDecalOcclusion(bool enable)
開關貼花遮擋剔除(基於投影儀深度圖),與陰影功能正交、獨立開關。開啟前應已調用 SetDecalProjection 設定投影儀,否則深度圖沒有有效 VP。
| 參數 | 類型 | 說明 |
|---|---|---|
enable | bool | 是否開啟貼花遮擋剔除 |
調用示例:
m_manager.SetDecalOcclusion(true);
說明:
- 僅 URP 支持(內置管線調用直接 no-op 並打印日誌)。
- 需要
Decal Depth Feature已註冊在 URP Renderer Asset 中。 - 依賴
SetDecalOcclusionSettings配置的質量參數。
SetDecalOcclusionSettings
// 返回值:void
public void SetDecalOcclusionSettings(DecalOcclusionSettings settings)
一次性設置貼花遮擋剔除的全部質量參數(四項含義見下方結構體與「遮擋剔除與調參」表)。
| 參數 | 類型 | 說明 |
|---|---|---|
settings | DecalOcclusionSettings | 遮擋剔除質量參數 |
調用示例:
m_manager.SetDecalOcclusionSettings(DecalOcclusionSettings.Default);
m_manager.SetDecalOcclusionSettings(new DecalOcclusionSettings {
opacityCutoff = 0.1f,
depthBias = 0.05f,
footprintMaxPx = 2,
resolution = 1024
});
說明:
- 隨時可調,參數會立即推到 GPU 與
Decal Depth Feature。 - 變更
resolution會觸發深度圖 RT 重建。
相關數據結構
DecalOcclusionSettings
public struct DecalOcclusionSettings {
public float opacityCutoff; // [0,1] splat 參與深度圖的最低 opacity,默認 0(全部參與)
public float depthBias; // 相對深度容差比例,默認 0.05
public int footprintMaxPx; // 方形足跡半徑上限(px),0~16,默認 2
public int resolution; // 深度圖分辨率(px,2 的冪,256~4096),默認 1024
public static DecalOcclusionSettings Default { get; } // cutoff=0, bias=0.05, footprint=2, resolution=1024
}
用於 SetDecalOcclusionSettings(),可通過 DecalOcclusionSettings.Default 獲取默認值。
| 字段 | 含義 | 默認 |
|---|---|---|
opacityCutoff | float,[0,1],opacity 閾值,僅不低於該值的 splat 參與深度圖 | 0 |
depthBias | float,相對深度容差 | 0.05 |
footprintMaxPx | int,0~16,方形足跡半徑上限(px) | 2 |
resolution | int,2 的冪 256~4096,遮擋深度圖分辨率 | 1024 |
注意事項
- 僅 PC / Mac 生效。 移動端與 VR 平台調用貼花相關接口會被忽略並打印警告,無需在業務側做平台判斷,但也不要期望在這些平台看到效果。
- 建議在場景加載完成後再設置貼花。 貼花是全局着色狀態,示例把
SetDecalTexture/SetDecalProjection/SetDecalEnabled放在Load完成回調裡,避免點雲尚未就緒時設置。 - 遮擋剔除依賴兩個前提。 一是 URP Renderer Asset 已註冊 Decal Depth Feature,二是已調用過
SetDecalProjection設定投影儀;兩者缺一則遮擋不生效。 - 靜態投影幾乎零額外開銷。 遮擋深度圖僅在投影儀位姿或點雲發生變化時重算,投影儀靜止時不產生額外的每幀計算。