Если вы хотите получить точку на определенном расстоянии от камеры, вам не нужен тест на попадание. Тест удара находит реальную поверхность мира перед камерой — если ваша камера не направлена на стену, которая идеально параллельна экрану устройства, вы всегда будете получать диапазон разных расстояний.
Если вам нужна точка на некотором расстоянии перед камерой, вам нужно получить положение/ориентацию камеры и применить к ней перевод (ваше предпочтительное расстояние). Затем, чтобы поместить туда содержимое SceneKit, используйте полученную матрицу, чтобы задать преобразование узла SceneKit.
Самый простой способ сделать это — придерживаться векторных/матричных типов SIMD, а не преобразовывать их в типы SCN. SceneKit добавляет в iOS 11 несколько новых средств доступа, поэтому вы можете напрямую использовать SIMD-типы.
Есть по крайней мере несколько способов сделать это, в зависимости от того, какой результат вы хотите.
Опция 1
// set up z translation for 20 cm in front of whatever
// last column of a 4x4 transform matrix is translation vector
var translation = matrix_identity_float4x4
translation.columns.3.z = -0.2
// get camera transform the ARKit way
let cameraTransform = view.session.currentFrame.camera.transform
// if we wanted, we could go the SceneKit way instead; result is the same
// let cameraTransform = view.pointOfView.simdTransform
// set node transform by multiplying matrices
node.simdTransform = cameraTransform * translation
Эта опция, использующая всю матрицу преобразования, не только помещает узел на постоянное расстояние перед вашей камерой, но и ориентирует его так, чтобы он указывал в том же направлении, что и ваша камера.
Вариант 2
// distance vector for 20 cm in front of whatever
let translation = float3(x: 0, y: 0, z: -0.2)
// treat distance vector as in camera space, convert to world space
let worldTranslation = view.pointOfView.simdConvertPosition(translation, to: nil)
// set node position (not whole transform)
node.simdPosition = worldTranslation
Этот параметр задает только положение узла, оставляя его ориентацию неизменной. Например, если вы разместите таким образом кучу кубов, перемещая камеру, все они будут выстроены в одном направлении, тогда как в варианте 1 все они будут в разных направлениях.
Выходя за рамки
Оба приведенных выше варианта основаны только на 3D-преобразовании камеры — они не учитывают положение 2D-касания на экране.
Если вы тоже хотите сделать это, у вас есть больше работы для вас — по сути, то, что вы делаете, — это тестирование ударов не по миру, а по виртуальной плоскости, которая всегда параллельна камере и находится на определенном расстоянии. далеко. Эта плоскость является поперечным сечением усеченной проекции камеры, поэтому ее размер зависит от того, на каком фиксированном расстоянии от камеры вы ее поместите. Точка на экране проецируется в точку на этой виртуальной плоскости, при этом ее положение на плоскости масштабируется пропорционально расстоянию от камеры (как на рисунке ниже):
Таким образом, чтобы отобразить касания на этой виртуальной плоскости, следует рассмотреть несколько подходов. (Я не даю код для них, потому что это не тот код, который я могу написать без тестирования, и я сейчас нахожусь в среде, свободной от Xcode.)
Создайте невидимый SCNPlane
, который является дочерним элементом узла pointOfView
вида, параллельного локальной плоскости xy и на некотором фиксированном расстоянии по оси z впереди. Используйте SceneKit hitTest
(не ARKit hit test!) для сопоставления касаний с эту плоскость и используйте worldCoordinates
результата теста попадания, чтобы расположить узлы SceneKit, которые вы перетаскиваете в свою сцену.
Используйте вариант 1 или вариант 2 выше, чтобы найти точку на некотором фиксированном расстоянии перед камерой (или всю матрицу преобразования, ориентированную в соответствии с камерой, перемещенную на некоторое расстояние вперед). Используйте метод SceneKit projectPoint
, чтобы найти нормализованное значение глубины Z для этой точки, затем вызовите unprojectPoint
с вашим 2D место касания и то же значение Z, чтобы получить трехмерное положение места касания с расстоянием до камеры. (Дополнительный код/указатели см. в моем аналогичном методе в этом ответе.)
31.10.2017