Как повернуть представление OpenGL относительно центра представления, а не центра отображаемого объекта? - PullRequest
1 голос
/ 21 февраля 2012

Я работаю над вилкой Pleasant3D .

При вращении отображаемого объекта объект всегда вращается вокруг одной и той же точки относительно себя, даже если эта точка находится не в центре вида (например, из-за того, что пользователь панорамировал, чтобы переместить объект в виде). ​​

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

Вот ядро ​​текущего кода, который вращает объект вокруг его центра (немного упрощенно) (от здесь ):

glLoadIdentity(); 

// midPlatform is the offset to reach the "middle" of the object (or more specifically the platform on which the object sits) in the x/y dimension.
// This the point around which the view is currently rotated.
Vector3 *midPlatform = [self.currentMachine calcMidBuildPlatform];
glTranslatef((GLfloat)cameraTranslateX - midPlatform.x, 
             (GLfloat)cameraTranslateY - midPlatform.y, 
             (GLfloat)cameraOffset);

// trackBallRotation and worldRotation come from trackball.h/c which appears to be
// from an Apple OpenGL sample.
if (trackBallRotation[0] != 0.0f) {
  glRotatef (trackBallRotation[0], trackBallRotation[1], trackBallRotation[2], trackBallRotation[3]);
}
// accumlated world rotation via trackball
glRotatef (worldRotation[0], worldRotation[1], worldRotation[2], worldRotation[3]);

glTranslatef(midPlatform.x, midPlatform.y, 0.);

// Now draw object...

Какие преобразования мне нужно применить, в каком порядке, чтобы получить желаемый эффект?


Что-то из того, что я пробовал до сих пор

Насколько я понимаю, это то, что делает текущий код:

«OpenGL выполняет умножение матриц в обратном порядке, если к вершине применяется несколько преобразований» (с здесь ). Это означает, что первое применяемое преобразование фактически является последним в приведенном выше коде. Он перемещает центр вида (0,0) к центру объекта.

Эта точка затем используется в качестве центра вращения для следующих двух преобразований (поворотов).

Наконец, перевод midPlatform выполняется в обратном порядке, чтобы переместить центр обратно в исходное местоположение, и применяются XY-переводы (панорамирование), выполненные пользователем. Здесь также «камера» перемещается от объекта в нужное место (обозначено cameraOffset).

Это кажется достаточно простым. Так что мне нужно изменить, вместо того, чтобы перевести центр вида в центр объекта (midPlatform), мне нужно перевести его в текущий центр вида, видимый пользователем, верно?

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

Я попытался изменить код на это:

glLoadIdentity(); 

glTranslatef(0, 
             0, 
             (GLfloat)cameraOffset);

if (trackBallRotation[0] != 0.0f) {
    glRotatef (trackBallRotation[0], trackBallRotation[1], trackBallRotation[2], trackBallRotation[3]);
}
// accumlated world rotation via trackball
glRotatef (worldRotation[0], worldRotation[1], worldRotation[2], worldRotation[3]);

glTranslatef(cameraTranslateX, cameraTranslateY, 0.);

Другими словами, я перевожу центр обзора в предыдущий центр, вращаюсь вокруг него, а затем применяю смещение камеры, чтобы переместить камеру в правильное положение. Это заставляет вращение вести себя именно так, как я хочу, но оно порождает новую проблему. Теперь любое панорамирование, выполненное пользователем, относится к объекту. Например, если объект вращается так, что камера смотрит вдоль оси X, и если пользователь перемещается влево-вправо, то объект, кажется, перемещается ближе / дальше от пользователя, а не влево или вправо.

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

