简体   繁体   中英

Rotate a 3D- Point around another one

I have a function in my program which rotates a point (x_p, y_p, z_p) around another point (x_m, y_m, z_m) by the angles w_nx & w_ny. The new coordinates are stored in global variables x_n, y_n, and z_n. Rotation around the y-axis (so changing value of w_nx - so that the y - values are not harmed) is working correctly, but as soon as I do a rotation around the x- or z- axis (changing the value of w_ny) the coordinates aren't accurate any more. I commented on the line I think my fault is in, but I can't figure out what's wrong with that code.

Can anyone help me?

void rotate(float x_m, float y_m, float z_m, float x_p, float y_p, float z_p, float w_nx ,float w_ny)
    {
        float z_b = z_p - z_m;
        float x_b = x_p - x_m;
        float y_b = y_p - y_m;
        float length_ = sqrt((z_b*z_b)+(x_b*x_b)+(y_b*y_b));
        float w_bx = asin(z_b/sqrt((x_b*x_b)+(z_b*z_b))) + w_nx;
        float w_by = asin(x_b/sqrt((x_b*x_b)+(y_b*y_b))) + w_ny; //<- there must be that fault
        x_n = cos(w_bx)*sin(w_by)*length_+x_m;
        z_n = sin(w_bx)*sin(w_by)*length_+z_m;
        y_n = cos(w_by)*length_+y_m;
    }

What the code almost does:

  • compute difference vector
  • convert vector into spherical coordinates
  • add w_nx and wn_y to the inclination and azimuth angle (see link for terminology)
  • convert modified spherical coordinates back into Cartesian coordinates

There are two problems:

  • the conversion is not correct, the computation you do is for two inclination vectors (one along the x axis, the other along the y axis)
  • even if computation were correct, transformation in spherical coordinates is not the same as rotating around two axis

Therefore in this case using matrix and vector math will help:

b = p - m
b = RotationMatrixAroundX(wn_x) * b
b = RotationMatrixAroundY(wn_y) * b
n = m + b

basic rotation matrices .

Try to use vector math. decide in which order you rotate, first along x, then along y perhaps.

If you rotate along z, [z' = z]

x' = x*cos a - y*sin a;
y' = x*sin a + y*cos a;  

The same repeated for y-axis: [y'' = y']

x'' = x'*cos b - z' * sin b;
z'' = x'*sin b + z' * cos b;  

Again rotating along x-axis: [x''' = x'']

y''' = y'' * cos c - z'' * sin c
z''' = y'' * sin c + z'' * cos c

And finally the question of rotating around some specific "point":

First subtract the point from the coordinates, then apply the rotations and finally add the point back to the result.

The problem, as far as I see, is a close relative to "gimbal lock". The angle w_ny can't be measured relative to the fixed xyz -coordinate system, but to the coordinate system that is rotated by applying the angle w_nx.

As kakTuZ observed, your code converts point to spherical coordinates. There's nothing inherently wrong with that -- with longitude and latitude one can reach all the places in Earth. And if one doesn't care about tilting the Earth equatorial plane relative to it's trajectory around the Sun, it's ok with me.

The result of not rotating the next reference axis along the first w_ny is that two points that are 1 km a part of each other at equator, move closer each other at the poles and at latitude of 90 degrees, they touch. Even though the apparent purpose is to keep them 1 km apart where ever they are rotated.

if you want to transform coordinate systems rather than only points you need 3 angles. But you are right - for transforming points 2 angles are enough. For details ask Wikipedia ...

But when you work with opengl you really should use opengl functions like glRotatef . These functions will be calculated on the GPU - not on the CPU as your function. The doc is here .

Like many others have said, you should use glRotatef to rotate it for rendering. For collision handling, you can obtain its world-space position by multiplying its position vector by the OpenGL ModelView matrix on top of the stack at the point of its rendering. Obtain that matrix with glGetFloatv, and then multiply it with either your own vector-matrix multiplication function, or use one of the many ones you can obtain easily online.

But, that would be a pain! Instead, look into using the GL feedback buffer. This buffer will simply store the points where the primitive would have been drawn instead of actually drawing the primitive, and then you can access them from there.
This is a good starting point.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM