Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Given a surface normal, find rotation for 3D Plane

Tags:

math

3d

So I have a 3D Plane described by 2 Vectors:

P : a point which lies on the Plane
N : the surface normal for the Plane

And i have a very large, flat square Polygon, which I want to render to represent this Plane. I can easily translate the polygon to the given point, but then I need to find the proper rotation to apply to make the surface normal actually be the surface normal.

I tried a method mentioned else where which was:

1) Take any none parallel vector (V) to the normal (N), and take the cross product (W1)
2) Take the cross product of (W1) and (N) now (W2) and that is a Vector (V') which lies on the Plane

I then generate a rotation matrix based on (V') laying on the Plane, so that my polygon would be aligned with (V'). that worked, but it's clear that this method is not working correctly as a whole. The Polygon is not perfectly perpendicular to the surface normal.

Any ideas on how to generate the proper rotation?

like image 819
Adam Avatar asked Jan 19 '10 19:01

Adam


People also ask

How do you rotate a vector 90 degrees?

Normally rotating vectors involves matrix math, but there's a really simple trick for rotating a 2D vector by 90° clockwise: just multiply the X part of the vector by -1, and then swap X and Y values.


1 Answers

Some useful things about rotations:

  • Any three orthonormal vectors arranged as rows define a transformation into a new basis (a rotation into that basis).
  • The transpose of any rotation is its inverse.
  • So, any three orthonormal vectors arranged as columns define a rotation from some basis into your "world" reference frame.

So, the problem is to find any set of three orthonormal vectors and arrange them as

| x1 x2 x3  0 |
| y1 y2 y3  0 |
| z1 z2 z3  0 |
|  0  0  0  1 |

this is exactly what the method you described tries to do, if it doesn't work then there is a problem with your implementation.

We can obviously use your normal as (x1,y1,z1), but the problem is the system has infinitely many solutions for the remaining two vectors (although knowing one of them gives you the other, as the cross product). The following code ought to give a stable vector perpendicular to (x1,y1,z1):

float normal[3] = { ... };

int imin = 0;
for(int i=0; i<3; ++i)
    if(std::abs(normal[i]) < std::abs(normal[imin]))
        imin = i;

float v2[3] = {0,0,0};
float dt    = normal[imin];

v2[imin] = 1;
for(int i=0;i<3;i++)
    v2[i] -= dt*normal[i];

This basically uses Gram-Schmidt orthogonalisation with the dimension that is already most orthogonal to the normal vector. v3 can then be obtained by taking the cross product of normal and v2.

You may need to take some care setting up the rotation, it's about the origin so you need to apply the translation after the rotation and it's for column vectors rather than row vectors. If you're using OpenGL watch that OpenGL takes arrays in column major order (rather than C's row major order) so you may need to transpose.

I'm afraid I haven't tested the above, I've merely nabbed it from some code I wrote a while ago and adapted it to your problem! Hopefully I haven't forgotten any details.

Edit: I did forget something :)

The matrix above assumes your normal to the polygon is along the x-axis, and I have a sneaking suspicion it won't be, all you need to do is put the "normal" vector in the correct column of the rotation matrix, and v2/v3 in the other two columns. So if the normal to your polygon is along the z axis, then the normal goes in the 3rd column and v2/v3 go in the first two columns.

Sorry if that causes any confusion.

like image 91
Adam Bowen Avatar answered Nov 26 '22 10:11

Adam Bowen