Я нашел некоторые подсказки в FAQ по OpenGL (http://www.opengl.org/resources/faq/technical/transformations.htm), например:

9.070 Как преобразовать мои объекты вокруг фиксированной системы координат, а не в локальной системе координат объекта?

Если вы вращаете объект вокруг своей оси Y, вы обнаружите, что оси X и Z вращаются вместе с объектом. Последующее вращение вокруг одной из этих осей вращается вокруг вновь преобразованной оси, а не исходной оси. Часто желательно выполнять преобразования в фиксированной системе координат, а не в локальной системе координат объекта.

Основной причиной проблемы является то, что матричные операции OpenGL повторно умножаются на стек матрицы, что приводит к преобразованию в объектном пространстве. Чтобы повлиять на трансформации пространства экрана, вам нужно предварительно умножить. OpenGL не предоставляет переключателя режимов для порядка умножения матриц, поэтому вам нужно предварительно умножить вручную. Приложение может реализовать это путем извлечения текущей матрицы после каждого кадра. Приложение умножает новые преобразования для следующего кадра поверх единичной матрицы и умножает накопленные текущие преобразования (из последнего кадра) на эти преобразования, используя glMultMatrix ().

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

И

9.120 Как найти координаты вершины, преобразованной только матрицей ModelView?

Часто полезно получить значение координатной точки глаза для вершины (то есть вершины пространства объекта, преобразованной матрицей ModelView). Вы можете получить это, извлекая текущую матрицу ModelView и выполняя простое умножение вектора на матрицу.

Но я не уверен, как применять их в моей ситуации.

1 Ответ

4 голосов
/ 22 февраля 2012

Вам необходимо преобразовать / перевести точку «центра зрения» в начало координат, повернуть, а затем инвертировать этот перевод обратно в преобразование объекта. Это известно как изменение базиса в линейной алгебре.

С этим гораздо проще работать, если у вас есть правильная 3d-математическая библиотека (я полагаю, у вас она есть), и это также помогает держаться подальше от устаревших API-интерфейсов с фиксированным конвейером. (подробнее об этом позже).

Вот как бы я это сделал:

  1. Найдите преобразование для центра точки обзора в мировых координатах (выясните это, затем нарисуйте его, чтобы убедиться, что оно правильное, с осями x, y, z, поскольку предполагается, что axii правильна относительно вид). Если вы используете точку центра обзора и вращение (обычно обратное вращению камеры), это будет преобразование из начала координат мира в центр обзора . Сохраните это в матричном преобразовании 4x4.

  2. Примените обратное преобразование, указанное выше, чтобы оно стало источником. glMultMatrixfv(center_of_view_tf.inverse());

  3. Поворот вокруг этой точки, как вы хотите (glRotate())

  4. Превратить все обратно в мировое пространство (glMultMatrixfv(center_of_view_tf);)

  5. Примените собственное преобразование мира объекта (glTranslate/glRotate или glMultMatrix) и нарисуйте его.


О конвейере с фиксированной функцией

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

В конце концов, он стал настолько продвинутым, что вы могли запрограммировать его довольно непристойным образом (зарегистрировать объединителей всех). И тогда стало возможным реально загрузить небольшую программу на ассемблере для всех преобразований на уровне вершин. Тогда имело смысл сохранять там много кремния, который просто делал одно (особенно если бы вы могли использовать эти транзисторы для ускорения программирования), так что все стало программируемым. Если требовался рендеринг с «фиксированной функцией», драйвер просто преобразовывал состояние (X-свет, проекции текстуры и т. Д.) В код шейдера и загружал его как вершинный шейдер.

Итак, в настоящее время, когда даже обработка фрагментов является программируемой, есть только много опций с фиксированными функциями, которые используются тоннами приложений OpenGL, но кремний на GPU просто запускает шейдеры (и многие из них) параллельно).

...

Чтобы сделать OpenGL более эффективным, а драйверы менее громоздкими, а аппаратное обеспечение более простым и пригодным для использования на мобильных / консольных устройствах, а также в полной мере использовать программируемое оборудование, которое OpenGL работает в наши дни, многие функции в API теперь отмечены осуждается. Они недоступны в OpenGL ES 2.0 и более поздних версиях (для мобильных устройств), и вы не получите от них максимальной производительности даже на настольных системах (где они еще будут в драйвере на долгие годы, обслуживая столь же древние базы кода, создаваемые назад к рассвету ускоренной трехмерной графики)

Фиксированная функциональность в основном касается того, как преобразования / освещение / текстурирование и т. Д. Выполняются по умолчанию в OpenGL (т. Е. glEnable(GL_LIGHTING)), вместо того, чтобы указывать эти операции в пользовательских шейдерах.

В новой, программируемой OpenGL матрице преобразования - это просто униформа в шейдере. Любой поворот / перевод / мульти / обратный (как указано выше) должен выполняться клиентским кодом (вашим кодом) перед загрузкой в ​​OpenGL. (Использование only glLoadMatrix - один из способов начать думать об этом, но вместо использования gl_ModelViewProjectionMatrix и тому подобного в вашем шейдере, используйте свою собственную униформу.)

Это немного беспокоит, так как вам нужно реализовать довольно много из того, что было сделано драйвером GL раньше, но если у вас есть свой собственный список объектов / граф с преобразованиями, где-то преобразования и т. Д., Это не так уж много Работа. (OTOH, если у вас есть много glTranslate / glRotate в вашем коде, это может быть ...). Как я уже сказал, хорошая библиотека 3d-математики здесь необходима.

- ..

Итак, чтобы изменить вышеприведенный код на стиль «программируемый конвейер», вы просто сделаете все эти умножения матриц в своем собственном коде (вместо того, чтобы это делал драйвер GL, все еще на CPU), а затем отправите полученную матрицу чтобы открыть gg как униформу, прежде чем активировать шейдеры и вывести свой объект из VBO.

(Обратите внимание, что современные карты не имеют кода с фиксированной функцией, просто много кода в драйвере для компиляции состояния рендеринга с фиксированной функцией в шейдер, который выполняет эту работу. Неудивительно, что «классические» драйверы GL огромны .. .)

...

Некоторая информация об этом процессе доступна в Руководстве по аппаратному обеспечению Тома и, вероятно, в Google тоже.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...