как проверить пересечение луча с объектом в ARCore

Есть ли способ проверить, коснулся ли я объекта на экране? Насколько я понимаю, класс HitResult позволяет мне проверить, коснулся ли я распознанной и нанесенной на карту поверхности. Но я хочу проверить, что я прикоснулся к объекту, который находится на этой поверхности.


person Fixus    schedule 02.09.2017    source источник


Ответы (2)


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

Однако я могу помочь с получением самого луча (будет добавлен в HelloArActivity):

/** 
 * Returns a world coordinate frame ray for a screen point.  The ray is
 * defined using a 6-element float array containing the head location
 * followed by a normalized direction vector.
 */
float[] screenPointToWorldRay(float xPx, float yPx, Frame frame) {
    float[] points = new float[12];  // {clip query, camera query, camera origin}
    // Set up the clip-space coordinates of our query point
    // +x is right:
    points[0] = 2.0f * xPx / mSurfaceView.getMeasuredWidth() - 1.0f;
    // +y is up (android UI Y is down):
    points[1] = 1.0f - 2.0f * yPx / mSurfaceView.getMeasuredHeight(); 
    points[2] = 1.0f; // +z is forwards (remember clip, not camera)
    points[3] = 1.0f; // w (homogenous coordinates)

    float[] matrices = new float[32];  // {proj, inverse proj}
    // If you'll be calling this several times per frame factor out
    // the next two lines to run when Frame.isDisplayRotationChanged().
    mSession.getProjectionMatrix(matrices, 0, 1.0f, 100.0f);
    Matrix.invertM(matrices, 16, matrices, 0);
    // Transform clip-space point to camera-space.
    Matrix.multiplyMV(points, 4, matrices, 16, points, 0);
    // points[4,5,6] is now a camera-space vector.  Transform to world space to get a point
    // along the ray.
    float[] out = new float[6];
    frame.getPose().transformPoint(points, 4, out, 3);
    // use points[8,9,10] as a zero vector to get the ray head position in world space.
    frame.getPose().transformPoint(points, 8, out, 0);
    // normalize the direction vector:
    float dx = out[3] - out[0];
    float dy = out[4] - out[1];
    float dz = out[5] - out[2];
    float scale = 1.0f / (float) Math.sqrt(dx*dx + dy*dy + dz*dz);
    out[3] = dx * scale;
    out[4] = dy * scale;
    out[5] = dz * scale;
    return out;
}

Если вы вызываете это несколько раз за кадр, см. Комментарий о вызовах getProjectionMatrix и invertM.

person Ian M    schedule 03.09.2017
comment
прежде всего спасибо за ответ. Меня беспокоит то, что в Интернете я могу найти множество различных примеров пересечения лучей с простыми объектами, а что с более сложными объектами? - person Fixus; 04.09.2017

Помимо выбора мыши с помощью Ray Casting, см. Ответ Яна, другой часто используемый метод - это буфер выбора, подробно объясненный (с кодом C ++) здесь

Уловка 3D-выбора очень проста. Мы прикрепим индекс к каждому треугольнику и заставим FS выводить индекс треугольника, которому принадлежит пиксель. Конечным результатом является то, что мы получаем «цветной» буфер, который на самом деле не содержит цветов. Вместо этого для каждого пикселя, покрытого каким-либо примитивом, мы получаем индекс этого примитива. При щелчке мышью по окну мы считываем этот индекс (в соответствии с положением мыши) и окрашиваем выбранный треугольник в красный цвет. Комбинируя буфер глубины в процессе, мы гарантируем, что когда несколько примитивов перекрывают один и тот же пиксель, мы получим индекс самого верхнего примитива (ближайшего к камере).

Итак, в двух словах:

  1. Каждому методу рисования объекта требуется постоянный индекс и логическое значение, определяющее, отображает ли это рисование буфер пикселей или нет.
  2. Метод визуализации преобразует индекс в оттенки серого, и сцена визуализируется.
  3. После того, как весь рендеринг будет завершен, получите цвет пикселя в позиции касания GL11.glReadPixels(x, y, /*the x and y of the pixel you want the colour of*/). Затем преобразуйте цвет обратно в индекс, а индекс обратно в объект. Вуаля, у вас есть объект, на который щелкнули мышью.

Честно говоря, для мобильного варианта использования вам, вероятно, следует прочитать прямоугольник 10x10, выполнить итерацию по нему и выбрать первый найденный не фоновый цвет, потому что касания никогда не бывают такими точными.

Этот подход работает независимо от сложности ваших объектов.

Пример буфера кликов

person PhilLab    schedule 04.09.2017
comment
да, но он работает с простыми объектами с парой цветов. Когда вы находитесь на улице, и вы будете использовать зеленый цвет для объекта, это легко для неправильной интерпретации (например, трава, дерево). - person Fixus; 04.09.2017
comment
Нет, вы можете визуализировать всю сцену с неправильными цветами, то есть без освещения, без текстур ... только с фиксированным цветом для каждого объекта (см. Скриншот, который я добавил выше). Это изображение никогда не отображается пользователю и используется только для определения щелчка. - person PhilLab; 04.09.2017
comment
Так у меня было бы 2 слоя? Один с неправильными цветами, а второй виден пользователю? - person Fixus; 04.09.2017
comment
Да, если слой - это неформальная фраза (формальная могла бы быть буфером). А слой с неправильным цветом отображается только при нажатии. - person PhilLab; 04.09.2017