Руководство по разработке привязки к рёбрам и углам
LCC Unity SDK поддерживает обнаружение рёбер и углов и предпросмотр привязки на поверхностях 3DGS, что можно использовать в сценариях взаимодействия, требующих точного позиционирования, таких как измерение, аннотирование и размещение. Ниже описаны полные шаги по реализации обнаружения рёбер/углов и привязки.
Примечание:
Функция привязки (обнаружение рёбер/углов, предпросмотр привязки и рейкастинг с привязкой) поддерживается только на платформах PC / Mac. Вызов связанных API на платформах mobile / VR не даёт эффекта и выводит предупреждение.
Шаг 1: Создайте сцену и GameObject
Создайте совершенно новую сцену в Unity. Unity по умолчанию создаёт Main Camera и Directional Light.
Настройте Main Camera
Выберите Main Camera в окне Hierarchy и задайте его свойства в окне Inspector, как показано ниже.

Настройка свойств Main Camera
Создайте LCCManager
Щёлкните правой кнопкой мыши в пустой области окна Hierarchy, выберите Create Empty во всплывающем меню, чтобы создать пустой GameObject, назовите его LCCManager, нормализуйте его Transform и добавьте к нему скрипт LCCManager через Add Component.

Создание GameObject LCCManager и добавление скрипта LCCManager
Создайте LCCRenderer
Щёлкните правой кнопкой мыши в пустой области окна Hierarchy, выберите Create Empty, чтобы создать пустой GameObject, назовите его LCCRenderer и задайте его Transform значения Rotation.x = -90 и Scale.x = -1.

Создание LCCRenderer и настройка его Transform (Rotation.x = -90, Scale.x = -1)
Примечание:
Настройки Transform GameObject LCCRenderer (Rotation.x = -90, Scale.x = -1) выравнивают его с системой координат данных LCC. Подробнее о выравнивании системы координат см. в разделе «Система координат данных LCC» документа Введение в Unity SDK.
Шаг 2: Добавьте Render Feature
- В строке меню Unity выберите File → Project Settings, выберите Graphics слева и подтвердите текущий Default Render Pipeline Asset. В этом примере используется URP (Universal Render Pipeline Asset).

Подтверждение Default Render Pipeline Asset в настройках Graphics
Щёлкните по этому Asset, найдите соответствующий файл ассета в окне Project и найдите связанный с ним Renderer (
URP_Renderer.assetв этом примере).Выберите этот файл ассета Renderer, нажмите Add Renderer Feature в окне Inspector и добавьте Snap Frame Capture Feature.

