Как найти угол ч/б 3D-объекта и камеры устройства Vision Pro (вид пользователя)

У меня есть трехмерный объект (рекламный щит), который содержит рекламу на одной стороне, и я хотел бы определить, виден ли он (через камеру устройства машинного зрения) или нет для пользователя.

Вот код, который я использовал для расчета угла, но, похоже, он работает не так, как ожидалось, поскольку я получаю неправильное значение угла между камерой и рекламным щитом. Для этого я использовал формулу скалярного произведения. Но не уверен, правильно это или нет. Дайте мне знать, что не так в расчетах или альтернативный способ сделать это.

class cityViewModel {
    var billboard = Entity()
}

struct CityView: View {
    private var cameraTracker = VisionProCameraTracker()
    var model = cityViewModel()
    
    var body: some View {
        RealityView { content in
            // Add the initial RealityKit content
            if let scene = try? await Entity(named: "newcity", in: citywithAdBundle) {
                content.add(scene)
                if let entity = scene.findEntity(named: "billboard") {
                    model.billboard = entity
                    print("Billboard position \(entity.position)")
                }
                updatingSceneEventsWith(content)
            }
        }
        .task {
            await cameraTracker.runArSession()
        }
    }
    
    private func updatingSceneEventsWith(_ content: RealityViewContent) {
        _ = content.subscribe(to: SceneEvents.Update.self) { _ in
            Task {
                let cameraTransform = await cameraTracker.getTransform()
                let x2 = cameraTransform!.columns.3.x
                let x1 = await model.billboard.position.x
                let y2 = cameraTransform!.columns.3.y
                let y1 = await model.billboard.position.y
                let z2 = cameraTransform!.columns.3.z
                let z1 = await model.billboard.position.z
                let distance = sqrtf( (x2-x1) * (x2-x1) +
                                      (y2-y1) * (y2-y1) +
                                      (z2-z1) * (z2-z1) )
                var formatted = String(format: "%.2f m", arguments: [distance])
                print("Distance from camera to billboard is:", formatted)
                
                let ab = (x1 * x2) + (y1 * y2) + (z1 * z2)
                let magnitudeOfAd = sqrtf( (x1 * x1) + (y1 * y1) + (y1 * y1) )
                let magnititeOfCamera = sqrtf( (x2 * x2) + (y2 * y2) + (y2 * y2) )
                let cosTheta = ab / (magnitudeOfAd * magnititeOfCamera)
                let angleInRadians = acos(cosTheta)
                let angleInDegrees = angleInRadians * 180 / .pi
                formatted = String(format: "%.2f m", arguments: [angleInDegrees])
                print("Angle b/w camera and billboard is \(formatted) degrees.")
            }
        }
    }
}

@Observable class VisionProCameraTracker {
    let session = ARKitSession()
    let worldTracking = WorldTrackingProvider()
    
    func runArSession() async {
        Task {
            try? await session.run([worldTracking])
        }
    }

    func getTransform() async -> simd_float4x4? {
        guard let deviceAnchor = worldTracking.queryDeviceAnchor(atTimestamp: 4)
        else { return nil }
        let transform = deviceAnchor.originFromAnchorTransform
        return transform
    }
}

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
320
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Измерение угла между камерой и моделью

В SceneKit есть метод экземпляра isNode(_:insideFrustumOf:), который помогает нам получить значение true, если ограничивающая рамка искомого узла пересекает усеченную пирамиду перспективной камеры, определенную узлом POV. Подобный метод нам бы очень помог. К сожалению, RealityKit для VisionOS 1.2 не имеет такого полезного метода. Так что будем довольствоваться только тем, что имеем.

Чтобы решить вашу проблему самым простым способом, я использовал метод экземпляра orientation(relativeTo:). Этот код позволяет найти угол отклонения 0...45 градусов по осям Y и X в любом направлении. Настройте отслеживание привязки устройства ARKit так же, как описано в вашем вопросе (поскольку мы будем использовать объект класса VisionProCameraTracker). Затем создайте объект привязки и передайте ему матрицу преобразования привязки устройства. Теперь вы можете измерить угол.

Вот мой код:

import SwiftUI
import RealityKit
import ARKit

struct ContentView : View {
    let vpct = VisionProCameraTracker()
    let anchor = AnchorEntity(.head, trackingMode: .continuous)
    @State var billboard = Entity()
    
    var body: some View {
        RealityView { rvc in
            billboard = try! await Entity(named: "Billboard")
            rvc.add(billboard)
            rvc.add(anchor)
            
            let _ = rvc.subscribe(to: SceneEvents.Update.self) { _ in
                anchor.transform.matrix = vpct.getTransform()
                
                let radAngle = anchor.orientation(relativeTo: billboard).angle
                
                var radToDeg: Any = Int(radAngle * (180/Float.pi)) % 240
                
                if (radToDeg as! Int) > 45 {
                    radToDeg = "Angle is greater than 45 degrees"
                }
                if anchor.position.z < billboard.position.z {
                    radToDeg = "Billboard is not visible"
                }
                print(radToDeg)
            }
        }
        .task { await vpct.runArSession() }
    }
}


Если вам нужно разделить значения углов X и Y, чтобы определить не только величину угла, но и его вектор, используйте этот код:

let _ = rvc.subscribe(to: SceneEvents.Update.self) { _ in
    anchor.transform.matrix = vpct.getTransform()
            
    let xVec = anchor.orientation(relativeTo: billboard).vector.x
    let yVec = anchor.orientation(relativeTo: billboard).vector.y
    let magnitude = anchor.orientation(relativeTo: billboard).angle.magnitude
               
    let radAngleX = xVec * magnitude  // radians
    let radAngleY = yVec * magnitude  // radians
            
    let radToDeg_X = Int(radAngleX * (180/Float.pi)) % 91
    let radToDeg_Y = Int(radAngleY * (180/Float.pi)) % 91

    print("x:", radToDeg_X, "y:", radToDeg_Y)
}

Если вам интересно, что такое параметры real и imaginary кватерниона, прочтите этот пост.

В RealityKit для VisionOS преобразование AnchorEntity(.head) скрыто по дизайну, поэтому я использовал преобразование привязки устройства. Кроме того, я использовал оператор модуля ... % 240 как способ избавиться от значений, находящихся в диапазоне 0...180 за спиной пользователя.

Andy Jazz 17.04.2024 06:35

Привет @Andy Jazz: Если мне нужно определить, виден ли этот рекламный щит или нет, как я могу этого добиться? Например, если пользователь приблизится к рекламному щите и начнет вращаться влево, то после некоторого поворота рекламный щит не будет виден, как показано во вложении.

Santrack 17.04.2024 07:52

В операторе if-else укажите максимальный угол отклонения, при котором весь рекламный щит не будет виден (выберите любой желаемый угол в градусах: 45, или 50, или 60). Вы можете создавать различные условия ограничения угла отклонения в зависимости от расстояния от камеры до рекламного щита. Если рекламный щит находится позади вас, приложение отобразит сообщение: «Билборд не виден».

Andy Jazz 17.04.2024 08:06

Если пользователь перемещается в крайнее левое или правое положение и не меняет угол камеры, рекламный щит не виден. Как определить этот вариант использования?

Santrack 17.04.2024 08:18

В этом случае сравните диапазон position.x и диапазон position.y для камеры и рекламного щита.

Andy Jazz 17.04.2024 08:42

Привет @Andy Jazz: я ​​пытался узнать, по какой оси вращается камера устройства, но не нашел правильного пути. Есть ли способ определить, по какой оси вращается камера?

Santrack 29.04.2024 18:49

Другие вопросы по теме