实时裁剪开发指南
LCC Unity SDK 支持对 3DGS 场景进行高亮、裁剪等编辑操作。本文介绍实时裁剪相关操作:通过 2D 投影纹理、3D 几何体或二者混合的方式圈选区域,把选中区域从场景中裁掉,支持正选、反选等操作。
注意:
当前裁剪功能仅是运行时的显示效果,不会对原始数据文件产生任何修改,也不支持将编辑结果保存导出。如果需要调色、裁剪并保存数据,请使用其域创新(XGrids)LCC Editor 软件工具处理。
如果不希望修改数据,也可以将操作记录下来,下次在数据加载完成后重放操作。
前置条件
通过 SDK 对场景进行裁剪,需要满足以下条件:
- 在加载数据之前进入编辑状态。 SDK 默认处于浏览状态(View),该状态能提供最佳性能,正常浏览时建议保持。要进行裁剪等编辑操作,需在
Load数据之前通过SetEditorMode(true)切换到编辑状态。该方法只在加载所有数据之前调用才有效。 - 在数据加载完成后再执行裁剪操作。 裁剪接口需要在场景数据完全加载后调用。
步骤 1:搭建场景
参照下图在场景中准备好裁剪所需的游戏对象。

裁剪示例的场景层级结构
- Main Camera:场景相机,2D 裁剪会用到它的视图与投影矩阵。
- LCCManager:空游戏对象,Transform 归一,添加
LCCManager脚本。 - LCCRenderer:空游戏对象,Transform 设置为 Rotation.x = -90、Scale.x = -1(与 LCC 数据坐标系对齐),用于挂载后面编写的
Clip脚本。 - 3D 选区几何体:用于 3D 裁剪的辅助对象。示例中准备了 3 个 Cube(
Box1、Box2、Box3)和 1 个 Sphere(Clipsphere),移动、缩放这些对象即可控制裁剪选区的位置和大小。
注意:
渲染相机、LCCManager、LCCRenderer 游戏对象(GameObject)相关设置请参照PC 快速开发入门指南、工程设置优化指南 相关章节。