Добавление Snap Frame Capture Feature в URP Renderer
Шаг 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("Запущено ");
m_renderer = m_manager.GetRender(this.transform);
m_renderer.Load(m_FilePath, onProgress, onComplete);
}
private void onProgress(float _progress)
{
Debug.Log($"[LCCRenderer] прогресс загрузки файла: {_progress:P1}");
}
private void onComplete()
{
Debug.Log($"[LCCRenderer] файл загружен");
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. Предпросмотр привязки также нужно непрерывно обновлять черезUpdateSnapPreviewвUpdate.
Приведённый выше пример демонстрирует следующие моменты:
- Включение обнаружения рёбер и углов:
m_manager.SetSnapEnabled(true) - Включение предпросмотра привязки:
m_manager.SetSnapPreviewEnabled(true) - Обновление предпросмотра привязки в реальном времени:
m_manager.UpdateSnapPreview(Input.mousePosition, m_camera) - Рейкастинг в точке привязки и создание сферы в точке попадания:
m_manager.RaycastWithSnap(...); при попадании в точкеresult.hitPosсоздаётся красная сфера
Шаг 4: Прикрепите скрипт и задайте путь к данным
- Прикрепите скрипт
Snap.csк GameObject LCCRenderer в окне Hierarchy. - В окне Inspector перетащите GameObject LCCManager на свойство Manager скрипта.
- Заполните путь к файлу LCC в свойстве File Path скрипта.
Шаг 5: Запустите
Нажмите кнопку Play в окне Game, чтобы увидеть обнаруженные в реальном времени рёбра и углы и значок предпросмотра цели привязки. При движении мыши значок предпросмотра следует за ближайшей целью привязки в реальном времени; щелчок правой кнопкой выполняет рейкастинг в точке привязки и создаёт красную сферу.

Рёбра, углы и значок предпросмотра привязки, обнаруженные в реальном времени после запуска
FAQ: Отключите режим совместимости
Если окно Console Unity сообщает об ошибках и значок предпросмотра привязки не появляется, отключите режим совместимости: откройте панель настроек через Edit → Project Settings → Graphics, выберите вкладку URP и не отмечайте флажок после Render Graph → Compatibility Mode (Render Graph Disabled).

Не отмечайте режим совместимости Render Graph (Render Graph Disabled)
Ключевые шаги
Ключевые шаги для реализации обнаружения рёбер/углов и привязки:
- Убедитесь, что Snap Frame Capture Feature добавлен в URP Renderer.
- Убедитесь, что обнаружение рёбер/углов (
SetSnapEnabled(true)) и предпросмотр привязки (SetSnapPreviewEnabled(true)) включены в коде после завершения загрузки LCC-сцены. - Убедитесь, что
UpdateSnapPreviewвызывается в реальном времени вUpdateдля обновления значка предпросмотра.
API, связанные с привязкой
LCC Unity SDK предоставляет следующие API, связанные с привязкой, через LCCManager. Они поддерживаются только на платформах PC / Mac — вызов на платформах mobile / VR не даёт эффекта и выводит предупреждение — и все они должны вызываться после завершения загрузки LCC-сцены.
SetSnapEnabled
// Возвращаемое значение: void
public void SetSnapEnabled(bool enable)
Переключает функцию привязки. При включении RaycastWithSnap автоматически ищет ближайшие рёбра/углы для привязки.
| Параметр | Тип | Описание |
|---|---|---|
enable | bool | Включать ли привязку |
Пример использования:
m_manager.SetSnapEnabled(true);
Примечания:
- Отключено по умолчанию.
- Поддерживается только на PC / Mac; вызов на mobile / 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)
Задаёт порог обнаружения рёбер Собеля. Чем меньше значение, тем больше рёбер обнаруживается.
| Параметр | Тип | Описание |
|---|---|---|
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: угол / ребро
// 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: угол / ребро / нет
// hit.hitPos: мировая позиция после привязки
// hit.originalHitPos: исходная точка попадания до привязки
}
Примечания:
- Требует предварительного вызова
SetSnapEnabled(true). - Приоритет привязки: угол > ребро > нет.
- Эта точка входа синхронно блокирует основной поток (ожидая результат чтения с GPU); рекомендуется вызывать её только на событиях взаимодействия (например, клики), а не каждый кадр.
- В режиме предпросмотра (
SetSnapPreviewEnabled(true)), если цель привязки уже существует, кэш предпросмотра используется повторно без пересчёта; иначе используется обычный поток привязки. - Поддерживается только на PC / Mac.
Связанные структуры данных
SnapType
public enum SnapType { None, Edge, Corner }
Тип цели привязки: нет / ребро / угол.
SnapPreviewState
public struct SnapPreviewState {
public bool hasTarget; // есть ли цель привязки
public SnapType snapType; // нет / ребро / угол
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; // альфа попавшего эллипсоида
public float dist2Origin; // расстояние до начала луча
public float scale; // масштаб эллипсоида
public SnapType snapType; // тип привязки (нет / ребро / угол)
public Vector3 originalHitPos; // исходная точка попадания до привязки
}
⚠️ Различие в именах полей: поле для мировой позиции точки попадания называется
hitPos(а неposition); вызывающий код должен обращаться к нему по фактическому имени поля.