Russian Qt Forum

Qt => OpenGL => Тема начата: spirits25 от Июнь 20, 2013, 17:25



Название: [РЕШЕНО] получение координат точки на сфере
Отправлено: spirits25 от Июнь 20, 2013, 17:25
Всем привет!

Есть сфера (QGLSceneNode), центр которой совпадает с центром всей сцены. Необходимо получить декартовые координаты точки, по которуй произведён клик мышью. Какой алгоритм используется? необходимо через registerObject регистрировать объект и при его сигнале clicked определять эти координаты, по которым произведён клик?

Я тут как в тёмном лесу=) Спасибо за любую помощь (ссылки, алгоритм, код).


Название: Re: получение координат точки на сфере
Отправлено: Igors от Июнь 20, 2013, 18:06
Собственно сама задача простейшая, но откопать исходные данные в куче навороченных классов (которыми Вы наверняка пользуетесь) - я не берусь, копайте сами. Нужно иметь

- focal length в пикселях (не в мм) ИЛИ угол зрения. Ну и размеры рендеримой картинки (с этим проблем быть не должно)

Найдете - тогда поговорим


Название: Re: получение координат точки на сфере
Отправлено: spirits25 от Июнь 21, 2013, 10:34
Нужно иметь

- focal length в пикселях (не в мм) ИЛИ угол зрения. Ну и размеры рендеримой картинки (с этим проблем быть не должно)

Найдете - тогда поговорим

Угол зрения есть - есть значение с которого "камера" смотрит на объект. А размеры картинки - это сфера, размер известен, но камера постоянно то приближается, то удаляется.


Название: Re: получение координат точки на сфере
Отправлено: Igors от Июнь 21, 2013, 11:06
Угол зрения есть - есть значение с которого "камера" смотрит на объект. А размеры картинки - это сфера, размер известен, но камера постоянно то приближается, то удаляется.
Объект здесь совершенно ни при чем. Кликая на пиксель картинки (пусть даже пустой) Вы можете получить луч/вектор из камеры (0, 0, 0) в бесконечность. Он может пересекать сколько угодно объектов или ничего.
Цитировать
dir.x = pixel.x - center.x;   // X камеры слева направо
dir.y = center.y - pixel.y;   // Y камеры снизу вверх, а у пикселей наоборот
dir.z = -focal_length_pixels;  // Z камеры "на нас" (правая тройка)
Вот этот focal_length_pixels - параметр перспективной камеры который должен быть так или иначе задан. Например угол зрения alpha = 28 градусов по горизонтали, тогда
Цитировать
focal_length_pixels = image_width / 2 / tan(alpha * M_PI / 180 / 2);
Ну или image_height если по вертикали. Или может задаваться просто коэффициентои K, тогда
Цитировать
focal_length_pixels = image_width * K;
Как там в крутых классах - не знаю, там умудряются задрочить простейшие вещи  :'( Но то что этот параметр есть (или вычисляется из других) - это стопудово, без него перспективной камеры не существует.


Название: Re: получение координат точки на сфере
Отправлено: spirits25 от Июнь 21, 2013, 11:23
Здесь (в Qt3D) у класса QGLView есть метод camera(), который возвращает объект камеры QGLCamera. Она смотрит в центр координат (где у меня и центр сферы).

У объекта камеры есть метод eye, который возвращает QVector3D
Цитировать
This property holds the position of the viewer's eye. The default value is (0, 0, 10).
По этому методу и определяется положение камеры относительно центра координат. Например, имею QVector3D(0, 0, 10), немного смещаю камеру - получаю QVector3D(-3.39115, -0.237631, 9.40444). Из этого я и высчитываю на какие координаты моей сферы в данный момент смотрит камера. Это те данные?

Но я всё же не понимаю, что такое image_width/image_height. - это просто сумма всех изображений на сфере (в пикселях)? Если да, то я их могу посчитать для каждой ситуации (это значение меняется при приближении и удалении камеры).


Название: Re: получение координат точки на сфере
Отправлено: spirits25 от Июнь 21, 2013, 11:40
Ещё с перспективой связан метод viewSize(), который соответственно возвращает размер области, которая в данный момент отображена (вмещается в обзор камеры).

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


Название: Re: получение координат точки на сфере
Отправлено: Igors от Июнь 21, 2013, 12:21
Просто сердце кровью обливается глядя как Вы неумело насилуете 3D  :'(  Ну вот Вы видели что футболиста могут показывать крупным планом (как этот дятел по мячику бьет) - но ведь камера на поле не выезжает. Просто маленький угол зрения (большой focal length, больше расстояние от пленки до затвора). А др камера может показать все поле (большой угол зрения).

У объекта камеры есть метод eye, который возвращает QVector3D
Цитировать
This property holds the position of the viewer's eye. The default value is (0, 0, 10).
По этому методу и определяется положение камеры относительно центра координат. Например, имею QVector3D(0, 0, 10), немного смещаю камеру - получаю QVector3D(-3.39115, -0.237631, 9.40444). Из этого я и высчитываю на какие координаты моей сферы в данный момент смотрит камера. Это те данные?
Это позиция/вращение самой камеры в мире. Получив вектор в координатах камеры мы можем перевести его в мир (если нужно, обычно нет). Тогда используются те данные что Вы говорите

Ещё с перспективой связан метод viewSize(), который соответственно возвращает размер области, которая в данный момент отображена (вмещается в обзор камеры).
Ищите что-то типа fieldOfView


Название: Re: получение координат точки на сфере
Отправлено: spirits25 от Июнь 21, 2013, 16:15
Я, наверное, неправильно выразился или неверно написал. Вот часть кода, которая отвечает за изменение масштаба (или как это правильно назвать):
Код:
    float fov = camera()->fieldOfView();
    if (fov != 0.0f)
        camera()->setFieldOfView(2*scale);
    else
        camera()->setViewSize(scale2F);
Сам его не придумывал, взял из документации и с вашими словами он вроде как совпадает.

Цитировать
Это позиция/вращение самой камеры в мире. Получив вектор в координатах камеры мы можем перевести его в мир (если нужно, обычно нет). Тогда используются те данные что Вы говорите
Это я использую, я не знаю как рассчитать куда именно мышка кликает.

Спасибо за советы, сейчас буду копать в направленную сторону=)


Название: Re: получение координат точки на сфере
Отправлено: Igors от Июнь 21, 2013, 17:07
Это я использую, я не знаю как рассчитать куда именно мышка кликает.
Вы можете найти вектор в координатах камеры как в посте #3 (размеры рендеримой картинки известны, пксель куда кликнули тоже). Помножив вектор на обратную матрицу камеры Вы получите вектор в мире (хотя обычно удобнее все считать в камере)


Название: Re: получение координат точки на сфере
Отправлено: spirits25 от Июнь 24, 2013, 10:42
Вы можете найти вектор в координатах камеры как в посте #3 (размеры рендеримой картинки известны, пксель куда кликнули тоже). Помножив вектор на обратную матрицу камеры Вы получите вектор в мире (хотя обычно удобнее все считать в камере)

И после спроецировать с плоскости на сферу (пересчитать координаты)? Ведь так найдётся точка на плоскости, касательной к сфере в точке с координатами, куда в данный момент смотрит камера?


Название: Re: получение координат точки на сфере
Отправлено: Igors от Июнь 24, 2013, 10:51
И после спроецировать с плоскости на сферу (пересчитать координаты)? Ведь так найдётся точка на плоскости, касательной к сфере в точке с координатами, куда в данный момент смотрит камера?
Нет. Если хотите расскажу "основы", а то сейчас трудности с взаимопониманием  :)


Название: Re: получение координат точки на сфере
Отправлено: spirits25 от Июнь 24, 2013, 11:07
Нет. Если хотите расскажу "основы", а то сейчас трудности с взаимопониманием  :)

Думаю было бы не плохо=)


Название: Re: получение координат точки на сфере
Отправлено: Igors от Июнь 24, 2013, 11:45
Базовые понятия

1) "Система координат". Для простоты полагаем что это 3 взаимо-перпендикулярные оси единичной длины. Наиболее популярное направление осей см выше, но может быть любым, напр autodesk направил Z вверх (и отлично себя чувствует). Мировая система координат - та в которой меряются все остальные. Все исходные координаты (читаемые из файла) "в мире". Координаты и вектора могут быть переведены из мира в систему координат камеры и обратно домножением на прямую/обратную матрицу камеры. В системе координат камеры центр (0, 0, 0)  - в мире это соответствует точке где стоит камера, ну для примера (100, 200, 5). Все эти многочисленные параметры типа upVector, eyeDir - это всего лишь способы задания матрицы камеры, т.е. ее позиции в мире и направлений осей.

