Вращение кватерниона не работает как исключенное

В OpenGL ES 1 для android у меня есть кубик Rubic, который состоит из 27 меньших кубов. Мне нужны повороты, которые вызывают особый куб, который становится точно перед точкой зрения. поэтому мне нужны два вектора. один – это вектор, который исходит от начала объекта к конкретному кубу. а другой – это вектор, который исходит от источника к точке зрения. то поперечное произведение их дает мне ось вращения, а точечный продукт дает мне угол.

Я конвертирую (0,0,1), который является вектором, который исходит от начала координат до точки зрения в координатах координат мира. вот код:

matrixGrabber.getCurrentModelView(gl); temporaryMatrix.set(matrixGrabber.mModelView); inputVector[0] = 0f; inputVector[1] = 0f; inputVector[2] = 1f; inputVector[3] = 1f; Matrix.multiplyMV(resultVector, 0, temporaryMatrix.InvertMatrix(), 0, inputVector,0); resultVector[0]/=resultVector[3]; resultVector[1]/=resultVector[3]; resultVector[2]/=resultVector[3]; inputVector = ..... // appropriate vector due to user-selection axis = Vector.normalized(Vector.crossProduct(Vector.normalized(inputVector), Vector.normalized(resultVector))); degree = (float)Math.toDegrees(Math.acos(Vector.dot(Vector.normalized(inputVector), Vector.normalized(resultVector)))); 

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

  Quaternion currentRotation = new Quaternion(); Quaternion temporaryRotation = new Quaternion(); . . . currentRotation = (currentRotation).mulLeft(temporaryRotation.set(axis, degree)); currentRotation.toMatrix(matrix); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glMultMatrixf(matrix, 0); 

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

Например, если бы система координат была бы

  • X-right (1,0,0)
  • Y-up (0,1,0)
  • Z-in (0,0,1)

то первое rotation вокруг X 90 gradleусов против часовой стрелки (CCW) вызывает

  • X’-правая (1,0,0)
  • Y’-in (0,0,1)
  • Z’-down (0, -1,0)

и второе rotation вокруг Z 90 gradleусов CCW производит

  • X’-in (0,1,0)
  • Y’-left (-1,0,0)
  • Z’-down (0, -1,0)

но я ожидаю

  • X-up (0,1,0)
  • Y-in (0,0,1)
  • Z-вправо (1,0,0)

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

