Russian Qt Forum
Май 04, 2024, 01:08 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 2 3 [4] 5 6 ... 24   Вниз
  Печать  
Автор Тема: Геометрия (задачки)  (Прочитано 208786 раз)
Bepec
Гость
« Ответ #45 : Сентябрь 12, 2014, 15:37 »

Ну раз увеличиваем, то смещение будет равно текущим коодинатам + % увеличения, нет?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #46 : Сентябрь 12, 2014, 15:47 »

А нам не надо увеличивать вью, нам надо увеличивать прямоугольник.
Ну если растут размеры самого пр-ка - то др задача. А иначе не понял что Вы имели ввиду.

Только после увеличения камера должна продолжать смотреть на ту же точку прямоугольника, что и до увеличения.
Можно конечно, но если точка не в центре, то камера будет как-то хитро вращаться, поэтому обычно так не делают.

- запомнить текущий вектор камера-цель
- передвинуть камеру
- вычислить новый вектор камера-цель
- довернуть камеру на угол между 2 векторами

Как вычислить смещение?
Сделайте 2 ф-ции: одна переводит из пикселей в 3D, другая наоборот. В изометрии это возможно. Не исключено такие ф-ции есть в OpenGL. После этого все вопросы отпадут сами
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #47 : Сентябрь 12, 2014, 16:24 »

Сделайте 2 ф-ции: одна переводит из пикселей в 3D, другая наоборот. В изометрии это возможно. Не исключено такие ф-ции есть в OpenGL. После этого все вопросы отпадут сами
Я понял вашу идею. Но не проще ли вычислять положение камеры, точнее, матрицу проекции?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #48 : Сентябрь 13, 2014, 06:15 »

Я понял вашу идею. Но не проще ли вычислять положение камеры, точнее, матрицу проекции?
Текущая камера может быть повернута произвольным образом, заново вычисляя матрицу Вы потеряете этот поворот.

Для перспективной камеры обычно принимается какое-то "расстояние до цели", напр основываясь на объектах или просто юзверь может задать. Одна и та же точка экрана может соответствовать близкому/дальнему объекту, известен луч но не расстояние по нему. Вы можете двигать камеру по лучу но какое видимое увеличение это вызовет - хз.
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #49 : Сентябрь 13, 2014, 12:56 »

Я понял вашу идею. Но не проще ли вычислять положение камеры, точнее, матрицу проекции?
Текущая камера может быть повернута произвольным образом, заново вычисляя матрицу Вы потеряете этот поворот.
Давайте заново.
Имеется ортографическая проекция, описываемая матрицей project.
Имеется положение объекта в пространстве, описываемое матрицей modelview.
Применяя увеличение мы не станем затрагивать матрицу modelview, мы сократим только обзор матрицы project. Также мы применим к ней смещение относительно курсора.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #50 : Сентябрь 14, 2014, 10:46 »

.. мы сократим только обзор матрицы project. Также мы применим к ней смещение относительно курсора.
А разве эта матрица переводит в пиксели? По-моему нет, напр если в resizeGL вызовем glViewPort то получим уже др пиксели, но матрица GL_MODELVIEW останется той же. Хотя я могу ошибаться, знатоком OpenGL никогда не был. Проверьте, потом продолжим
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #51 : Сентябрь 14, 2014, 13:24 »

glViewPort задаёт лишь область в окне, куда будет выводиться картинка.
Матрица ModelView преобразует исходную геометрию в новую с учётом поворотов, трансляций и масштабов.
Матрица Projection преобразует геометрию (результат преобразования матрицы ModelView) в новую таким образом, что полученные координаты помещаются в "коробку" размером 2.0f х 2.0f х 2.0f, где центр этой "коробки" находится в {0; 0; 0}. Примитивы, выходящие за пределы коробки отсекаются конвейером, остальные прорисовываются.

Таким образом у нас на экране получается, что центр преобразованной геометрии соответствует центру ViewPort'а, левый и нижний края соответствуют проекционным значениям -1.0f, а правый и верхний - 1.0f.

Что я хочу сказать. Матрицу преобразований трогать мы не станем, нам достаточно только сдвинуть положение этой "коробки", то есть немножечко изменить проекционную матрицу. Задать её параметры l, r, t, b (прямоугольник) смещёнными относительно курсора.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #52 : Сентябрь 14, 2014, 14:06 »

Матрицу преобразований трогать мы не станем,
Так давайте лучше о ней и не упоминать  Улыбающийся

Матрица Projection преобразует геометрию (результат преобразования матрицы ModelView) в новую таким образом, что полученные координаты помещаются в "коробку" размером 2.0f х 2.0f х 2.0f, где центр этой "коробки" находится в {0; 0; 0}. Примитивы, выходящие за пределы коробки отсекаются конвейером, остальные прорисовываются.
А по-моему за размер коробки отвечает glOrtho, и он может быть любым (не только 2х2х2). А матрица Projection просто преобразует из одних координат в другие - что получилось, то и получилось. Давайте сначала разберемся зависит ли она от glViewport и glOrtho

Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #53 : Сентябрь 14, 2014, 14:46 »

ViewPort преобразует координаты таким образом:

Слева результирующие координаты в пространстве окна. xd, yd, zd - исходные координаты, приходящие во вьюпорт (после всех матричных преобразований, то есть в нормированном состоянии). px, py - ширина и высота вьюпорта в пикселях (здесь прошу обратить внимание на то, что не матрица преобразует координаты в пиксели, это мнение, на мой взгляд, ошибочно). f и n - координаты дальней и ближней плоскостей (1.0f и -1.0f). ox и oy - координаты центра вьюпорта в пикселях.
Отсюда видно, что можно выкинуть всякие projection и modelView и рисовать свои координаты напрямую в поле [-1.0f; 1.0f] x [-1.0f; 1.0f] x [-1.0f; 1.0f];
ModelView матрица выполняет переориентирование геометрии относительно нулевой координаты (надеюсь, правильно выразился)
Новая геометрия получается по формуле modelView * vertex = newVertex.
В правом столбце матрицы ModelView находится центр будущей геометрии относительно нулевой координаты. Таким образом центр можно задать тупо перезаписав значения в правом столбце. В первых 3х находятся направления базиса по x, y и z в виде единичных векторов.
Поворот геометрии осуществляется по формуле rotationMatrix * currentModelView * vertex = newVertex.
Трансляция осуществляется с учётом поворота:
translationMatrix * currentModelView = newModelView
Про масштабирование говорить не берусь - не применял, боюсь обмануть.
После применения вышеописанных преобразований мы получаем матрицу, которая будет преобразовывать нашу геометрию.
(Существует ещё матрица lookAt, которая тоже относится к преобразованиям ModelVew)

Projection матрица описывает "коробку", в которую будут помещаться вершины, ранее преобразованные матрицей ModelView (далее vertex - преобразованная вершина с помощью ModelView)
Одной из самых популярных матриц проекции является перспективная матрица. Один из способов её задания является описание усечённой пирамиды (Frustum):

Полученная усечённая пирамида "сжимается в параллелепипед" (таким образом, что ближайшее сечение сжимается меньше, чем дальнее) и получается эффект перспективы.
Стоит упомянуть, что библиотеки Qt и GLU предоставляют возможность более простого задания Frustum через функцию Perspective.
Также существует матрица ортографической проекции, которая описывает параллелепипед (Ortho):

Умножая матрицу проекции на ранее преобразованную вершину мы на выходе получаем спроецированную вершину:
projection * vertex = projectedVertex.

Теперь давайте рассмотрим весь процесс отрисовки. Для простоты примем, что мы отрисовываем исключительно точки:
Первоначально мы создаём матрицу проекции proj.
Далее создадим матрицу преобразований геометрии modelView. Изначальна она единичная. Применяя вышеописанные операции приведем её к целевой.
Введем также ещё одну временную матрицу modelViewProjection = proj * modelView. Она и станет у нас преобразователем вершин.
Теперь берём каждую вершину и преобразовываем:
modelViewProjection * vertex = convertedVertex.
Далее проверяем, какие вершины в своих координатах имеют числа выходящие за предел [-1.0f; 1.0f] (за предел нашей "коробочки"). Такие вершины отсеиваются (в случае рисования точек).
Теперь полученные вершины натягиваем на вьюпорт с помощью первой формулы.
Далее происходит отрисовка пикселей.
« Последнее редактирование: Сентябрь 14, 2014, 14:47 от __Heaven__ » Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #54 : Сентябрь 14, 2014, 15:20 »

Аллегория матрицы проецирования с коробкой, скорее всего, не очень хороша, но пока не знаю, как лучше изъяснить свое мнение. Надеюсь, что всё-таки оно остаётся понятным.

Теперь, к нашим баранам  Улыбающийся
Возьмем коробку шириной и высотой в размер экрана и бесконечной глубиной. Возьмем в ней плоскость, на глубине 0 (середина коробки). На данной плоскости нанесем вершины, которые все попадут в коробку. Так как размеры коробки равны размерам вьюпорта, содержимое её остается неизмненным и мы получаем:

Теперь давайте увеличим нашу геометрию относительно центра в 2 раза. Для этого плоскость с вершинами оставим прежнюю, а коробку возьмем в 2 раза меньше по каждому из её краев кроме глубины:

Вершины, не попавшие в коробку отсекаются, содержимое коробки натягивается на вьюпорт - получаем:

Теперь произведём увеличение с учетом положения курсора мышки. Положение новой коробки должно быть выбрано таким образом, чтобы курсор до и после увеличения указывал на один и тот же объект (глубиной пренебрегаем).
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #55 : Сентябрь 14, 2014, 15:59 »

Давайте сначала с теорией разберемся
после всех матричных преобразований, то есть в нормированном состоянии
). px, py - ширина и высота вьюпорта в пикселях (здесь прошу обратить внимание на то, что не матрица преобразует координаты в пиксели, это мнение, на мой взгляд, ошибочно).
Матрицы ничего не нормируют, они просто перегоняют из одних координат в другие. Никакого "нормированного состояния" не существует. Преобразование вьюпорта матричное, другое дело "в OpenGL нет матрицы переводящей в пиксели" - но никто не мешает сделать ее самому, просто домножив на матрицу вьюпорта.

ModelView матрица выполняет переориентирование геометрии относительно нулевой координаты (надеюсь, правильно выразился)
Не надо искать в матрице какой-то "волшебный смысл" Улыбающийся, это просто перевод из одного пр-ва в другое - здесь из модели во вьюер. Таким образом убедились - от glOrtho и glViewport эта матрица не зависит. Уже хорошо.

Одной из самых популярных матриц проекции является перспективная матрица. Один из способов её задания является описание усечённой пирамиды (Frustum):

Полученная усечённая пирамида "сжимается в параллелепипед" (таким образом, что ближайшее сечение сжимается меньше, чем дальнее) и получается эффект перспективы.
Перспектива не описывается матрицей (что бы ни говорил OpenGL).
Матричное (аффинное) преобразование отображает равные отрезки в равные
А для перспективного преобразования это не так. Поэтому НИКАКАЯ матрица не делает из куба фрустум (усеченную пирамиду). Что имеется ввиду в OpenGL - не знаю. Во всяком случае это не та матрица что в математике.

Насчет остального - я подумаю и отпишусь позже. (если смогу, тут опять стрельба  Плачущий)
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #56 : Сентябрь 14, 2014, 17:27 »

Никакого "нормированного состояния" не существует.
Да, загнул.
НИКАКАЯ матрица не делает из куба фрустум (усеченную пирамиду)
Я имел в виду, что происходит имитация пирамиды. Эту имитацию придают значения в третьем столбце
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #57 : Сентябрь 15, 2014, 11:30 »

Обратил внимание
Матричное (аффинное) преобразование отображает равные отрезки в равные
Строго говоря это неверно - напр мы можем вытянуть куб вдоль одной из осей. Правильно "параллельные равные". Кстати и параллельность сохраняется. А перспективное преобразование это просто

x_new = x * focal_length / z;
y_new = y * focal_length / z;

где x, y, z = исходные координаты, а focal_length = параметр перспективной камеры. Ладно, пока разберемся с изометрией

---------------

Из того что Вы рассказали я понял что матрица GL_MODELVIEW никакого отношения к масштабу и пикселям не имеет. Ну и хорошо, тогда просто так

scale = ortho_width / viewport_width;  

ortho_width - размер пресловутой "коробки" по горизонтали (то что подается в glOrtho). Масштаб по Y тот же. При увеличении бОльший размер подается и наоборот. Пусть есть пыксель с координатами x_screen, y_screen (от левого верхнего угла) и мы хотим чтобы при увеличении он остался на месте

x_delta = (x_screen - viewport_width * 0.5) * (scale_old - scale_new);
y_delta = (viewport_height * 0.5 - y_screen) * (scale_old - scale_new);

Получили x_delta и y_delta в координатах вьюера. Эти значения надо добавить в последний столбец матрицы GL_MODELVIEW. Остается проверить на практике  Улыбающийся
« Последнее редактирование: Сентябрь 15, 2014, 11:32 от Igors » Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #58 : Сентябрь 15, 2014, 12:46 »

На практике расписал поиск новых top left на бумаге и получил это
Код
C++ (Qt)
#include <QApplication>
#include <QGLWidget>
#include <QWheelEvent>
#include <QMouseEvent>
#include <QDebug>
 
