Реалізація AR-розміщення 3D-об'єктів на площині
Поставити 3D-модель на виявлену площину — це не завдання одного вечора, якщо мова йде про стабільне production-рішення. Об'єкт повинен стояти рівно при русі камери, не «плити» при зміні освітлення, адекватно масштабуватися та не утопати в поверхні на 5 сантиметрів. Кожен з цих пунктів — окремий технічний вопрос.
Формати 3D-моделей та оптимізація для AR
Стандарт для мобільного AR — USDZ (iOS/RealityKit) та GLB/GLTF (ARCore/cross-platform). Типова помилка: брати модель з Blender або 3ds Max без оптимізації та намагатися грузити її в AR. 500k трикутники полігонів, текстури 4096×4096 без mip-mapping — гарантований FPS провал на iPhone 12 і нижче.
Цільові параметри для мобільного AR:
| Тип об'єкта | Полігони | Текстури | Розмір GLB |
|---|---|---|---|
| Невеликий предмет (стіл, світильник) | до 30k | 1024×1024 | до 5 МБ |
| Середній об'єкт (диван, стіл) | до 80k | 2048×2048 | до 15 МБ |
| Крупний (набір меблів, кухня) | до 200k | 2048×2048 | до 40 МБ |
Компресія: KTX2 + Basis Universal для GLB, HEIC-текстури в USDZ. В RealityKit — Reality Composer Pro (Xcode 15+) для запекання фізичних матеріалів PBR прямо в .reality формат.
Anchor, Raycast та чому hitTest застарів
В ARKit 4+ та ARCore 1.18+ переважний спосіб визначення точки розміщення — Raycast, а не застарілий hitTest. Різниця суттєва:
ARSession.raycast(from:allowing:alignment:) повертає список ARRaycastResult з target: .estimatedPlane або .existingPlaneGeometry. existingPlaneGeometry точніше — використовується геометрія вже детектованої площини. estimatedPlane працює там, де площина ще не зафіксована.
guard let query = arView.makeRaycastQuery(
from: arView.center,
allowing: .existingPlaneGeometry,
alignment: .horizontal
) else { return }
let results = arView.session.raycast(query)
if let first = results.first {
placeEntity(at: first.worldTransform)
}
Для ARCore — Frame.hitTest() все ще документується, але Session.createRaycastQuery() + Frame.raycast() дає більш стабільний результат на краях площин.
Стабілізація позиції об'єкта
Після першого розміщення об'єкт не повинен «гуляти» при русі камери. Стандартний підхід — привязка до AnchorEntity:
let anchor = ARAnchor(transform: worldTransform)
arView.session.add(anchor: anchor)
let anchorEntity = AnchorEntity(anchor: anchor)
anchorEntity.addChild(modelEntity)
arView.scene.addAnchor(anchorEntity)
Без явного ARAnchor об'єкт продовжує оновлювати позицію разом з оновленням площини. Це особливо помітно перші 10-15 секунд сесії, коли ARKit активно уточнює геометрію площини.
Тіні та фізичне освітлення
AR-об'єкт без тіні виглядає «летючим». RealityKit рендерит contact shadow автоматично для .castsShadow = true. В SceneKit — SCNLight з типом .ambient + directional light з castsShadow = true.
ARKit Environment Texturing (доступно з A12+): ARWorldTrackingConfiguration.environmentTexturing = .automatic — ARKit будує environment map з камери та застосовує його до PBR-матеріалів. Металічні та глянцеві поверхні починають відображати реальне оточення. Без цього хромований предмет виглядає пластиковим.
Переміщення та обертання після розміщення
Drag gesture для переміщення об'єкта — через ARView.installGestures(.translation, for: entity) в RealityKit. Для кастомної поведінки — UILongPressGestureRecognizer + continuous raycast під час руху пальця.
Обертання навколо вертикальної осі — ARView.installGestures(.rotation, for: entity) або ручне через UIPanGestureRecognizer з simd_quatf(angle:axis:). Обов'язково обмежити вісь обертання тільки Y — іначе об'єкт почне «заваливатися» при неточному жесті.
Терміни
Базове розміщення одного об'єкта з raycast, anchor та тінню — 4-6 днів. Мультиобъектне розміщення, переміщення/обертання, підтримка кількох форматів моделей — 10-15 днів. Вартість розраховується індивідуально.







