边角点吸附开发指南
LCC Unity SDK 支持对 3DGS 表面进行边缘、角点检测和吸附预览,可用于测量、标注、放置等需要精确定位的交互场景。下面介绍实现边缘/角点检测及吸附的完整步骤。
说明:
吸附功能(边缘/角点检测、吸附预览、带吸附的射线检测)仅在 PC / Mac 平台支持,移动端 / VR 平台调用相关 API 无效并会输出警告。
步骤 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 数据坐标系」。
步骤 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,添加 Snap Frame Capture Feature。

在 URP Renderer 中添加 Snap Frame Capture Feature
步骤 3:编写脚本开启吸附功能
新建一个 C# 脚本,命名为 Snap.cs,代码如下:
using LCCCore;
using UnityEngine;
public class Snap : MonoBehaviour
{
public LCCManager m_manager;
public string m_FilePath;
private LCCCore.Renderer m_renderer;
private bool m_loaded = false;
private Camera m_camera;
void Start()
{
m_camera = Camera.main;
m_manager.SetPlatformType(PlatformType.PC);
Debug.Log("Started ");
m_renderer = m_manager.GetRender(this.transform);
m_renderer.Load(m_FilePath, onProgress, onComplete);
}
private void onProgress(float _progress)
{
Debug.Log($"[LCCRenderer] file load progress: {_progress:P1}");
}
private void onComplete()
{
Debug.Log($"[LCCRenderer] file loaded");
m_loaded = true;
// 开启边缘/角点检测与吸附预览
m_manager.SetSnapEnabled(true);
m_manager.SetSnapPreviewEnabled(true);
}
private void Update()
{
if (!m_loaded) return;
// 根据当前鼠标位置实时更新吸附预览
m_manager.UpdateSnapPreview(Input.mousePosition, m_camera);
if (Input.GetMouseButtonDown(1))
{
m_manager.RaycastWithSnap(Input.mousePosition, m_camera, out HitResult result);
if (result.isHit)
{
GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
sphere.transform.position = result.hitPos;
sphere.GetComponent<UnityEngine.Renderer>().material.color = Color.red;
sphere.transform.localScale = new Vector3(0.1f, 0.1f, 0.1f);
}
}
}
}
注意:
边缘/角点检测(
SetSnapEnabled)与吸附预览(SetSnapPreviewEnabled)需要在 LCC 场景加载完成后开启,因此二者都在onComplete回调中调用。吸附预览还需要在Update中通过UpdateSnapPreview持续更新。
上述示例演示了以下要点:
- 开启边缘、角点检测:
m_manager.SetSnapEnabled(true) - 开启吸附预览:
m_manager.SetSnapPreviewEnabled(true) - 实时更新吸附预览:
m_manager.UpdateSnapPreview(Input.mousePosition, m_camera) - 在吸附点进行射线检测并创建碰撞点小球:
m_manager.RaycastWithSnap(...),命中后在result.hitPos处创建红色小球
步骤 4:挂载脚本并设置数据路径
- 将
Snap.cs脚本挂载到 Hierarchy 窗口中的 LCCRenderer 游戏对象上。 - 在 Inspector 窗口中,将 LCCManager 游戏对象拖动到脚本的 Manager 属性上。
- 在脚本的 File Path 属性中填写 LCC 文件的路径地址。
步骤 5:运行
在 Game 窗口中点击 Play 按钮,即可看到实时检测到的边缘、角点以及吸附目标的预览图标。移动鼠标时预览图标会实时跟随最近的吸附目标;单击鼠标右键会在吸附点处进行射线检测并创建一个红色小球。

运行后实时检测到的边缘、角点及吸附预览图标
常见问题:关闭兼容模式
如果 Unity Console 窗口报错且没有出现吸附预览图标,请关闭兼容模式:通过 Edit → Project Settings → Graphics 打开设置面板,选择 URP 选项卡,不要勾选 Render Graph → Compatibility Mode (Render Graph Disabled) 后的多选框。

