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.