对度数和 OpenGL/GLUT 相机移动/旋转感到困惑

发布于 2024-10-20 22:16:53 字数 1819 浏览 6 评论 0原文

注意:我编辑了下面的问题,它比下面的文本更与我的实际问题相关,如果您愿意,可以跳过此问题,但由于历史原因,我会将其留在这里。

看看我是否正确理解,C 中的浮点值与弧度值相同,对吗?我的意思是,360° = 6.28318531 弧度,我刚刚在我的 OpenGL 应用程序上注意到,完整的旋转从 0.0 到 6.28,这似乎是正确的。我只是想确保我做对了。

我使用从 0.0 到 360.0 的 float (我们称之为 anglePitch)(这样更容易以度为单位读取,并避免将 int 转换为 < code>float 一直),我在网上看到的所有代码都使用某种 DEG2RAD() 宏,其定义为 DEG2RAD 3.141593f / 180。最后它会是这样的:

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

这确实进行了一次完整的旋转,但完整的旋转将在 anglePitch = 180anglePitch * 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)

现在,当以度为单位处理俯仰/打哈欠角度时,它们是浮动,再次避免投射所有这次,我只使用 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° 的完整俯仰旋转更有意义的方法是什么?

NOTICE: I have edited the question below which is more relevant to my real issue than the text right below, you can skip this if you but I'll leave it here for historic reasons.

To see if I get this right, a float in C is the same as a value in radians right? I mean, 360º = 6.28318531 radians and I just noticed on my OpenGL app that a full rotation goes from 0.0 to 6.28, which seems to add up correctly. I just want to make sure I got that right.

I'm using a float (let's call it anglePitch) from 0.0 to 360.0 (it's easier to read in degrees and avoids casting int to float all the time) and all the code I see on the web uses some kind of DEG2RAD() macro which is defined as DEG2RAD 3.141593f / 180. In the end it would be something like this:

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

This really does a full rotation but that full rotation will be when anglePitch = 180 and anglePitch * DEG2RAD = 3.14, but a full rotation should be 360|6.28. If I change the macro to any of the following:

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

It works as expected, a full rotation will happen when anglePitch = 360.

What am I missing here and what should I use to properly convert angles to radians/floats?

IMPORTANT EDIT (REAL QUESTION):
I understand now the code I see everywhere on the web about DEG2RAD, I'm just too dumb at math (yeah, I know, it's important when working with this kind of stuff). So I'm going to rephrase my question:

I have now added this to my code:

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

Now, when working the pitch/yawn angles in degrees, which are floats, once again, to avoid casting all the time, I just use the DEG2RAD macro and the degree value will be correctly converted to radians. These values will be passed to sin/cos/tan functions and will return the proper values to be used in GLUT camera.

Now the real question, where I was really confused before but couldn't explain myself better:

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

This code will be executed when I press the LEFT/RIGHT keys and the camera will rotate in the Y axis accordingly. A full rotation goes from 0º to 360º.

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

This is similar code and will be executed when I press the UP/DOWN keys and the camera will rotate in the X axis. But in this situation, a full rotation goes from 0º to 180º degrees and that's what's really confusing me. I'm sure it has something to do with the tangent function but I can't get my head around it.

Is there way I could use sin/cos (as I do in the yawn code) to achieve the same rotation? What is the right way, the most simple code I can add/fix and what makes more sense to create a full pitch rotation from 0º to 360º?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

伏妖词 2024-10-27 22:16:54

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*Pi/360°,则 Pi/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!

编辑,关于修改后的问题:

你的角度是浮动的一切都很好。为什么你甚至认为度是整数我无法理解。通常你不必自己定义 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的点的向前移动量。这就是 refXrefZ 所分配的内容。

然而,refY 这样写是没有意义的。 tan = sin/cos 因此,当我们接近 n*pi/2(即 90°)时,它会发散到 +/- 无穷大。至少它解释了你的 pi/180° 循环范围,因为那是 tan 的周期。

我首先想到 tan 可能已被使用标准化方向向量,但也没有意义。该系数为 1./sqrt(sin²(Pitch) + 1)
我仔细检查过:使用 tan 是正确的。

编辑2:我不明白你的问题出在哪里:俯仰角是 -90° 到 +90°,这是完全有道理的。去给自己买一个地球仪:东西坐标(经度)从-180°到+180°,南北坐标(纬度)从-90°到+90°。想一想:任何更大的坐标范围都会产生歧义。

我向您提供的唯一好建议是:拿一些数学教科书并围绕球坐标思考!很抱歉这样告诉您。无论你拥有什么都可以完美地工作,你只需要了解球面几何即可。

您使用的是术语“偏航”和“俯仰”。这些通常用于欧拉角。现在不幸的是,欧拉角一开始引人注目,后来会造成严重的麻烦(如万向节锁)。您根本不应该使用它们。如果您使用一些铅笔/棍子/任何东西来分解您想要用手了解其机制的旋转,这也可能是一个好主意。


顺便说一句:也有非整数度数。只需跳转到 http://maps.google.com 即可查看它们的实际效果(只需选择某个地点,然后让http://maps.google.com 为您提供了指向它的链接)。

360° = 2 * Pi, Pi = 3.141593…

Radians are defined by the arc length of an angle along a circle of radius 1. The circumfence of a circle is 2*r*Pi, so one full turn on a unit circle has an arc length of 2*Pi = 6.28…

The measure of angles in degrees stem from the fact, that by aligning 6 equilateral triangles you span a full turn. So we have 6 triangles, each making up a 6th of the turn, so the old babylonians divided a circle into pieces of 1/(6*6) = 1/36, and to further refine it this was subdivded by 10. That's why we ended up with 360° in a full circle. This number is arbitrarily choosen, though.

So if there are 2*Pi/360° this makes Pi/180° = 3.141593…/180° which is the conversion factor from degrees to radians. The reciprocal, 180°/Pi = 180/3.141593…

Why on earth the old OpenGL function glRotate and GLU's gluPerspective used degrees instead of radians I cannot fathom. From a mathematical point of view only radians make sense. Which I think is most beautifully demonstrated by Euler's equation

e^(i*Pi) - 1 = 0

There you have it, all the important numbers of mathematics in one single equation. What's this got to do with angles? Well:

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

EDIT, with respect to modified question:

Your angles being floats is all fine. Why would you even think degress being integers I cannot understand. Normally you don't have to define PI yourself, it comes predefined in math.h, usually called M_PI, M_2PI, and M_PI2 for Pi, 2*Pi and Pi/2. You also should change your macro, the way it's written now can create strange effects.

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

GLUT has no camera at all. GLUT is a rather dumb OpenGL framework I recommend not using. You probably refer to gluLookAt.

Those obstacles out of the way let's see what you're doing there. Remember that trigonometric functions operate on the unit circle. Let the angle 0 point towards the right and angles increment counterclockwise. Then sin(a) is defined as the amount of rightwards and cos(a) and the amount of forwards to reach the point at angle a on the unit circle. This is what the refX and refZ are getting assigned to.

refY however makes no sense written that way. tan = sin/cos so as we approach n*pi/2 (i.e. 90°) it diverges to +/- infinity. At least it explains your pi/180° cyclic range, because that's the period of tan.

I was first thinking that tan may have been used to normalize the direction vector, but didn't make sense either. The factor would have been 1./sqrt(sin²(Pitch) + 1)
I double checked: using tan there does the right thing.

EDIT2: I don't see where your problem is: The pitch angle is -90° to +90°, which makes perfect sense. Go get yourself a globe (of the earth): The east-west coordinates (longitude) go from -180° to +180°, the south-north coordinate (latitude) goes -90° to +90°. Think about it: Any larger coordinate range would create ambiguities.

The only good suggestion I offer you is: Grab some math text book and bend your mind around spherical coordinates! Sorry to tell you that way. Whatever you have works perfectly fine, you just need to understand sperical geometry.

You're using the terms Yaw and Pitch. Those are normally used in Euler angles. Now unfortunately Euler angles, which compelling at first, cause serious trouble later on (like gimbal lock). You should not use them at all. It may also be a good idea if you used some pencil/sticks/whatever to decompose the rotations you're intending with your hands to understand their mechanics.