2) "Перспектива". Камера смотрит в центр рендеримого экрана. Луч камера-пиксель определяет что будет видно в этом пикселе (что он пересекает). При этом необходимо так знать на каком расстоянии камера от центра экрана, иначе нет однозначности "какой луч для этого пикселя". Как говорилось выше этот параметр задается всяко-разно, часто через угол зрения.

Теперь Ваша задача.
Есть сфера (QGLSceneNode), центр которой совпадает с центром всей сцены. Необходимо получить декартовые координаты точки, по которуй произведён клик мышью.
Кликнув в пиксель Вы находите данный луч/вектор (не точку) см посте #3. Этот вектор в координатах камеры. Нормируете его и домножаете на радиус Вашей сферы - получили точку где луч пересек сферу. Переводите ее в мир (если надо). Voila

 


Название: Re: получение координат точки на сфере
Отправлено: spirits25 от Июнь 24, 2013, 12:37
Кликнув в пиксель Вы находите данный луч/вектор (не точку) см посте #3. Этот вектор в координатах камеры.

Код:
void EarthView::mouseDoubleClickEvent(QMouseEvent *e)
{
    QVector3D pick = mapPoint(e->pos());
...
Камера находится на расстоянии 10 от центра координат.
Получаю примерно такой вектор в координатах камеры QVector3D(0.0935829, -0.0614973, -5). Это тот самый "данный луч/вектор".

Нормируете его и домножаете на радиус Вашей сферы - получили точку где луч пересек сферу.

Диаметр сферы - 1. То есть нормирую и домножаю на 0.5.
Код:
pick.normalized() * 0.5
Насколько я понимаю, по этим значениям невозможно понять пересеклась сфера или нет, случай "промаха" нужно отбрасывать заранее (не проблема).

Переводите ее в мир (если надо). Voila
Тут я не понял, на что именно нужно домножить? на projectionmatrix ( http://qt.developpez.com/doc/5.0-snapshot/qglcamera/#projectionmatrix )?


Название: Re: получение координат точки на сфере
Отправлено: spirits25 от Июнь 24, 2013, 17:07
Вот с помощью такого кода я могу найти расстояние от камеры до точки пересечения со сферой:

Код:
// длина вектора
qreal norm(QVector3D v)
{
  return sqrt(v.x()*v.x()+v.y()*v.y()+v.z()*v.z());
}

// скалярное произведение 2х векторов
qreal sprod(QVector3D v1, QVector3D v2)
{
  return v1.x()*v2.x()+v1.y()*v2.y()+v1.z()*v2.z();
}

// знак числа
// x>0 : 1
// x=0 : 0
// x<0 : -1
int Sign(float x)
{
  return (x>0)-(x<0);
}

/*
 *p  - начальная точка луча
 *l - направляющий вектор луча, необяз. единичный
 *c - центр сферы
 *r - радиус сферы
 */
float Ray2Sphere(QVector3D p, QVector3D l, QVector3D c, float r)
{
  // квадрат радиуса
  r*=r;
  // квадрат расстояния от точки до центра сферы
  float d=qPow(norm(c-p),2);
  // проекция центра сферы на луч
  float s=sprod(c-p,l)/norm(l);
  if(d>r && s<0) // точка снаружи сферы и луч направлен от сферы
    return -1; // нет пересечения
  // квадрат расстояния от прямой до центра сферы по теореме Пифагора
  float h=d-s*s;
  if(h>r) // луч не пересекает сферу
    return -1;
  return s+sqrt(r-h)*Sign(r-d); // расстояние до пересечения
}

То есть я знаю точку камеры, знаю направление и знаю расстояние. Можно посчитать саму точку (формулу ещё не искал).
В этом решении есть какие-то косяки?


Название: Re: получение координат точки на сфере
Отправлено: Igors от Июнь 24, 2013, 17:16
Код:
void EarthView::mouseDoubleClickEvent(QMouseEvent *e)
{
    QVector3D pick = mapPoint(e->pos());
...
Камера находится на расстоянии 10 от центра координат.
Получаю примерно такой вектор в координатах камеры QVector3D(0.0935829, -0.0614973, -5). Это тот самый "данный луч/вектор".
Не знаю что возвращает mapPoint - похоже на точку (не вектор) в мире. Это можно проверить сдвинув камеру напр по X на 100. Если точка в мире, тогда

pt_on_world_sphere = (pick - cam_Pos).normalized()  * R + cam_Pos;
где
cam_Pos - позиция камеры в миое
R - радиус сферы (0.5)

То есть я знаю точку камеры, знаю направление и знаю расстояние. Можно посчитать саму точку (формулу ещё не искал).
В этом решении есть какие-то косяки?
А зачем если известно что камера в центре сферы? Достаточно просто нормировать направление и домножить на радиус


Название: Re: получение координат точки на сфере
Отправлено: spirits25 от Июнь 24, 2013, 18:43
А зачем если известно что камера в центре сферы? Достаточно просто нормировать направление и домножить на радиус
Камера не в центре сферы. Начало мировых координат в центре сферы.


Название: Re: получение координат точки на сфере
Отправлено: Igors от Июнь 25, 2013, 08:53
Камера не в центре сферы. Начало мировых координат в центре сферы.
Тогда да, надо искать пересечение луча  (pick - cam_Pos).normalized() 



Название: Re: получение координат точки на сфере
Отправлено: spirits25 от Июнь 25, 2013, 12:07
Тогда да, надо искать пересечение луча  (pick - cam_Pos).normalized() 

Да, но pick и cam_Pos в разных системах (pick - в координатах камеры, а cam_Pos в мировых координатах). Я всё остальное сделал, пересечение могу найти, а вот как перевести просто точку из координат камеры в мировые пока не догоняю=)


Название: Re: получение координат точки на сфере
Отправлено: Igors от Июнь 25, 2013, 12:50
Да, но pick и cam_Pos в разных системах (pick - в координатах камеры, а cam_Pos в мировых координатах). Я всё остальное сделал, пересечение могу найти, а вот как перевести просто точку из координат камеры в мировые пока не догоняю=)
Ну вот, "much better", грамотно излагаете. Я глянул исходники и вроде там пишут что getPos mapPoint возвращает точку в мире (хотя хз на чем она лежит). Это легко проверить - сдвиньте камеру напр на 100 по Х. Если это точка в мире - то и у pickPos будет большое X


Название: Re: получение координат точки на сфере
Отправлено: spirits25 от Июнь 25, 2013, 13:36
Ну вот, "much better", грамотно излагаете. Я глянул исходники и вроде там пишут что getPos возвращает точку в мире (хотя хз на чем она лежит). Это легко проверить - сдвиньте камеру напр на 100 по Х. Если это точка в мире - то и у pickPos будет большое X

Не понимаю, что такое getPos, чей это метод?
pickPos - если это
Код:
QVector3D pick = mapPoint(e->pos());
То он в координатах камеры и как бы я камеру не смещал, это значение всё равно вертится около значения QVector3D(0.0360963, -0.152406, -5), причём координата z -5, получается, что это точка на плоскости xoy, со значением z = -5.

Чтобы перевести координату из координат камеры в мировые координаты необходимо домножить на modelViewMatrix, например:
Код:
QVector3D pickWorld = camera()->modelViewMatrix() * pick;
Вроде так всё правильно. Сейчас поразбираюсь дальше, вроде данных хватает=)

