Сдвиг объекта мышью вдоль определенной оси в 3D

Тема в разделе "WASM.OpenGL", создана пользователем AlexBond, 27 июл 2007.

  1. AlexBond

    AlexBond Member

    Публикаций:
    0
    Регистрация:
    30 янв 2005
    Сообщения:
    69
    Адрес:
    Belarus
    Необходимо реализовать следующее:
    имеется выделеный объект в 3D который необходимо перемещать курсором вдоль определенной оси, причем чтобы объект был прикреплен к курсору.(Анология в 3DSMAX).

    я делаю следующее на OpenGL:

    Код (Text):
    1. glGetIntegerv(GL_VIEWPORT,@vp);
    2. glGetDoublev(GL_MODELVIEW_MATRIX,@VmMatrix);
    3. glGetDoublev(GL_PROJECTION_MATRIX,@PrMatrix);
    4. wx0:=wx;  wy0:=wy;   wz0:=wz;
    5. gluProject(wx0,wy0,wz0,@VmMatrix,@PrMatrix,@vp,w2x,w2y,w2z);
    6. gluUnProject(w2x+offX,w2y,w2z,@VmMatrix,@PrMatrix,@vp,wx1,wy1,wz1);
    7. vx:=wx0-wx1; vy:=wy0-wy1; vz:=wz0-wz1;
    8. wx:=wx+(abs(vx*vx+vy*vy+vz*vz)/(vx));
    wx,wy,wz - первоночальная позиция объекта.
    OffX - смещение мышки.

    Как видно из примера, я беру позицию объекта проэктирую ее на экранные координаты, потом прибавляю смещение от мыши, узнаю вторую позицию, потом вычесляю по ним позицию для X.

    Как ни крыво выглядит, но работает, проблема в том что объект при больших сдвигах обгоняет курсор, а при малых отстает.
    Есть ли способ чтобы все было синхронно? (как это в 3DSMAX)
    Может все это можно сделать не используя gluProject, ручками?
     
  2. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Ты в состоянии решить задачу о пересечении прямой с плоскостью?
     
  3. AlexBond

    AlexBond Member

    Публикаций:
    0
    Регистрация:
    30 янв 2005
    Сообщения:
    69
    Адрес:
    Belarus
    В состоянии, только вот кде тут та прямая и где тут та плоскость, перспектива делает из прямой кривую, или нет?, если имеется в виду прямая - экранная Z. Я ж говрю туплю, где тут плоскость где прямая... запутался.
     
  4. Pavia

    Pavia Well-Known Member

    Публикаций:
    0
    Регистрация:
    17 июн 2003
    Сообщения:
    2.409
    Адрес:
    Fryazino
    _DEN_
    Нет там прямой. Перспектива она искажения криволинейные дает.

    AlexBond
    Код (Text):
    1. vx:=wx0-wx1; vy:=wy0-wy1; vz:=wz0-wz1;
    2. wx:=wx+(abs(vx*vx+vy*vy+vz*vz)/(vx));
    Зачем, вроде просто
    Код (Text):
    1. wx:=wx0-wx1; wy:=wy0-wy1; wz:=wz0-wz1;
    Можно и ручками. Но там все равно будут тежи самые преобразования.
     
  5. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    На рисунке в аттаче принцип нахождения положения объектов находящихся за и перед экраном. Соответсвенно, при перемещении объекта мышью в плоскости экрана действует "принцип рычага" т.е. реальное перемещение во столько же раз соотносится с перемещение его экранной проекции, во сколько раз расстояние от наблюдателя до объекта соотносится с расстоянием от наблюдателя до экрана (достаточно соотношения Z координат). Для заэкранных объектов реальное перемещение больше экранного, для передэкранных меньше. Не помню где я читал, что в ОГЛ положение наблюдателя относительно экрана жёстко фиксировано в середине экрана а расстояние по оси Z таково, что угол обзора экрана (голубая линия на рисунке) составляет 45° (только я не понял к чему это относятся к вертикали, горизонтали или диагонали экрана ;). И вроде как изменять это положение нельзя (только не путать положение камеры относительно экрана монитора с положением камеры относительно сцены, которое легко меняется).
     
  6. Pavia

    Pavia Well-Known Member

    Публикаций:
    0
    Регистрация:
    17 июн 2003
    Сообщения:
    2.409
    Адрес:
    Fryazino
    Y_Mur
    Угол изменять можно задается при зодании перспективы параметр Fov выбирается любой, но обычно 45-60-90-120-150.
    Относится он к высоте экрана.
     
  7. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    [EDIT]: Беру таймаут до того как протрезвею))
     
  8. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    Pavia
    Замечательно :))
    А случаем не знаешь, менять можно только угол обзора или положение камеры тоже можно из центра экрана перенести?

    AlexBond
    Значит всё просто - через известный угол обзора определяешь z расстояние от экрана до камеры, затем через подобие прямоугольных треугольников образованных плоскостью экрана, нормалью к экрану проходящей через камеру и лучом камера-объект вычисляешь положение объекта через положение его проекции ;))
    т.е. новые (заданные мышью) координаты (x,y) проекции объекта (относительно центра экрана в который попадает нормаль экран - камера), умножаешь на коэффициент равный отношению z расстояния от камеры до объекта к z расстоянию от камеры до экрана и получишь искомые 3D координаты x,y (они тоже будут относительно центра экрана), а z не изменяется, т.к. мышь ходит в плоскости экрана (x,y)
     
  9. AlexBond

    AlexBond Member

    Публикаций:
    0
    Регистрация:
    30 янв 2005
    Сообщения:
    69
    Адрес:
    Belarus
    Да уж просто...
    Что есть центр экрана в 3D пространстве, это точка куда направлена камера? И что подразумевается под растоянием от камеры до экрана? (у меня камера динамическая)

    На счет прямой и плоскости, прямая то неопределена, она может быть определена только после нахождения точки, так что способ поиска точки пересеения прямой и плоскости отпадает :dntknw:

    Повторю данные:
    1. Объект c позицией (wx,wy,wz).
    2. Экранное смещение offX и offY.
    3. Ось или плоскость вдоль которой производится смещение объекта.
    4. Песпективная камера.

    Сложность:
    Визуальная синхронизация экранного смещения со смещением вдоль плоскости или оси.
     
  10. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    AlexBond
    Я уже писал в #5, что динамическое положение камеры относительно сцены это не тоже самое, что положение камеры относительно экрана. Ты как бы ходишь по виртуальному пространству в виртуальном шлеме, где экран висит у тебя перед носом на заданном расстоянии (как его определить см. #8) и двигается экран относительно сцены вместе с твоей виртуальной головой (камерой). Помедитируй над картинкой в #5, пойми чем проекция отличается от объекта и всё получится ;)

    ЗЫ: тебе нужно знать размер экрана как 3D объекта, тогда ты сможешь определить расстояние до него (#8), затем зная это расстояние и глядя рисунок в #5 поймёшь как зная координаты проекции найти 3D координаты объекта ;)

    ЗЗЫ: текстовка у меня в #5, #8 несколько сумбурная, поскольку сабжем занимался давно и не в ОГЛе, но суть верная, вечером попробую сформульровать её получше.
     
  11. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    Центр экрана это и есть центр экрана как плоского прямоугольника ;) Но в ОГЛ это одновременно ещё и точка куда попадает перпендикуляр проведённый из камеры на плоскость экрана О'. В ОГЛ насколько я понимаю сместить камеру из центра экрана и смотреть на экран как бы сбоку просто нельзя (что не есть хорошо).

    Это как раз длина перпендикуляра от камеры к экрану. Например в реальном мире прислони к центру монитора торец линейки перпендикулярно к плоскости экрана и прислонись глазом к другому торцу линейки ;) В виртуальном мире тебе нужно будет смоделировать тоже самое по отношению к камере и плоскости на которую проецируются 3D объекты ;) ОГЛ делает за тебя большую работу не всегда поясняя как это происходит, но тем не менее понимать суть процесса нужно ;)

    Из подобия треугольников АВО и АВ'О' следует, соотношение:
    АО'/АО = В'О'/ВО
    Где АО' - расстояние от камеры до экрана (измеряется по перпендикуляру к экрану, ось Z), АО - расстояние от камеры до объекта, В'О' - расстояние от центра экрана (точнее от точки куда попадает перпендикуляр к экрану АО' ;)) до проекции твоего объекта на экранную поверхность в 3Д мире (В'О' это как раз экранная координата X' или Y', в зависимости от того какую пару осей ты рассмотришь XZ или YZ), а ВО соответственно уже настоящая 3Д X или Y координата объекта.

    Алгоритм:
    зная (wx,wy,wz) и положение виртуального экрана в ОГЛ 3Д мире ты используя соотношение
    АО'/АО = В'О'/ВО
    находишь экранные координаты X', Y' (ОГЛ тоже это делает, но не делится с тобой результатами, а они тебе нужны в явном виде ;), к ним прибавляешь свои требуемые экранные смещения offX и offY (надеюсь я правильно понял что offX и offY как раз те расстояния на которые нужно переместить объект?), и делаешь обратное преобразование - по скорректированным X', Y' находишь новые X, Y (Z не потребует коррекции), помещаешь объект в эту точку, тогда ОГЛ отрисует его точнохонько там где тебе нужно ;)

    ЗЫ: реализовать сабж без знания расстояния от камеры до плоскости экрана увы не удастся.
     
  12. R_S

    R_S New Member

    Публикаций:
    0
    Регистрация:
    19 ноя 2008
    Сообщения:
    2
    Не могли подсказать в моем случае
    как получается?
    координаты объекта:
    (wx,wy,wz) = (1,0,0);
    координаты мышки старые:
    x=705 y=323
    новые:
    x=709 y=323
    проекция:
    gluPerspective (90.0, 1.0, 0.5, 100.0);
    камера:
    gluLookAt(0.0,0.0,5.0, 0.0,0.0,0.0, 0.0,1.0,0.0);
     
  13. Y_Mur

    Y_Mur Active Member

    Публикаций:
    0
    Регистрация:
    6 сен 2006
    Сообщения:
    2.494
    R_S
    Ещё нужны размеры экрана - у тебя 1280х1024 ? или другие ?
     
  14. R_S

    R_S New Member

    Публикаций:
    0
    Регистрация:
    19 ноя 2008
    Сообщения:
    2
    размеры экрана - 1024x768.
    Угол обзора - 45 или он задается в gluPerspective()?
     
  15. AlexBond

    AlexBond Member

    Публикаций:
    0
    Регистрация:
    30 янв 2005
    Сообщения:
    69
    Адрес:
    Belarus
    Приведу свою рабочую функцию, конечно убогая, но работает безотказно, может кому пригодится.

    Функция GetProjectVert(PTarget.X,PTarget.Y,MAxis,p);
    Которая по координатам мыши PTarget и заданой плоскости MAxis.
    Получает координаты в точку P.
    Суть простая, перед вызовом функции должна быть матрица положения объекта. Мы вычисляем координату центра матрицы объекта на экране. Из них находим треугольник паралельный экрану (он играет роль плоскости). Затем находим две точки p1, p2 максимума и минимума экранна по глубине. Ну и находим точку P пересечение отрезка (p1,p2) c плоскостью (pa,pb,pc) полученой в зависемости от нужной оси Axis. В итоге задав нужную ось, объект будет перемещаться мышью, прикрепленный к мыши, и двигающийся лишь вдоль заданой оси.

    Код (Text):
    1. function LineFacet(p1, p2: TXYZ; Axis: TAxis; var p: TXYZ;
    2.   wd, wd0: TXYZ): Boolean;
    3. var
    4.   d: Real;
    5.   denom, mu, Length: Real;
    6.   n, pa, pb, pc: TXYZ;
    7. begin
    8.   Result := false;
    9.   p.x := 0;  p.y := 0;  p.z := 0;
    10.   pa.x := 0;  pa.y := 0;  pa.z := 0;
    11.   case Axis of
    12.     Axis_X:
    13.     begin    
    14.       pb.x := 1;      pb.y := 0;      pb.z := 0;    
    15.       pc := wd;
    16.     end;
    17.     Axis_XY:
    18.     begin    
    19.       pb.x := 1;      pb.y := 0;      pb.z := 0;    
    20.       pc.x := 0;      pc.y := 1;      pc.z := 0;
    21.     end;
    22.     Axis_Y:
    23.     begin    
    24.       pb.x := 0;      pb.y := 1;      pb.z := 0;    
    25.       pc := wd;  
    26.     end;
    27.     Axis_YZ:
    28.     begin    
    29.       pb.x := 0;      pb.y := 1;      pb.z := 0;    
    30.       pc.x := 0;      pc.y := 0;      pc.z := 1;
    31.     end;
    32.     Axis_Z:
    33.     begin    
    34.       pb.x := 0;      pb.y := 0;      pb.z := 1;    
    35.       pc := wd;
    36.     end;
    37.     Axis_XZ:
    38.     begin    
    39.       pb.x := 1;      pb.y := 0;      pb.z := 0;    
    40.       pc.x := 0;      pc.y := 0;      pc.z := 1;
    41.     end;
    42.     Axis_XYZ:
    43.     begin  
    44.       pb := wd;  
    45.       pc := wd0;
    46.     end;
    47.   end;
    48.  
    49.   n.x := (pb.y - pa.y) * (pc.z - pa.z) - (pb.z - pa.z) * (pc.y - pa.y);
    50.   n.y := (pb.z - pa.z) * (pc.x - pa.x) - (pb.x - pa.x) * (pc.z - pa.z);
    51.   n.z := (pb.x - pa.x) * (pc.y - pa.y) - (pb.y - pa.y) * (pc.x - pa.x);
    52.  
    53.   Length := Sqrt(n.x * n.x + n.y * n.y + n.z * n.z);
    54.   n.x := n.x / Length;
    55.   n.y := n.y / Length;
    56.   n.z := n.z / Length;
    57.  
    58.   d := -n.x * pa.x - n.y * pa.y - n.z * pa.z;
    59.  
    60.   denom := n.x * (p2.x - p1.x) + n.y * (p2.y - p1.y) + n.z * (p2.z - p1.z);
    61.   if (Abs(denom) < 1.0E-8)        then     Exit;
    62.   mu := -(d + n.x * p1.x + n.y * p1.y + n.z * p1.z) / denom;
    63.   if (mu < 0) or (mu > 1)      then     Exit;
    64.   p.x := p1.x + mu * (p2.x - p1.x);
    65.   p.y := p1.y + mu * (p2.y - p1.y);
    66.   p.z := p1.z + mu * (p2.z - p1.z);
    67.   case Axis of
    68.     Axis_X:
    69.     begin
    70.       p.y := 0;      p.z := 0;
    71.     end;
    72.     Axis_Y:
    73.     begin
    74.       p.x := 0;      p.z := 0;
    75.     end;
    76.     Axis_Z:
    77.     begin
    78.       p.y := 0;      p.x := 0;
    79.     end;
    80.   end;
    81.   Result := true;
    82. end;
    83.  
    84. procedure GetProjectVert(X, Y: Real; axis: TAxis; var p: TXYZ);
    85. var
    86.   p1, p2, wd, wd0, wd1: TXYZ;
    87.   vp: TVector4i;
    88.   PrMatrix, VmMatrix: TGLMatrixd4;
    89. begin
    90.   glGetIntegerv(GL_VIEWPORT, @vp);
    91.   glGetDoublev(GL_MODELVIEW_MATRIX, @VmMatrix);
    92.   glGetDoublev(GL_PROJECTION_MATRIX, @PrMatrix);
    93.  
    94.   case axis of
    95.     Axis_Y, Axis_X, Axis_Z, Axis_XYZ:
    96.     begin
    97.       wd.x := 0;         wd.y := 0;         wd.z := 0;
    98.  
    99.       gluProject(wd.x, wd.y, wd.z, VmMatrix, PrMatrix, vp, @wd.x, @wd.y, @wd.z);
    100.       gluUnProject(wd.x, wd.y + 10, wd.z, VmMatrix, PrMatrix, vp,
    101.         @wd0.x, @wd0.y, @wd0.z);
    102.       gluUnProject(wd.x + 10, wd.y, wd.z, VmMatrix, PrMatrix, vp,
    103.         @wd1.x, @wd1.y, @wd1.z);
    104.  
    105.       if axis = Axis_XYZ then
    106.         wd := wd0
    107.       else
    108.       begin
    109.         wd.x := (wd0.x + wd1.x) / 2;
    110.         wd.y := (wd0.y + wd1.y) / 2;
    111.         wd.z := (wd0.z + wd1.z) / 2;
    112.       end;
    113.     end;
    114.   end;
    115.  
    116.   gluUnProject(X, Y, 0, VmMatrix, PrMatrix, vp, @p1.x, @p1.y, @p1.z);
    117.   gluUnProject(X, Y, 1, VmMatrix, PrMatrix, vp, @p2.x, @p2.y, @p2.z);
    118.  
    119.   LineFacet(p1, p2, axis, p, wd, wd1);
    120.  
    121. end;
     
  16. nefiod

    nefiod New Member

    Публикаций:
    0
    Регистрация:
    12 фев 2012
    Сообщения:
    3
    Буду краток и попробую без заумностей ))).
    Берем координаты курсора мыши в окне.
    При помощи gluUnProject узнаем координаты OpenGl
    Затем зная глубину погружения объекта по оси Z выполняем следующие действия
    Вычисляем котангенс угла заданного в gluPerspective (первый параметр (у меня 45 градусов)) и получаем растояние от камеры (глаза) до экрана (у меня получается 2,4142135623730950488016887242097).
    Делим глубину объекта (координата Z (у меня 8.0f)) на это расстояние и получаем коэфициент смещения. У меня получается 3.3137084989847603904135097936785;
    Но!!! Это только для оси Y!
    Не даром первый параметр gluPerspective так и называется fovy.
    Для Икса делаем следующие делим ширину вьюпорта на высоту. Например, 640/480.
    Получаем 1,3333333333333333333333333333333. Умножаем на коэфициент смещения по Игрек (тобишь 3.3137084989847603904135097936785) получаем коэфициент для Икс (4.4182779986463471872180130582373).
    Ну а дальше приращиваем смещение мыши к коородинатам обекта, домножая координаты X и Y на соответствующие коэфициенты.
    И все будет хокей! Спасибо за внимание, надеюсь комунить помог. И спасибо авторам выше за то что помогли мне!
     
    Sashasan нравится это.
  17. DEEP

    DEEP Андрей

    Публикаций:
    0
    Регистрация:
    27 апр 2008
    Сообщения:
    491
    Адрес:
    г. Владимир
    nefiod
    Вы, конечно, очень вовремя спохватились.
    Теме уже четвёртый годок пошёл…
     
  18. nefiod

    nefiod New Member

    Публикаций:
    0
    Регистрация:
    12 фев 2012
    Сообщения:
    3
    Чуть оговорюсь! Для вычисления котангенса делим угол на 2 (у меня 45/2=22.5);
     
  19. nefiod

    nefiod New Member

    Публикаций:
    0
    Регистрация:
    12 фев 2012
    Сообщения:
    3
    Уважаемый DEEP! Время ничто - Тема Всё! Тем более, что когда я столкнулся с проблемой перемещения объекта OpenGl мышкой только этот форум помог мне разрешить её (гуглить я умею и еще как, но ни чего толковее чем здешние советы не встречал). Еще раз спасибо Вам. З.Ы. Поисковик то на ваш сайт кажет ))))