不要勾选 Render Graph 兼容模式(Render Graph Disabled)
关键步骤
实现边缘/角点检测及吸附的关键步骤:
- 确保在 URP Renderer 中添加了 Snap Frame Capture Feature 渲染特性。
- 确保在 LCC 场景加载完成后通过代码开启了边缘/角点检测功能(
SetSnapEnabled(true))和吸附预览功能(SetSnapPreviewEnabled(true))。 - 确保在
Update中实时调用UpdateSnapPreview更新预览图标。
吸附相关 API
LCC Unity SDK 通过 LCCManager 提供了以下吸附相关 API。它们仅在 PC / Mac 平台支持,移动端 / VR 平台调用无效并输出警告;且都需要在 LCC 场景加载完成后调用。
SetSnapEnabled
// 返回值:void
public void SetSnapEnabled(bool enable)
开关吸附功能。启用后 RaycastWithSnap 会自动搜索附近的边缘/角点进行吸附。
| 参数 | 类型 | 说明 |
|---|---|---|
enable | bool | 是否启用吸附 |
调用示例:
m_manager.SetSnapEnabled(true);
说明:
- 默认关闭。
- 仅 PC / Mac 平台支持,移动端 / VR 调用无效并输出警告。
- 启用后会分配 GPU 资源(EdgeDetect / CornerDetect / SnapSearch )。
SetSnapRadius
// 返回值:void
public void SetSnapRadius(float pixels)
设置吸附搜索半径。在此像素范围内搜索最近的边缘/角点。
| 参数 | 类型 | 说明 |
|---|---|---|
pixels | float | 吸附搜索半径(像素) |
调用示例:
m_manager.SetSnapRadius(30f);
说明:
- 默认 20 像素。
- 值越大搜索范围越广,但可能吸附到非预期目标。
- 仅 PC / Mac 平台支持。
SetEdgeThreshold
// 返回值:void
public void SetEdgeThreshold(float threshold)
设置 Sobel 边缘检测的阈值。值越小检测到的边缘越多。
| 参数 | 类型 | 说明 |
|---|---|---|
threshold | float | 边缘检测阈值 |
调用示例:
m_manager.SetEdgeThreshold(0.2f);
说明:
- 默认 0.3。
- 建议取值范围
[0.1, 0.8]。 - 值过小会产生大量噪声边缘。
- 仅 PC / Mac 平台支持。
SetSnapPreviewEnabled
// 返回值:void
public void SetSnapPreviewEnabled(bool enable)
开关吸附预览功能。启用后鼠标移动时实时显示最近的吸附目标指示器。
| 参数 | 类型 | 说明 |
|---|---|---|
enable | bool | 是否启用吸附预览 |
调用示例:
m_manager.SetSnapPreviewEnabled(true);
说明:
- 需先调用
SetSnapEnabled(true)。 - 预览会持续消耗 GPU 资源(每帧检测)。
- 仅 PC / Mac 平台支持。
UpdateSnapPreview
// 返回值:void
public void UpdateSnapPreview(Vector2 mousePos, Camera cam)
更新吸附预览状态。在鼠标移动时调用,更新预览指示器位置。
| 参数 | 类型 | 说明 |
|---|---|---|
mousePos | Vector2 | 当前鼠标屏幕坐标 |
cam | Camera | 相机 |
调用示例:
void Update()
{
m_manager.UpdateSnapPreview(Input.mousePosition, Camera.main);
}
说明:
- 需先调用
SetSnapPreviewEnabled(true)。 - 建议在
Update中持续调用。 - 仅 PC / Mac 平台支持。
GetSnapPreviewState
// 返回值:SnapPreviewState(screenPos 为屏幕坐标)
public SnapPreviewState GetSnapPreviewState()
获取当前吸附预览状态,包括是否有吸附目标、目标类型、目标屏幕位置和到鼠标的像素距离。
调用示例:
var state = m_manager.GetSnapPreviewState();
if (state.hasTarget)
{
// state.snapType: Corner / Edge
// state.screenPos: 吸附目标屏幕坐标
// state.distanceToMouse: 像素距离
}
说明:
- 需先调用
SetSnapPreviewEnabled(true)和UpdateSnapPreview。 - 无目标时返回
default(SnapPreviewState)。 screenPos是屏幕坐标,不是世界坐标;需要世界坐标时请用RaycastWithSnap取HitResult.hitPos。- 仅 PC / Mac 平台支持。
RaycastWithSnap
// 返回值:bool(true = 命中)
public bool RaycastWithSnap(Vector2 screenPos, Camera cam, out HitResult result)
带吸附功能的射线检测。先进行普通 Raycast,再在命中点附近搜索边缘/角点进行吸附。
| 参数 | 类型 | 说明 |
|---|---|---|
screenPos | Vector2 | 屏幕坐标 |
cam | Camera | 相机 |
result | out HitResult | 命中结果(含吸附信息) |
调用示例:
if (m_manager.RaycastWithSnap(Input.mousePosition, m_camera, out HitResult hit))
{
// hit.snapType: Corner / Edge / None
// hit.hitPos: 吸附后的世界坐标
// hit.originalHitPos: 吸附前的原始命中点
}
说明:
- 需先调用
SetSnapEnabled(true)。 - 吸附优先级:Corner > Edge > None。
- 本入口同步阻塞主线程(等待 GPU 回读结果),建议仅在交互事件(如点击)时调用,不要每帧调用。
- 预览模式(
SetSnapPreviewEnabled(true))下若已有吸附目标,则直接复用预览缓存,不再重新计算;否则走正常吸附流程。 - 仅 PC / Mac 平台支持。
相关数据结构
SnapType
public enum SnapType { None, Edge, Corner }
吸附目标类型:无 / 边缘 / 角点。
SnapPreviewState
public struct SnapPreviewState {
public bool hasTarget; // 是否有吸附目标
public SnapType snapType; // None / Edge / Corner
public Vector2 screenPos; // 吸附目标屏幕坐标(非世界坐标)
public float distanceToMouse; // 到鼠标位置的像素距离
}
⚠️
screenPos是屏幕像素坐标,不是世界坐标。需要世界坐标时请用RaycastWithSnap取HitResult.hitPos。
HitResult
public struct HitResult {
public bool isHit; // 是否命中
public float distance; // 距离
public Vector3 hitPos; // 命中点世界坐标(字段名为 hitPos,非 position)
public Vector3 normal; // 命中法线
public float hitAlpha; // 被击中椭球的 Alpha
public float dist2Origin; // 到射线源的距离
public float scale; // 椭球 scale
public SnapType snapType; // 吸附类型(None / Edge / Corner)
public Vector3 originalHitPos; // 吸附前原始命中点
}
⚠️ 字段命名差异:命中点世界坐标的字段是
hitPos(而非position),调用方代码请按实际字段名访问。