Запутался в градусах и движении / вращении камеры OpenGL / GLUT

ВНИМАНИЕ. Я отредактировал приведенный ниже вопрос, который более актуален для моей реальной проблемы, чем текст ниже. Вы можете пропустить его, но я оставлю его здесь по историческим причинам.

Чтобы увидеть, правильно ли я понял, float в C - это то же самое, что и значение в радианах, верно? Я имею в виду, 360º = 6,28318531 радиан, и я только что заметил в своем приложении OpenGL, что полное вращение идет от 0,0 до 6,28, что, кажется, складывается правильно. Я просто хочу убедиться, что понял это правильно.

Я использую float (назовем его anglePitch) от 0,0 до 360,0 (его легче читать в градусах, и я избегаю преобразования int в float все время), и весь код, который я вижу в Интернете, использует какой-то макрос DEG2RAD(), который определяется как DEG2RAD 3.141593f / 180. В итоге получилось бы примерно так:

anglePitch += direction * 1; // direction will be 1 or -1
refY = tan(anglePitch * DEG2RAD);

Это действительно полное вращение, но это полное вращение будет, когда anglePitch = 180 и anglePitch * DEG2RAD = 3.14, но полное вращение должно быть 360 | 6.28. Если я изменю макрос на любой из следующих:

#define DEG2RAD 3.141593f / 360
#define DEG2RAD 3.141593f / 2 / 180

Работает как положено, полный оборот произойдет, когда anglePitch = 360.

Что мне здесь не хватает и что я должен использовать для правильного преобразования углов в радианы / числа с плавающей запятой?

ВАЖНОЕ ИЗМЕНЕНИЕ (НАСТОЯЩИЙ ВОПРОС):
Теперь я понимаю код, который я вижу повсюду в Интернете по поводу DEG2RAD, я слишком туп в математике (да, я знаю, это важно при работе с такого рода вещи). Итак, я перефразирую свой вопрос:

Я добавил это в свой код:

#define PI         3.141592654f
#define DEG2RAD(d) (d * PI / 180)

Теперь при работе с углами наклона / зевки в градусах, которые снова равны floats, чтобы избежать постоянного заброса, я просто использую макрос DEG2RAD, и значение градуса будет правильно преобразовано в радианы. Эти значения будут переданы в функции sin / cos / tan и вернут правильные значения, которые будут использоваться в камере GLUT.

А теперь настоящий вопрос, в котором я раньше действительно был сбит с толку, но не мог лучше объяснить:

angleYaw += direction * ROTATE_SPEED;
refX = sin(DEG2RAD(angleYaw));
refZ = -cos(DEG2RAD(angleYaw));

Этот код будет выполнен, когда я нажму клавиши ВЛЕВО / ВПРАВО, и камера будет вращаться по оси Y соответственно. Полный оборот идет от 0º до 360º.

anglePitch += direction * ROTATE_SPEED;
refY = tan(DEG2RAD(anglePitch));

Этот похожий код будет выполнен, когда я нажму клавиши ВВЕРХ / ВНИЗ и камера будет вращаться по оси X. Но в этой ситуации полный поворот идет от 0 ° до 180 °, и это меня действительно сбивает с толку. Я уверен, что это как-то связано с функцией касательной, но я не могу понять это.

Могу ли я использовать sin / cos (как в коде зевоты) для достижения такого же вращения? Каков правильный способ, самый простой код, который я могу добавить / исправить, и что имеет больше смысла для создания полного поворота по углу наклона от 0º до 360º?


person rfgamaral    schedule 06.03.2011    source источник
comment
Кстати, у вас есть «зевота» в нескольких местах, где, я думаю, вы имели в виду «рыскание».   -  person JustJeff    schedule 07.03.2011


Ответы (3)


360° = 2 * Pi, Pi = 3.141593…

Радианы определяются длиной дуги угла вдоль окружности радиуса 1. Окружность окружности равна 2 * r * Pi, поэтому один полный оборот на единичной окружности имеет длину дуги 2 * Pi = 6,28…

Углы в градусах измеряются тем фактом, что, совмещая 6 равносторонних треугольников, вы покрываете полный оборот. Итак, у нас есть 6 треугольников, каждый составляет 6-ю часть поворота, поэтому древние вавилоняне разделили круг на части 1 / (6 * 6) = 1/36, и для дальнейшего уточнения это было подразделено на 10. Вот почему мы закончили полный круг на 360 °. Однако это число выбрано произвольно.

Итак, если имеется 2 * Пи / 360 °, получается Пи / 180 ° = 3,141593… / 180 °, что является коэффициентом преобразования из градусов в радианы. Обратное, 180 ° / Pi = 180 / 3,141593…

Я не могу понять, почему старая функция OpenGL glRotate и GLU gluPerspective использовали градусы вместо радианов. С математической точки зрения имеют смысл только радианы. Что, на мой взгляд, лучше всего демонстрирует уравнение Эйлера

e^(i*Pi) - 1 = 0

Вот и все, все важные математические числа в одном уравнении. При чем тут углы? Что ж:

e^(i*alpha) = cos(alpha) + i * sin(alpha), alpha is in radians!

ИЗМЕНИТЬ в отношении измененного вопроса:

Ваши углы плавания - все в порядке. Почему вы вообще думаете, что degress - это целые числа, я не могу понять. Обычно вам не нужно определять PI самостоятельно, он предопределен в math.h, обычно называется M_PI, M_2PI и M_PI2 для Pi, 2 * Pi и Pi / 2. Вы также должны изменить свой макрос, то, как он написан сейчас, может создавать странные эффекты.

#define DEG2RAD(d) ( (d) * M_PI/180. )

В GLUT камеры нет вообще. GLUT - это довольно тупой фреймворк OpenGL, который я рекомендую не использовать. Вы, вероятно, имеете в виду gluLookAt.

Уберите эти препятствия с дороги, давайте посмотрим, что вы там делаете. Помните, что тригонометрические функции действуют на единичной окружности. Пусть угол 0 направлен вправо, а углы увеличиваются против часовой стрелки. Затем sin (a) определяется как количество вправо и cos (a), а также количество вперед, чтобы достичь точки под углом a на единичной окружности. Это то, чему присваиваются refX и refZ.

refY, однако, не имеет смысла писать таким образом. tan = sin / cos, поэтому по мере приближения к n * pi / 2 (т.е. 90 °) он расходится до +/- бесконечности. По крайней мере, это объясняет ваш циклический диапазон пи / 180 °, потому что это период tan.

Сначала я подумал, что tan, возможно, использовался для нормализации вектора направления, но это тоже не имело смысла. Фактор был бы 1./sqrt(sin²(Pitch) + 1) Я дважды проверил: использование tan делает правильные вещи.

РЕДАКТИРОВАТЬ2: Я не понимаю, в чем ваша проблема: Угол наклона составляет от -90 ° до + 90 °, что имеет смысл. Купите себе глобус (земли): координаты восток-запад (долгота) изменяются от -180 ° до + 180 °, координаты юг-север (широта) идут от -90 ° до + 90 °. Подумайте об этом: любой больший диапазон координат создал бы двусмысленность.

Единственное хорошее предложение, которое я вам предлагаю: возьмите учебник по математике и сосредоточьтесь на сферических координатах! Извините, что говорю вам об этом. Все, что у вас есть, отлично работает, вам просто нужно понимать геометрию сперматозоидов.

Вы используете термины "рыскание" и "тангаж". Обычно они используются в углах Эйлера. Теперь, к сожалению, углы Эйлера, которые поначалу впечатляют, в дальнейшем вызывают серьезные проблемы (например, блокировка кардана). Вам вообще не следует их использовать. Также может быть хорошей идеей, если вы использовали карандаш / палочки / что-то еще, чтобы разложить руки, которые вы собираетесь вращать, чтобы понять их механику.


И кстати: есть еще и нецелые степени. Просто перейдите на http://maps.google.com, чтобы увидеть их в действии (просто выберите место и позвольте http://maps.google.com дает вам ссылку на него).