用于 3D 裁剪的选区几何体(Box1 / Box2 / Box3 / Clipsphere)
步骤 2:了解数据结构
裁剪涉及 2D、3D 和 2D/3D 混合三种操作方式,用到的枚举和结构体如下。
public enum SelectMode { normal, invert, clip } // 正常选区、反选、实时裁剪
public enum OperationMode { normal, erase } // 正常选区、擦除选区
public enum ShapeType { box, sphere } // 3D Box 选区、3D Sphere 选区
public enum EditType { Edit2D, Edit3D } // 2D 编辑、3D 编辑
// 2D 纹理选区
public struct Data2D
{
public Texture2D texture; // 2D 蒙版纹理
public Matrix4x4 mv; // 相机视图矩阵
public Matrix4x4 mvp; // 相机视图投影矩阵
public SelectMode mode; // 选择模式
}
// 3D 几何体选区
public struct Data3D
{
public ShapeType type; // 几何体类型(box / sphere)
public Matrix4x4 invMat; // 几何体的世界到局部(逆变换)矩阵
public OperationMode operation; // 操作类型(normal 并集 / erase 差集)
public SelectMode mode; // 选择模式
}
// 2D / 3D 混合选区
public struct DataMix
{
public EditType editType; // 编辑类型(必填,决定使用 2D 还是 3D 字段)
public Texture2D texture; // 2D 蒙版纹理(Edit2D 使用)
public Matrix4x4 mv; // 相机视图矩阵(Edit2D 使用)
public Matrix4x4 mvp; // 相机视图投影矩阵(Edit2D 使用)
public ShapeType type; // 几何体类型(Edit3D 使用)
public Matrix4x4 invMat; // 几何体逆变换矩阵(Edit3D 使用)
public OperationMode operation; // 操作类型(Edit3D 使用)
public SelectMode mode; // 选择模式(必填)
}
关于选区列表,有三点需要注意:
- 列表是有序的。 无论 2D 还是 3D,列表中的条目按顺序依次生效,请确保添加顺序符合预期。
- 反选是操作指令,可不关联具体选区数据。 只需追加一个
mode = SelectMode.invert的条目,即可把已圈选区域反选。 - 单个列表的条目数量上限为 255。 超出会返回错误码。
步骤 3:了解裁剪 API
裁剪相关接口都由 LCCManager 提供,包含三个 SetClip 重载和一个退出方法。
SetClip(2D 纹理裁剪)
// 返回值:int
// 0:执行成功
// 1:列表为 null 或元素个数为 0
// 2:列表元素超出最大值 255
public int SetClip(List<Data2D> _2Ds)
使用 2D 投影纹理蒙版对场景进行裁剪,被标记的区域从场景中裁掉。
SetClip(3D 几何体裁剪)
// 返回值:int
// 0:执行成功
// 1:列表为 null 或元素个数为 0
// 2:列表元素超出最大值 255
// 3:用于选择的几何框数量为 0
public int SetClip(List<Data3D> _3Ds)
使用 3D 几何体(长方体、正方体、球体)定义裁剪区域,支持并集与差集组合。至少需要一个 operation == OperationMode.normal 的几何体。
SetClip(混合模式裁剪)
// 返回值:int
// 0:执行成功
// 1:列表为 null 或元素个数为 0
// 2:列表元素超出最大值 255
public int SetClip(List<DataMix> _Mix)
同时使用 2D 纹理和 3D 几何体混合定义裁剪区域,每个条目通过 editType 决定按 2D 还是 3D 处理。
QuitClipMode
// 返回值:void
public void QuitClipMode()
退出裁剪模式,恢复场景完整显示。
提示:
三个
SetClip重载互斥。切换裁剪方式或清除当前裁剪时,先调用QuitClipMode()再设置新的裁剪。
步骤 4:2D 蒙版纹理规范
2D 裁剪通过一张蒙版纹理来描述选区,可支持四边形、三角形、笔刷、套索等各种圈选方式——在纹理上把对应像素标记为裁剪值即可。使用 2D 操作时应锁定相机,在固定视角下标识纹理。纹理和像素值有如下约定:
- 纹理规格固定为 1024 × 1024,
R8单通道,不生成 Mipmap,使用点滤波(FilterMode.Point),采样夹断(TextureWrapMode.Clamp)。 - 像素值约定:
| 像素值 | 含义 |
|---|---|
128 | 默认(保留) |
240 | 选中 |
180 | 减选(擦除已选区域) |
0 | 裁剪 |
在裁剪场景下,通常只用到 128(默认保留)和 0(裁剪)两个值。
步骤 5:编写脚本
新建一个 C# 脚本,命名为 Clip.cs。先完成基础框架:在 Start 中进入编辑状态并加载数据,加载完成后置位加载标记,供后续裁剪方法判断。
using LCCCore;
using System.Collections.Generic;
using UnityEngine;
public class Clip : MonoBehaviour
{
public LCCManager m_manager;
public string m_FilePath;
public GameObject m_Box1;
public GameObject m_Box2;
public GameObject m_Box3;
public GameObject m_Clipsphere;
private LCCCore.Renderer m_renderer;
private Camera m_camera;
private bool m_isLoaded = false;
void Start()
{
m_camera = Camera.main;
m_manager.SetPlatformType(PlatformType.PC);
// 进入编辑状态,必须在 Load 之前调用
m_manager.SetEditorMode(true);
m_renderer = m_manager.GetRender(this.transform);
if (m_renderer == null)
Debug.LogError("GetRender failed, check if the manager is set correctly and the file path is valid.");
m_renderer.Load(m_FilePath, onLoadProgress, onLoadCallback);
}
private void onLoadProgress(float value)
{
Debug.Log($"loading progress: {value}");
}
private void onLoadCallback()
{
Debug.Log("data loaded !!!");
m_isLoaded = true;
// 关闭环境(远景低精度点云)渲染,便于观察裁剪效果
m_renderer.SetEnvironment(false);
}
// 2D / 3D / 混合裁剪方法见下文
}
注意:
SetEditorMode(true)必须在Load之前调用才有效;而SetClip必须在数据加载完成(onLoadCallback回调触发)之后调用。SetEnvironment(false)用于关闭远景环境点云,让裁剪结果更清晰,可根据需要决定是否调用。
步骤 6:挂载脚本并设置属性
- 将
Clip.cs脚本挂载到 LCCRenderer 游戏对象上。 - 在 Inspector 窗口中,将 LCCManager 游戏对象拖动到脚本的 Manager 属性上。
- 在 File Path 属性中填写 LCC 文件的路径地址。
- 将场景中的
Box1、Box2、Box3、Clipsphere分别拖动到脚本对应的属性上。

在 Inspector 中设置 Clip 脚本的属性
步骤 7:裁剪操作示例
将下面的方法补充到 Clip 类中,并在运行、数据加载完成后调用对应方法。
2D 纹理裁剪
构造两张 R8 蒙版纹理,把要裁掉的像素标记为 0、其余保留区域标记为 128,再用相机矩阵构造 Data2D 加入列表。
public void on2DClip()
{
// 纹理尺寸固定 1024*1024,其它属性保持与示例一致
Texture2D _texture = new Texture2D(1024, 1024, TextureFormat.R8, false);
_texture.filterMode = FilterMode.Point;
_texture.wrapMode = TextureWrapMode.Clamp;
int _count = 1024 * 1024;
int _half = _count / 2;
byte[] _values = new byte[_count];
for (int i = 0; i < _count; i++)
{
if (i < _half)
_values[i] = 0; // 0 表示裁剪,128 表示默认,240 表示选中
else
_values[i] = 128;
}
_texture.SetPixelData(_values, 0);
Texture2D _texture2 = new Texture2D(1024, 1024, TextureFormat.R8, false);
_texture2.filterMode = FilterMode.Point;
_texture2.wrapMode = TextureWrapMode.Clamp;
byte[] _values2 = new byte[_count];
for (int i = 0; i < _count; i++)
{
if (i > _count * 0.75 && i < _count * 0.9)
_values2[i] = 0;
else
_values2[i] = 128;
}
_texture2.SetPixelData(_values2, 0);
List<Data2D> _2Ds = new List<Data2D>();
Data2D _2d01 = new Data2D();
_2d01.mv = m_camera.worldToCameraMatrix;
_2d01.texture = _texture;
_2d01.mode = SelectMode.normal;
_2d01.mvp = GL.GetGPUProjectionMatrix(m_camera.projectionMatrix, true) * m_camera.worldToCameraMatrix;
_2Ds.Add(_2d01);
Data2D _2d02 = new Data2D();
_2d02.mv = m_camera.worldToCameraMatrix;
_2d02.texture = _texture2;
_2d02.mode = SelectMode.normal;
_2d02.mvp = GL.GetGPUProjectionMatrix(m_camera.projectionMatrix, true) * m_camera.worldToCameraMatrix;
_2Ds.Add(_2d02);
m_manager.SetClip(_2Ds);
}
代码解释 : 构建了两张纹理蒙板,第一张蒙板将屏幕下半区域裁掉(屏幕以左下角为原点),第二张蒙板将上半屏幕 0.75 到 0.9 的区域裁掉,示例效果如下。
在实际使用时,可使用矩形框、三角形、笔刷、套索等各种圈选工具对蒙板对应位置进行选择。

2D 纹理裁剪效果
3D 几何体裁剪
用几何体的 worldToLocalMatrix 作为选区逆变换矩阵,通过 operation 组合并集(normal)与差集(erase),可通过 mode = SelectMode.invert 的条目实现反选。
public void on3DClip()
{
List<Data3D> _3Ds = new List<Data3D>();
var _mat1 = m_Box1.transform.worldToLocalMatrix;
var _mat2 = m_Box2.transform.worldToLocalMatrix;
var _mat3 = m_Box3.transform.worldToLocalMatrix;
var _mat4 = m_Clipsphere.transform.worldToLocalMatrix;
// Box 选区
Data3D _3d01 = new Data3D();
_3d01.type = ShapeType.box;
_3d01.operation = OperationMode.normal;
_3d01.mode = SelectMode.normal;
_3d01.invMat = _mat1;
_3Ds.Add(_3d01);
Data3D _3d02 = new Data3D();
_3d02.type = ShapeType.box;
_3d02.operation = OperationMode.normal;
_3d02.mode = SelectMode.normal;
_3d02.invMat = _mat2;
_3Ds.Add(_3d02);
// Sphere 选区
Data3D _3d04 = new Data3D();
_3d04.type = ShapeType.sphere;
_3d04.operation = OperationMode.normal;
_3d04.mode = SelectMode.normal;
_3d04.invMat = _mat4;
_3Ds.Add(_3d04);
// 擦除 Box/Sphere(差集)
Data3D _3d03 = new Data3D();
_3d03.type = ShapeType.box;
_3d03.operation = OperationMode.erase;
_3d03.mode = SelectMode.normal;
_3d03.invMat = _mat3;
_3Ds.Add(_3d03);
// 反选
//Data3D _3d05 = new Data3D();
//_3d05.mode = SelectMode.invert;
//_3Ds.Add(_3d05);
m_manager.SetClip(_3Ds);
}
代码解释 : 使用了 4 个几何体,其中 Box1、Box2、Sphere 为正常选择,而 Box3 为擦除选择,即将之前已选中的区域进行擦除。注释反选相关代码,效果示例如下:

3D 几何体裁剪效果
取消注释反选代码,即让反选生效时,效果示例如下,反选即是将之前所选的选区反置,这是一条指令,可不与操作数据关联。
特别需要注意的是,列表是有序的,不同的操作步骤会产生不同的最终效果。

3D 几何体+反选裁剪效果
混合模式裁剪
在同一个列表中混用 2D 纹理和 3D 几何体条目,每个条目通过 editType 指明处理方式。
public void onMixClip()
{
// --- 2D 选框准备 ---
// 纹理尺寸固定 1024*1024,其它属性保持与示例一致
Texture2D _texture = new Texture2D(1024, 1024, TextureFormat.R8, false);
_texture.filterMode = FilterMode.Point;
_texture.wrapMode = TextureWrapMode.Clamp;
int _count = 1024 * 1024;
int _half = _count / 2;
byte[] _values = new byte[_count];
for (int i = 0; i < _count; i++)
{
if (i < _half)
_values[i] = 128; // 128 表示默认,240 表示选中,0 表示裁剪
else
_values[i] = 0;
}
_texture.SetPixelData(_values, 0);
List<DataMix> _Mixs = new List<DataMix>();
DataMix _mix01 = new DataMix();
_mix01.editType = EditType.Edit2D;
_mix01.mv = m_camera.worldToCameraMatrix;
_mix01.texture = _texture;
_mix01.mode = SelectMode.normal;
_mix01.mvp = GL.GetGPUProjectionMatrix(m_camera.projectionMatrix, true) * m_camera.worldToCameraMatrix;
_Mixs.Add(_mix01);
// --- 3D 选框准备 ---
var _mat1 = m_Box1.transform.worldToLocalMatrix;
var _mat2 = m_Box2.transform.worldToLocalMatrix;
var _mat3 = m_Box3.transform.worldToLocalMatrix;
var _mat4 = m_Clipsphere.transform.worldToLocalMatrix;
// Box 选区
DataMix _mix02 = new DataMix();
_mix02.editType = EditType.Edit3D;
_mix02.type = ShapeType.box;
_mix02.operation = OperationMode.normal;
_mix02.mode = SelectMode.normal;
_mix02.invMat = _mat1;
_Mixs.Add(_mix02);
DataMix _mix03 = new DataMix();
_mix03.editType = EditType.Edit3D;
_mix03.type = ShapeType.box;
_mix03.operation = OperationMode.normal;
_mix03.mode = SelectMode.normal;
_mix03.invMat = _mat2;
_Mixs.Add(_mix03);
// Sphere 选区
DataMix _mix04 = new DataMix();
_mix04.editType = EditType.Edit3D;
_mix04.type = ShapeType.sphere;
_mix04.operation = OperationMode.normal;
_mix04.mode = SelectMode.normal;
_mix04.invMat = _mat4;
_Mixs.Add(_mix04);
// 擦除 Box/Sphere(差集)
DataMix _mix05 = new DataMix();
_mix05.editType = EditType.Edit3D;
_mix05.type = ShapeType.box;
_mix05.operation = OperationMode.erase;
_mix05.mode = SelectMode.normal;
_mix05.invMat = _mat3;
_Mixs.Add(_mix05);
// 反选(按需启用)
DataMix _mix06 = new DataMix();
_mix06.editType = EditType.Edit3D;
_mix06.type = ShapeType.box;
_mix06.mode = SelectMode.invert;
//_Mixs.Add(_mix06);
m_manager.SetClip(_Mixs);
}
代码解释 : 使用了 4 个几何体和 1 张 2D 纹理,可 2D 与 3D 混合处理,效果示例如下:

混合模式裁剪效果
步骤 8:退出裁剪
调用 QuitClipMode() 即可清除当前裁剪,恢复场景完整显示。
public void onClearClip()
{
m_manager.QuitClipMode();
}
关键步骤
对 3DGS 场景进行实时裁剪的关键步骤:
- 在
Load数据之前调用SetEditorMode(true)进入编辑状态。 - 在数据加载完成后(
onLoadCallback回调触发 或者通过按钮/事件触发)再调用SetClip。 - 根据需要选择 2D 纹理、3D 几何体或混合方式,并保证选区列表的添加顺序符合预期。
- 2D 蒙版纹理固定为 1024 × 1024、
R8单通道,按约定填充像素值(128默认保留、0裁剪)。 - 需要清除或切换裁剪时,先调用
QuitClipMode()。