And by the way: There are also non-integer degrees. Just hop over to http://maps.google.com to see them in action (just select some place and let http://maps.google.com give you the link to it).

泅渡 2024-10-27 22:16:54

“float”是一种类型,如 int 或 double。弧度和度是测量单位,两者都可以用您想要的任何精度表示。即,没有理由不能得到 22.5 度,并将该值保留在浮点数中。

以弧度为单位的完整旋转为 2*pi,约为 6.283,而以度为单位的完整旋转为 360。您可以通过除以起始单位的整圆,然后乘以所需单位的整圆来在它们之间进行转换。

例如,要从 90 度转换为弧度,请首先除以度数。 90 除以 360 等于 0.25(注意该值以“转数”为单位)。现在将 0.25 乘以 6.283,得到 1.571 弧度。

跟进

您看到音高周期比应有的快两倍的原因正是因为您使用 tan(pitch) 来计算 Y 分量。您应该知道 Y 分量取决于 sin(pitch)。即,尝试更改

refY = tan(DEG2RAD(anglePitch));

技术

refY = sin(DEG2RAD(anglePitch));

细节:进入外观矩阵的数字应全部在 -1 到 +1 的范围内,并且如果您要检查提供给 refY 的值,并将您的音调放在外面-45 到 +45 度,你就会看到问题; tan() 以 +/-90 度运行至无穷大。

另请注意,将值从 int 转换为 float 毫无意义 在度数和弧度之间进行转换。转换只是为您提供新存储类型中最接近的等效值。例如,如果将整数 22 转换为浮点数,则会得到 22.0f,而如果将 33.3333f 转换为 int 类型,则会得到 33。在处理角度时,您确实应该坚持使用浮点数,除非您受到使用嵌入式处理器或其他东西的限制。这对于弧度尤其重要,其中整数增量代表(大约)57.3 度的跳跃。

'float' is a type, like int or double. radians and degrees are units of measure, both of which can be represented with any precision you want. i.e., there's no reason you can't have 22.5 degrees, and keep that value in a float.

a full rotation in radians is 2*pi, about 6.283, whereas a full rotation in degrees is 360. You can convert between them by dividing out the starting unit's full circle, then multiplying by the desired unit's full circle.

for example, to get from 90 degrees to radians, first divide out the degrees. 90 over 360 is 0.25 (note this value is in 'revolutions'). Now multiply that 0.25 by 6.283 to arrive at 1.571 radians.

follow up

the reason you're seeing your pitch cycle twice as fast as it should is precisely because you're using tan(pitch) to compute the Y component. What you should have is that the Y component depends on sin(pitch). i.e., try changing

refY = tan(DEG2RAD(anglePitch));

to

refY = sin(DEG2RAD(anglePitch));

a technical detail: the numbers that go into the look matrix should all be in the range of -1 to +1, and if you were to inspect the values you're feeding to refY, and run your pitch outside of -45 to +45 degrees, you'd see the problem; tan() runs off to infinity at +/-90 degrees.

also, note that casting a value from int to float in no sense converts between degrees and radians. casting just gives you the nearest equivalent value in the new storage type. for example, if you cast the integer 22 to floating point, you get 22.0f, whereas if you cast 33.3333f to type int, you'd be left with 33. when working with angles, you really should just stick with floating point, unless you're constrained by working with an embedded processor or something. this is especially important with radians, where whole number increments represent leaps of (about) 57.3 degrees.

ˉ厌 2024-10-27 22:16:54

假设您的 ref 组件旨在用作您的查看向量,我认为您需要的是

refY = sin(DEG2RAD(anglePitch)); 
XZfactor = cos(DEG2RAD(anglePitch));
refX = XZfactor*sin(DEG2RAD(angleYaw));
refZ = -XZfactor*cos(DEG2RAD(angleYaw));

Assuming that your ref components are intended to be used as your look-at vector, I think what you need is

refY = sin(DEG2RAD(anglePitch)); 
XZfactor = cos(DEG2RAD(anglePitch));
refX = XZfactor*sin(DEG2RAD(angleYaw));
refZ = -XZfactor*cos(DEG2RAD(angleYaw));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文