person datenwolf    schedule 06.03.2011
comment
Я только что отредактировал свой вопрос выше. Буду очень признателен, если вы прочитаете и отредактируете свой ответ соответствующим образом. Извините за неприятности. - person rfgamaral; 06.03.2011
comment
Было бы очень полезно, если бы вы опубликовали фактический код OpenGL, в котором вы используете эти значения, чтобы я мог видеть их контекст. - person datenwolf; 06.03.2011
comment
Я использую это так gluLookAt(eyeX, eyeY, eyeZ, eyeX + refX, eyeY + refY, eyeZ + refZ, 0.0f, 1.0f, 0.0f); Но я все еще не уверен, что мне делать ...: / - person rfgamaral; 06.03.2011
comment
Вам следует избавиться от gluLookAt и разбить просмотр на вращения вокруг осей Z и X, используя два последовательных вызова glRotate. Перевод точки обзора выполняется с помощью glTranslatef. По моему опыту, новичкам намного легче понять это. - person datenwolf; 06.03.2011
comment
Я бы предпочел использовать gluLookAt. - person rfgamaral; 06.03.2011
comment
Только что заметил ваш edit2. Я знаю, что это имеет смысл, но я имел в виду, что хочу сделать полный круг, а это 360º, а не 180º. Я знаю, что tan () дает мне это, и это нормально, я спрашиваю, как сделать такой шаг полного круга. - person rfgamaral; 06.03.2011
comment
Назгуллед: Вы сталкиваетесь с определенным ограничением углов Эйлера: фиксатором кардана. В углах Эйлера угол наклона ›90 ° нельзя отличить от 90 ° - этот шаг и (рыскание + 180 °)% 360 ° - эта двусмысленность делает невозможным то, что вы намереваетесь. Что вам нужно, так это кватернионы или представление поворотов матрицами. - person datenwolf; 06.03.2011
comment
Мне трудно понять, почему это невозможно? Я имею в виду, если я могу сделать полный оборот по одной оси, почему я не могу сделать то же самое с любой из двух других? Я просто не могу понять это ... - person rfgamaral; 07.03.2011
comment
По простой причине: углы Эйлера просто несут информацию о направлении, а не об ориентации. Это все равно, что стоять на северном полюсе и пытаться отправиться дальше на север: как только вы окажетесь на северном полюсе, вы сможете идти только на юг. Угол наклона / рыскания - одно из немногих. В любом случае: вектор вверх, как вы передаете его в gluLookAt, всегда будет приводить к вертикальной ориентации, так что это второй ограничивающий фактор. Короче говоря: не используйте углы gluLookAt и Эйлера. Используйте кватернионы и glRotatef. - person datenwolf; 07.03.2011

'float' - это тип, например int или double. радианы и градусы - это единицы измерения, которые могут быть представлены с любой точностью. то есть нет причин, по которым вы не можете иметь 22,5 градуса и сохранять это значение в виде числа с плавающей запятой.

полный оборот в радианах равен 2 * пи, примерно 6,283, тогда как полный оборот в градусах равен 360. Вы можете преобразовать их, разделив полный круг начальной единицы, а затем умножив на полный круг желаемой единицы.

например, чтобы перейти от 90 градусов к радианам, сначала разделите градусы. 90 на 360 составляет 0,25 (обратите внимание, что это значение в «оборотах»). Теперь умножьте 0,25 на 6,283, чтобы получить 1,571 радиан.

продолжение

Причина, по которой вы видите свой цикл подачи в два раза быстрее, чем следовало бы, заключается именно в том, что вы используете tan (шаг) для вычисления Y-компоненты. Что у вас должно быть, так это то, что компонент Y зависит от sin (шага). т.е. попробуйте изменить

refY = tan(DEG2RAD(anglePitch));

to

refY = sin(DEG2RAD(anglePitch));

техническая деталь: все числа, которые входят в матрицу просмотра, должны быть в диапазоне от -1 до +1, и если вы должны были проверить значения, которые вы вводите в refY, и запустить свой шаг за пределами от -45 до + 45 градусов, вы бы заметили проблему; tan () уносится в бесконечность под углом +/- 90 градусов.

также обратите внимание, что преобразование значения из int в float ни в каком смысле преобразует градусы в радианы. приведение просто дает вам ближайшее эквивалентное значение в новом типе хранилища. например, если вы приведете целое число 22 к плавающей запятой, вы получите 22.0f, тогда как если вы приведете 33.3333f для ввода int, у вас останется 33. при работе с углами вы действительно должны просто придерживаться плавающей запятой, если только вы не ограничены работой со встроенным процессором или чем-то еще. это особенно важно для радианов, где целые числа представляют собой скачок (примерно) 57,3 градуса.

person JustJeff    schedule 06.03.2011
comment
Извините, но это не отвечает на мои вопросы. Вы в основном сказали то же, что и я, но другими словами. - person rfgamaral; 06.03.2011
comment
Я только что отредактировал свой вопрос выше. Буду очень признателен, если вы прочитаете и отредактируете свой ответ соответствующим образом. Извините за неприятности. - person rfgamaral; 06.03.2011

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

refY = sin(DEG2RAD(anglePitch)); 
XZfactor = cos(DEG2RAD(anglePitch));
refX = XZfactor*sin(DEG2RAD(angleYaw));
refZ = -XZfactor*cos(DEG2RAD(angleYaw));
person Martin Stone    schedule 06.03.2011
comment
Да, они. С вашим ядром, когда он достигает -90 и выходит за пределы, он больше не исчезает, но положение объекта немного подскакивает, это не плавное вращение в этой точке. - person rfgamaral; 06.03.2011
comment
Вероятно, это произойдет потому, что вектор взгляда будет указывать в том же направлении, что и вектор вверх вашей камеры. Вероятно, вам тоже нужно повернуть вектор вверх. Лучшим подходом может быть вместо этого манипулировать матрицей представления, накапливая на ней ваши вращения. - person Martin Stone; 06.03.2011