class Magnifier : public QGLWidget
{
public:
   Magnifier(QWidget* parent = 0) :
       QGLWidget(parent)
   {
       for (int i = 0; i < 5; i++)
           for (int j = 0; j < 10; j++)
           {
               points[(i * 10 + j) * 3] =  -0.8f + (i % 2) * 0.2f + j * 1.6f / 10.0f;
               points[(i * 10 + j) * 3 + 1] = -0.8f + i * 1.6f / 5.0f;
               points[(i * 10 + j) * 3 + 2] = 0.0f;
           }
       top = left = -1.0;
       mag = 1.0;
       countMagWindow(0, 0);
       setMouseTracking(true);
   }
   void initializeGL()
   {
       glClearColor(0.0f, 0.0f, 0.1f, 1.0f);
       glPointSize(3.0f);
       glMatrixMode(GL_MODELVIEW);
       glLoadIdentity();
       glMatrixMode(GL_PROJECTION);
   }
 
   void resizeGL(int w, int h)
   {
       glViewport(0, 0, w, h);
   }
 
   void paintGL()
   {
       glLoadIdentity();
       glOrtho(left, left + 2.0 / mag, top + 2.0 / mag, top, -1.0, 1.0);
 
       glClear(GL_COLOR_BUFFER_BIT);
       glColor3f(1.0f, 0.0f, 0.0f);
       glEnableClientState(GL_VERTEX_ARRAY);
       glVertexPointer(3, GL_FLOAT, 0, points);
       glDrawArrays(GL_POINTS, 0, 50);
       glDisable(GL_VERTEX_ARRAY);
 
       glColor3f(0.0f, 1.0f, 0.0f);
       glBegin(GL_LINES);
           glVertex2d(magLeft, magTop);
           glVertex2d(magLeft, magTop + 2.0 / mag / 1.1);
           glVertex2d(magLeft, magTop + 2.0 / mag / 1.1);
           glVertex2d(magLeft + 2.0 / mag / 1.1, magTop + 2.0 / mag / 1.1);
           glVertex2d(magLeft + 2.0 / mag / 1.1, magTop + 2.0 / mag / 1.1);
           glVertex2d(magLeft + 2.0 / mag / 1.1, magTop);
           glVertex2d(magLeft + 2.0 / mag / 1.1, magTop);
           glVertex2d(magLeft, magTop);
       glEnd();
 
   }
   void mouseMoveEvent(QMouseEvent *event)
   {
       qDebug() << QString("x: %1, y: %2").arg(event->x()).arg(event->y());
       countMagWindow(event->x(), event->y());
       updateGL();
   }
 
   void wheelEvent(QWheelEvent *event)
   {
       if (event->delta() > 0)     //от себя
       {
           left = magLeft;
           top = magTop;
           mag *= 1.1;
       }
       else
       {
           countUnMagWindow(event->x(), event->y());
           left = magLeft;
           top = magTop;
           mag /= 1.1;
       }
       countMagWindow(event->x(), event->y());
       updateGL();
   }
 
   void countMagWindow(int x, int y)
   {
       double futureMag = mag * 1.1;
       double right = left + 2.0 / futureMag,
              bottom = top + 2.0 / futureMag;
       magLeft = (right - left) * x / width() * (1.0 / 11.0) + left;
       magTop = (bottom - top) * y / height() * (1.0 / 11.0) + top;
   }
 
   void countUnMagWindow(int x, int y)
   {
       double futureMag = mag / 1.1;
       double right = left + 2.0 / futureMag,
              bottom = top + 2.0 / futureMag;
       magLeft = (right - left) * x / width() * (-0.1) + left;
       magTop = (bottom - top) * y / height() * (-0.1) + top;
   }
 
private:
   float points[50 * 3];
   double top, left, mag,
          magTop, magLeft;
};
 
int main(int argc, char *argv[])
{
   QApplication a(argc, argv);
   Magnifier w;
   w.show();
 
   return a.exec();
}
 

В решении имеется небольшое смещение при каждом шаге. Пока что не успел разобраться, но это точно какая-то погрешность.
Зелёной рамкой выделено будущее увеличение.
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #59 : Сентябрь 15, 2014, 13:13 »

Из того что Вы рассказали я понял что матрица GL_MODELVIEW никакого отношения к масштабу и пикселям не имеет.
Имеет.

Помимо способа, предложенного мною, можно не трогать матрицу PROJECTION и масштабировать вершины в матрице MODELVIEW с помощью glScale (устаревшая). А также можно сдвигать вершины вершины по осям, чтобы в коробку попали необходимые с помощью glTranslate (тоже устаревшая).

Я может быть ошибаюсь, но к пикселям имеет отношение почти всё, что встречается на пути по конвейеру (в общем случае). Не совсем понимаю, зачем мы их упоминаем.
Записан
Страниц: 1 2 3 [4] 5 6 ... 24   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.101 секунд. Запросов: 22.