Основываясь исключительно на вашем коде, основная проблема в Decagon()
для определения вершин формы. Поскольку такие вершины определены не в центре самой фигуры, а в направлении к верхнему правому углу, она не будет вращаться вокруг себя, а, кажется, будет вращаться вокруг центра, хотя ваша последовательность умножений матриц работает нормально.
Для простоты я бы визуализировал центрирование его в 0,0
вдоль плоскости xy, затем определял правую половину его формы, а затем отражал ее обратно в левую. Вы можете воспользоваться знаком -
минус. Неявно используйте преимущество определения формы в NDC (Normalizd Device Coordinate) пространстве .
Примечание : не совсем такое же соотношение, как в вашем первоначальном определении, но чтобы получить васидея. Вы можете попробовать поменять следующее на свое, тогда оно должно вращаться вокруг себя.
glBegin(GL_POLYGON);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.25f, 0.5f, 0.0f);
glVertex3f(0.45f, 0.30f, 0.0f);
glVertex3f(0.55f, 0.0f, 0.0f);
glVertex3f(0.45f, -0.30f, 0.0f);
glVertex3f(0.25f, -0.5f, 0.0f);
glVertex3f(-0.25f, -0.5f, 0.0f);
glVertex3f(-0.45f, -0.30f, 0.0f);
glVertex3f(-0.55f, 0.0f, 0.0f);
glVertex3f(-0.45f, 0.30f, 0.0f);
glVertex3f(-0.25f, 0.5f, 0.0f);
//glScalef(10, 10, 10); // this won't have any effect on result
//glTranslatef(1, 2, -3);// the same
glEnd();
У вас есть 2 варианта здесь
- Полностью изменить определение вершин (только с
Decagon
быть похожим на вышеуказанное относительно происхождения). Другие формы уже хороши, они определены относительно начала координат. - Тщательно определите источник формы независимо от того, как вершины вашей определенной формы. Используйте такое положение, чтобы сначала перевести форму назад как часть операции матрицы перед всеми другими операциями (пожалуйста, прочтите, чтобы узнать почему).
Концепция вращения вокруг себя
Концепция вращения вокруг себя заключается в том, что нам нужно выполнить следующие операции в следующем порядке:
- Масштаб (в данном случае у нас нет)
- Вращение
- Перевод
Масштабирование, хотя у нас его нет в этом случае, должно быть последним, иначе это может повлиять на другие две операции.
Если мы переведем сначала в произвольную позицию,тогда вращение произойдет вокруг такой точки. На самом деле вращение работает относительно начала координат 0,0
, поэтому нам просто нужно каким-либо образом сначала поместить объект обратно в начало координат, прежде чем мы продолжим, затем мы можем вращать, переводить в желаемое положение, в котором он должен быть, и масштабировать.
Давайте посмотрим ваш порядок умножения матриц
glScalef(0.7f, 0.7f, 0.7f); //increase the object size
glTranslatef(d[0], d[1], d[2]); // Move box down X axis.
glRotatef(zAngle, 0, 0, 1);
glRotatef(yAngle, 0, 1, 0);
glRotatef(xAngle, 1, 0, 0);
glRotatef(spin, 0.0, 0.0, 1.0);
Это означает, что мы делаем следующее в следующем порядке:
- вращаемся вокруг оси z с
spin
angle - вращается вокруг оси x с
xAngle
углом - вращается вокруг оси y с
yAngle
углом - вращается вокруг оси z с
zAngle
углом
Хотя мы могли бы, возможно, объединить первое и последнее вместе, но в любом случае это нормально.
Также вы можете захотеть дополнительно взглянуть на Углы Эйлера , когда мы вращаемся вокруг3 кардинальных оси, подобные этой, могут привести к проблеме блокировки карданного подвеса , но ее можно решить путем ограничения углов, которые пользователь может вращать вокруг определенной оси.
Порядок правильный. Это переведено в математические термины как S * T * Rz * Ry * Rx * Rspin
, в котором вы можете видеть, что в коде он обратен порядку. Rspin
происходит сначала, затем Rx
, а затем и так далее.
И что же произойдет, если форма Decagon
определена не относительно начала координат, но определена так, как онапереведено вправо.
Возьмите мое определение вершин, но + 0.55f
для всех позиций x, у нас будет
glBegin(GL_POLYGON);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f(0.80f, 0.5f, 0.0f);
glVertex3f(1.0f, 0.30f, 0.0f);
glVertex3f(1.10f, 0.0f, 0.0f);
glVertex3f(1.0f, -0.30f, 0.0f);
glVertex3f(0.80f, -0.5f, 0.0f);
glVertex3f(0.30f, -0.5f, 0.0f);
glVertex3f(0.10f, -0.30f, 0.0f);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3f(0.1f, 0.30f, 0.0f);
glVertex3f(0.30f, 0.5f, 0.0f);
glEnd();
Если вы поменяете код выше на определение вершин, то оно выиграетбольше не вращайся вокруг себя. Но мы знаем, что для возврата формы в исходное положение требуется -0.55f
по оси x, поэтому, если мы добавим glTranslatef(-0.55f, 0.0f, 0.0f)
в качестве первой выполняемой операции, она будет работать так же.
Мы 'd have
glScalef(0.7f, 0.7f, 0.7f);
glTranslatef(d[0], d[1], d[2]);
glRotatef(zAngle, 0, 0, 1);
glRotatef(yAngle, 0, 1, 0);
glRotatef(xAngle, 1, 0, 0);
glRotatef(spin, 0.0, 0.0, 1.0);
glTranslatef(-0.55f, 0.0f, 0.0f); // <------ add this
Короче говоря, прежде чем вращать (вокруг себя), переведите целевой объект в исходное положение, затем выполните правильную последовательность действий.
Если вы хотите иметьтакой объект должен быть расположен в том месте, где вы определили вершины фигуры, т.е. он находится справа +0.55f
вдоль оси x и все еще вращается вокруг себя. Тогда вместо этого вы используете glTranslatef(d[0] + 0.55f, d[1], d[2])
.
Дополнительные примечания
- Последние две операции gl
glScalef()
и glTranslatef()
не будут иметь никакого эффекта, так как вы уже нарисовали фигуру. Эти две операции отбрасываются каждый кадр, когда вы вызываете glLoadIdentity()
. - . Просто обратите внимание, что исходный код все еще основан на конвейере фиксированных функций OpenGL. Возможно, вы захотите взглянуть на современный программируемый конвейер. Это даст вам больше гибкости в управлении виртуальной камерой, поэтому операции с матрицами будут более четкими и отделенными от самого объекта всякий раз, когда нам нужно перемещаться. Таким образом, это облегчит понимание и понимание матричных операций.
Редактировать Для дополнительного контроля и удовлетворения требований приложения, как указано ниже
- Щелкните левой кнопкой мыши, чтобы вращаться бесконечно, затем снова щелкните левой кнопкой мыши, чтобы остановить
- Щелкните правой кнопкой мыши, чтобы пролистать цвет для визуализированной формы
У нас должны быть контрольные флаги и информация для насизменить в любое время кадра следующим образом.
bool isSpinning = false;
#define NUM_COLOR 4
int sCurrColor = 0;
GLfloat sColor[NUM_COLOR][3] = {
{1.0f, 1.0f, 1.0f},
{1.0f, 0.0f, 0.0f},
{0.0f, 1.0f, 0.0f},
{0.0f, 0.0f, 1.0f}
};
Так что для цветов у нас есть белый, красный, синий и зеленый. Всего в 4 цветах, каждый цвет имеет 3 значения компонента для RGB. Мы начинаем с белого цвета, как видно из sCurrColor
для нашего индекса.
Теперь ваша mouse()
функция будет выглядеть следующим образом
void mouse(int buton, int state, int x, int y)
{
switch (buton) {
case GLUT_LEFT_BUTTON:
if (state == GLUT_DOWN && !isSpinning)
isSpinning = true;
else if (state == GLUT_DOWN && isSpinning)
isSpinning = false;
break;
case GLUT_RIGHT_BUTTON: //here I don't know how to change the color of the shape
if (state == GLUT_DOWN)
sCurrColor = (sCurrColor + 1) % NUM_COLOR;
break;
default:glutIdleFunc(NULL);
break;
}
}
Мы оптимизировали перемещение glutIdleFunc(spinDisplay);
для вызова внутриФункция main()
как раз перед glutMainLoop()
. В соответствии с вашими требованиями, нам не нужно менять его каждый кадр.
Таким образом, spinDisplay()
теперь изменяется на
void spinDisplay() //here it's the problematic function
{
if (isSpinning)
{
spin = spin + 3.0;
if (spin > 360.0)
{
spin = 0.0;
}
}
glutPostRedisplay();
}
Возможно, лучше изменить имяфункционировать в нечто вроде display()
, так как это более общее, чтобы не путать, что мы должны вращаться каждый раз. Во всяком случае, я не изменил это для краткости и последовательности вашего кода.
Теперь последняя часть - подключить sColor
для использования всеми этими фигурами при рендеринге. Например, для Decagon
вы можете сделать это
glColor3f(sColor[sCurrColor][0], sColor[sCurrColor][1], sColor[sCurrColor][2]);
Это будет то же самое для других фигур, если вы хотите получить тот же эффект, щелкнув правой кнопкой мыши для циклического изменения цвета.