简体   繁体   中英

Need rotation matrix for opengl 3D transformation

The problem is I have two points in 3D space where y+ is up, x+ is to the right, and z+ is towards you. I want to orientate a cylinder between them that is the length of of the distance between both points, so that both its center ends touch the two points. I got the cylinder to translate to the location at the center of the two points, and I need help coming up with a rotation matrix to apply to the cylinder, so that it is orientated the correct way. My transformation matrix for the entire thing looks like this:

translate(center point) * rotateX(some X degrees) * rotateZ(some Z degrees)

The translation is applied last, that way I can get it to the correct orientation before I translate it.

Here is what I have so far for this:

mat4 getTransformation(vec3 point, vec3 parent)
{
    float deltaX = point.x - parent.x;
    float deltaY = point.y - parent.y;
    float deltaZ = point.z - parent.z;

    float yRotation = atan2f(deltaZ, deltaX) * (180.0 / M_PI);
    float xRotation = atan2f(deltaZ, deltaY) * (180.0 / M_PI);
    float zRotation = atan2f(deltaX, deltaY) * (-180.0 / M_PI);
    if(point.y < parent.y)
    {
        zRotation = atan2f(deltaX, deltaY) * (180.0 / M_PI);
    }

    vec3 center = vec3((point.x + parent.x)/2.0, (point.y + parent.y)/2.0, (point.z + parent.z)/2.0);
    mat4 translation = Translate(center);
    return translation * RotateX(xRotation) * RotateZ(zRotation) * Scale(radius, 1, radius) * Scale(0.1, 0.1, 0.1);
}

I tried a solution given down below, but it did not seem to work at all

mat4 getTransformation(vec3 parent, vec3 point)
{
    // moves base of cylinder to origin and gives it unit scaling
    mat4 scaleFactor = Translate(0, 0.5, 0) * Scale(radius/2.0, 1/2.0, radius/2.0) * cylinderModel;

    float length = sqrtf(pow((point.x - parent.x), 2) + pow((point.y - parent.y), 2) + pow((point.z - parent.z), 2));
    vec3 direction = normalize(point - parent);
    float pitch = acos(direction.y);
    float yaw = atan2(direction.z, direction.x);

    return Translate(parent) * Scale(length, length, length) * RotateX(pitch) * RotateY(yaw) * scaleFactor;
}

After running the above code I get this: 在此输入图像描述

Every black point is a point with its parent being the point that spawned it (the one before it) I want the branches to fit into the points. Basically I am trying to implement the space colonization algorithm for random tree generation. I got most of it, but I want to map the branches to it so it looks good. I can use GL_LINES just to make a generic connection, but if I get this working it will look so much prettier. The algorithm is explained here .

Here is an image of what I am trying to do (pardon my paint skills)

Well, there's an arbitrary number of rotation matrices satisfying your constraints. But any will do. Instead of trying to figure out a specific rotation, we're just going to write down the matrix directly. Say your cylinder, when no transformation is applied, has its axis along the Z axis. So you have to transform the local space Z axis toward the direction between those two points. Ie z_t = normalize(p_1 - p_2) , where normalize(a) = a / length(a) .

Now we just need to make this a full 3 dimensional coordinate base. We start with an arbitrary vector that's not parallel to z_t. Say, one of (1,0,0) or (0,1,0) or (0,0,1); use the scalar product · (also called inner, or dot product) with z_t and use the vector for which the absolute value is the smallest, let's call this vector u . In pseudocode:

# Start with (1,0,0)
mindotabs = abs( z_t · (1,0,0) )
minvec = (1,0,0)
for u_ in (0,1,0), (0,0,1):
    dotabs = z_t · u_
    if dotabs < mindotabs:
        mindotabs = dotabs
        minvec = u_

u = minvec_

Then you orthogonalize that vector yielding a local y transformation y_t = normalize(u - z_t · u) .

Finally create the x transformation by taking the cross product x_t = z_t × y_t

To move the cylinder into place you combine that with a matching translation matrix.

Transformation matrices are effectively just the axes of the space you're "coming from" written down as if seen from the other space. So the resulting matrix, which is the rotation matrix you're looking for is simply the vectors x_t, y_t and z_t side by side as a matrix. OpenGL uses so called homogenuous matrices, so you have to pad it to a 4×4 form using a 0,0,0,1 bottommost row and rightmost column.

That you can load then into OpenGL; if using fixed functio using glMultMatrix to apply the rotation, or if using shader to multiply onto the matrix you're eventually pass to glUniform.

Begin with a unit length cylinder which has one of its ends, which I call C1 , at the origin (note that your image indicates that your cylinder has its center at the origin, but you can easily transform that to what I begin with). The other end, which I call C2 , is then at (0,1,0) .

I'd like to call your two points in world coordinates P1 and P2 and we want to locate C1 on P1 and C2 to P2 .

Start with translating the cylinder by P1 , which successfully locates C1 to P1 .

Then scale the cylinder by distance(P1, P2) , since it originally had length 1 .

The remaining rotation can be computed using spherical coordinates. If you're not familiar with this type of coordinate system: it's like GPS coordinates: two angles; one around the pole axis (in your case the world's Y-axis) which we typically call yaw , the other one is a pitch angle (in your case the X axis in model space). These two angles can be computed by converting P2-P1 (ie the local offset of P2 with respect to P1 ) into spherical coordinates. First rotate the object with the pitch angle around X, then with yaw around Y.

Something like this will do it (pseudo-code):

Matrix getTransformation(Point P1, Point P2) {
    float length = distance(P1, P2);
    Point direction = normalize(P2 - P1);
    float pitch = acos(direction.y);
    float yaw = atan2(direction.z, direction.x);

    return translate(P1) * scaleY(length) * rotateX(pitch) * rotateY(yaw);
}

Call the axis of the cylinder A . The second rotation (about X ) can't change the angle between A and X , so we have to get that angle right with the first rotation (about Z ).

Call the destination vector (the one between the two points) B . Take -acos(B X /B Y ), and that's the angle of the first rotation.

Take B again, ignore the X component, and look at its projection in the (Y, Z) plane. Take acos(B Z /B Y ), and that's the angle of the second rotation.

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