P.S.: проверил ещё раз - такой перевод неверен. То есть перевести из координат камеры в мировые координаты пока не могу.


Название: Re: получение координат точки на сфере
Отправлено: spirits25 от Июнь 25, 2013, 13:45
Цитировать
QVector3D QGLCamera::mapPoint(const QPoint & point, qreal aspectRatio, const QSize & viewportSize) const

Maps point from viewport co-ordinates to eye co-ordinates. The size of the viewport is given by viewportSize, and its aspect ratio by aspectRatio.

The returned vector will have its x and y components set to the position of the point on the near plane, and the z component set to -nearPlane().

This function is used for converting a mouse event's position into eye co-ordinates within the current camera view.

Не понимаю, почему "the z component set to -nearPlane()"? может поэтому у меня неверные данные.


Название: Re: получение координат точки на сфере
Отправлено: spirits25 от Июнь 25, 2013, 14:15
Умножение на camera()->modelViewMatrix() переводит из мировых координат в координаты камеры.
Для обратного перевода мне необходимо умножить на обратную матрицу?


Название: Re: получение координат точки на сфере
Отправлено: Igors от Июнь 25, 2013, 14:55
Умножение на camera()->modelViewMatrix() переводит из мировых координат в координаты камеры.
Для обратного перевода мне необходимо умножить на обратную матрицу?
Да


Название: Re: получение координат точки на сфере
Отправлено: spirits25 от Июнь 25, 2013, 15:44
Да, всё верно
Код:
camera()->modelViewMatrix().inverted() * pointVector;
где pointVector - координаты точки в координатах камеры (масло масленное))).
Причём важно умножать матрицу на точку, а не наоборот=)

Igors, ещё раз большое спасибо за помощь=)