Ну вчера я решил записать загадку Rubic Cube, потому что все, что я пробовал в прошлом, было действительно неудобно для меня и, наконец, получил некоторое настроение / время, чтобы закодировать его сам. Как я уже закончил, здесь есть мои идеи:

  1. Представление Rubic Cube

    Я не считаю кватернионы хорошим выбором для этого. Вместо этого мне удобнее:

    • 4×4 равномерные матрицы преобразования

    Таким образом, я закончил список матриц 3*3*3=27 плюс один дополнительный для вращения всего куба. В начальном состоянии все кубики имеют часть вращения блока, а исходники установлены для покрытия всех комбинаций { -1 , 0 ,+1 } чтобы заполнить весь Rubic Cube (размер каждой подсеточной сетки равен 1.0 и центрирован вокруг (0,0,0) )

    осях

    Мои кубы в коде на C ++ определяют следующим образом:

     reper cube[27]; // reper is transform matrix 
  2. графический интерфейс пользователя

    Я хотел, чтобы контроль и просмотр были настолько близки к реальному, насколько я мог. Таким образом, rotation управляется мышью простым щелчком на целевом area0 (в области area0 или области area1 ), а затем с направления перетаскивания мыши определяется, какая ось вращается и в каком направлении.

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

  3. Как исправить изменение локальной системы координат?

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

    В matrixх стиля C ++ и OpenGL это выглядит так:

     void RubiCube::axises_unit(reper &rep,int &x,int &y,int &z,int &sx,int &sy,int &sz) { int i; double p[3],xyz[3][3],a,b; rep.axisx_get(xyz[0]); rep.axisy_get(xyz[1]); rep.axisz_get(xyz[2]); vector_ld(p,1.0,0.0,0.0); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { x=i; b=a; } } sx=+1; if (b<0) sx=-1; vector_ld(p,0.0,1.0,0.0); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { y=i; b=a; } } sy=+1; if (b<0) sy=-1; vector_ld(p,0.0,0.0,1.0); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { z=i; b=a; } } sz=+1; if (b<0) sz=-1; } 

    Где reper - class, содержащий прямую и инверсную матрицу преобразования. get_axis просто заглядывает внутрь прямой матрицы и возвращает выбранный векторный вектор направления оси. vector_mul является точечным произведением, а vector_ld просто заполняет 3D-вектор координатами x,y,z .

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

     void RubiCube::axises_obj(reper &rep,int &x,int &y,int &z,int &sx,int &sy,int &sz) { int i; double p[3],xyz[3][3],a,b; rep.axisx_get(xyz[0]); rep.axisy_get(xyz[1]); rep.axisz_get(xyz[2]); vector_ld(p,+0.707,-0.299,-0.641); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { x=i; b=a; } } sx=+1; if (b<0) sx=-1; vector_ld(p,-0.000,-0.906,+0.423); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { y=i; b=a; } } sy=+1; if (b<0) sy=-1; vector_ld(p,-0.707,-0.299,-0.641); for (b=0.0,i=0;i<3;i++) { a=vector_mul(xyz[i],p); if (fabs(a)>=fabs(b)) { z=i; b=a; } } sz=+1; if (b<0) sz=-1; } 

    Обе функции возвращают ту ось, в которой x,y,z и если направление противоположно (sx, sy, sz) по сравнению с матрицей единичного преобразования.

  4. Вращение fragmentа

    Это kernel ​​головоломки. Это простой поворот среза вокруг оси. Это используется для анимации, поэтому шаг угла мал (я использую 9 gradleусов), но весь поворот должен составлять 90 gradleусов, иначе Rubic Cube сломается.

     void RubiCube::cube_rotate(int axis,int slice,double ang) { int j,k,a[3],s[3]; double p[3],p0[3]={0.0,0.0,0.0},lang; reper *r; _redraw=true; for (k=0;k<27;k++) { r=&cube[k]; // local axis,sign axises_unit(*r,a[0],a[1],a[2],s[0],s[1],s[2]); // lang is local signed angle change lang=ang; if (s[axis]<0) lang=-lang; // select slice r->gpos_get(p); j=round(p[axis]+1.0); if (j!=slice) continue; // rotate global position if (axis==0) vector_rotx(p0,p,+ang); if (axis==1) vector_roty(p0,p,-ang); if (axis==2) vector_rotz(p0,p,+ang); r->gpos_set(p); // rotate local cube orientation if (a[axis]==0) r->lrotx(-lang); if (a[axis]==1) r->lroty(-lang); if (a[axis]==2) r->lrotz(-lang); } } 

    Где reper::gpos_get возвращает начало матрицы как 3D-вектор (точка), а reper::gpos_set основном устанавливает новое положение матрицы. vector_rotx(p0,p,a) вращает вектор p вокруг p0 и ось x на угол a . Знаки +/- только соответствуют поворотам от classа reper (я где-то различался). reper::lrotx поворачивает reper вокруг своей локальной оси x чтобы узнать больше о первой ссылке.

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

Здесь вы можете попробовать свое демо: Win32 + OpenGL Rubic Cube Demo

А вот анимированный gif некоторых поворотов:

анимация

[Edit1] Я добавил простой решатель на свой RubiCube

Для реализации решателя я добавил поверхностную планарную цветовую карту (слева ... средний квадрат - это имя и индекс стороны, которую я использую), вычисленный из внутреннего представления RubiCube . Я также добавляю внутренние команды que для решателя (оси и направление справа):

осях

Каждая команда представлена ​​2 символьными строками:

 edge slice CW: RLUDFB edge slice CCW: R'L'U'D'F'B' mid slice CW: R0L0U0D0F0B0 whole cube CW: RcLcUcDcFcBc 

И карта выглядит так:

 int map[6][3][3]; 

Где map[side][u][v] содержит цвет квадрата на стороне s , строку u и столбец v . Я реализовал простое 7-ступенчатое решение (например, решение реального куба человеком):

шаги решения

  1. входное состояние (не шаг)
  2. Белый крест с желтой серединой (желтая середина спереди)
  3. Белый крест (Белый середина спереди)
  4. Белые углы (белая сторона обращена вниз)
  5. Средний уровень (с использованием первых 3 команд)
  6. Верхний слой желтого креста (с использованием 4-й команды)
  7. переупорядочить крест так, чтобы стороны совпадали (5-я команда) и переупорядочивание углов (6-я команда)
  8. ориентация углов верхнего слоя для завершения куба (7-я команда)

Solver прост и работает с строками (неоптимизирован), поэтому он немного медленный, но в любом случае полное решение занимает до 50 мс в моей настройке. Здесь вы можете попробовать обновленную демоверсию:

  • Win32 + OpenGL Rubic Cube Демо с решателем

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

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

Случается, что когда вы применяете это преобразование к своей модели (rotation в вашем случае), вы также вращаете ее базовые векторы. Подумайте об этом, как если бы вы также вращали вашу систему координат или, как если бы вы смотрели с первого взгляда на вашу модель. Каждое преобразование, которое вы делаете, будет влиять на следующий.

Поскольку вы обычно хотите сохранить свою собственную систему координат, вам может потребоваться переместить камеру вокруг куба, а затем повернуть куб. Я уверен, что вы можете найти метод «lookAt» либо в вашем API, либо в Интернете. Он должен принимать 3 вектора: cameraPosition, lookAtPoint, upVector. При таком подходе вы можете расположить куб до (0,0,0), который также является вашим «lookAtPoint», первая камера. Позиция должна быть чем-то вроде (0,0, -1) и сначала upVector (0,1,0). Теперь для движения (вы, вероятно, используете только левый / правый и вверх / вниз в качестве входа): Чтобы перейти вверх / вниз (rotation вокруг X), вы выполните следующие действия:

 originalDistance = (cameraPosition-objectPosition).lenght leftVector = normalizedVector(crossProduct(camearPosition, upVector))//generaly cameraPosition-objectPosition camearPosition = cameraPosition + upVector*inputScalar //inputScalar should be a small floating value cameraPosition = normalizedVector(cameraPosition)*originalDistance //put camera to original distance from object upVector = normalizedVector(crossProduct(cameraPosition, leftVector))//generaly cameraPosition-objectPosition 

Чтобы идти влево / вправо (rotation вокруг X), вы выполните следующие действия:

 originalDistance = (cameraPosition-objectPosition).lenght leftVector = normalizedVector(crossProduct(camearPosition, upVector))//generaly cameraPosition-objectPosition camearPosition = cameraPosition + leftVector*inputScalar //inputScalar should be a small floating value cameraPosition = normalizedVector(cameraPosition)*originalDistance //put camera to original distance from object leftVector = normalizedVector(crossProduct(cameraPosition, upVector))//generaly cameraPosition-objectPosition upVector = normalizedVector(crossProduct(cameraPosition, leftVector))//generaly cameraPosition-objectPosition 

Это должно в целом решить проблему .. (просьба сказать мне, если я сделал ошибку, поскольку я пишу это жестким)

Что касается вашего подхода к вращению самого объекта, вы должны выяснить, каков ваш кватернион в собственной системе координат объекта и повернуть его вокруг него. Это также довольно легко, если у вас есть математические навыки. Кроме того, вы могли бы также просто определить 2 угла (X, Y) и изменить их непосредственно через вход и использовать кватернионы (1,0,0, X) и (0,1,0, Y), но могут возникнуть проблемы с этот подход, когда Y составляет 90 gradleусов.

Надеюсь, это поможет.

  • Как использовать OpenGL ES в отдельном streamе на iphone?
  • С чего начать openGL ES создать и повернуть куб в iPhone?
  • Явное vs Автоматическое привязка местоположения атрибутов для шейдеров OpenGL
  • Рендеринг текстуры без использования двух текстур на iPhone
  • Должен ли я когда-либо использовать `vec3` внутри однородного буфера или шейдерного хранилища?
  • GLES10.glGetIntegerv возвращает 0 только в Lollipop
  • libgdx Сценарий SpriteBatch для текстуры
  • CADisplayLink OpenGL-рендеринг нарушает поведение UIScrollView
  • разработка игр для iphone
  • Как сделать интерактивный глобус / землю для iPhone OpenGL ES?
  • Гауссовский фильтр с шейдерами OpenGL
  • Давайте будем гением компьютера.