1
votes

I am trying to render 3d laser beams in opengl. I have two points (p1, p2) and a three-sided cylinder scaled by the distance between p1 and p2. The problem is that i can't get the cylinder to rotate properly. I've tried using atan2 to calculate the three axis angles and applying them to a matrix, so far with no success. The cylinder just doesn't follow the points at all. This is how i've done the rotation calculation:

p3 = p2 - p1
rotX = atan2(p3.y, p3.z)
rotY = atan2(p3.x, p3.z)
rotZ = atan2(p3.x, p3.y)

matrix.rotate(rotX, rotY, rotZ)
1
use a quaternion and convert it to a rotation matrixuser3235832
@willywonka_dailyblah: That won't help. The problem is the choice of angles. Using quaternions is just a different technique to get the same answer.Dietrich Epp

1 Answers

1
votes

This is one of those situations where a little bit of linear algebra goes a long way. You don't need trigonometry at all. Trigonometry is all about angles, and what you're doing is converting rectangular coordinates to angles and then back to rectangular coordinates. The two conversions are unnecessary.

Let's define v = p2 - p1.

Let's say your cylinder goes along the Z axis, from (0, 0, 0) to (0, 0, 1). We are trying to create a matrix A that gives us the following transformation:

A * (0, 0, 1) = v

Well, once you've written it out like that, it looks really simple, doesn't it! Because right-multiplying a matrix by a basis vector gives us one of the column vectors. So v must be one of the columns of the matrix.

A = [ ? ? v.x ]
    [ ? ? v.y ]
    [ ? ? v.z ]

Now we just pick two other column vectors that are normalized and orthogonal to v so we preserve the cross sectional shape of the laser. This is easy enough. Here is some code as it would be written in GLSL (you can use glm with C++ or gl-matrix with JavaScript to get similar syntax).

mat3 create_laser_matrix(vec3 v) {
    // Find a vector, ref, not parallel to v
    vec3 vmag = abs(v);
    vec3 ref;
    if (vmag.x <= vmag.y && vmag.x <= vmag.z) {
        ref = vec3(1.0, 0.0, 0.0);
    } else if (vmag.y <= vmag.z) {
        ref = vec3(0.0, 1.0, 0.0);
    } else {
        ref = vec3(0.0, 0.0, 1.0);
    }
    // Use ref to create two unit vectors, u1, u2, so {v, u1, u2} are orthogonal
    vec3 utemp = cross(v, ref);
    vec3 u1 = normalize(cross(v, utemp));
    vec3 u2 = normalize(cross(v, u1));
    return mat3(u1, u2, v);
}

To expand on the math: Trigonometry allows us to convert between angles and rectangular coordinates and vice versa, but it turns out that we don't need to do this at all. A "rotation matrix" is just a kind of orthogonal matrix (one with determinant 1 rather than -1), and an orthogonal matrix is just a matrix where the column vectors are orthogonal, and that just means that the dot product of any two column vectors is zero. No trigonometry needed. We aren't exactly after an orthogonal matrix because we need to "stretch out" the laser to connect the two points, but most of the same math applies.