简体   繁体   中英

Calculate rotation to align object with two points in 3D space

I am working with motion capture data and I want to do "skinning" in processing. So basically with every two points I get from my data I have to add a 3D object in between (I'll be using a box for now and the placement and rotation coordinates are the center of the 3D object) and rotate it so that it is aligned in all three dimensions with the vector that connects the two points.

Here we can see on the left, the initial placed box between the two points and on the right the now correctly rotated box: 白色平行六面体的分割图像与图像左侧的红色对角线(左上至右下)不对齐,白色平行六面体与图像右侧的红线对齐

The only way I know of to rotate an object in processing is to use the rotateX(), rotateY(), rotateZ() functions, which rotate an object around the global(?) axes using euler angles.

Now I am seriously struggling with finding a way to calculate this rotation properly.

I have already written a function for calculating the angle between two vectors:

float calcVectorAngle(PVector p1, PVector p2) {
    return acos((p1.dot(p2)) / (mag(p1.x, p1.y, p1.z) * mag(p2.x, p2.y, p2.z)));
}

And I then tried to feed the vector (between the two points) combined with a unit vector for one of each rotation axis:

float x = calcVectorAngle(vector, new PVector(1,0,0));
float y = calcVectorAngle(vector, new PVector(0,1,0));
float z = calcVectorAngle(vector, new PVector(0,0,1));

But when I use these values to rotate the object the rotation is completely off.

A code example:

PVector p1;
PVector p2;
PVector boxSize = new PVector(500, 100, 100);

void setup() {
    size(1000,1000,P3D);
    p1 = new PVector(100, 100, 0);
    p2 = new PVector(900, 900, -1000);
}

void draw() {
    background(125);
    strokeWeight(2);
    stroke(255, 0, 0);

    pushMatrix();
    
    line(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z);

    PVector midPoint = calcMidPoint(p1, p2);
    translate(midPoint.x, midPoint.y, midPoint.z);

    PVector rotation = calcRotation(p1, p2);
    rotateX(rotation.x);
    rotateY(rotation.y);
    rotateZ(rotation.z);

    box(boxSize.x, boxSize.y, boxSize.z);
    popMatrix();
    
}


PVector calcMidPoint(PVector p1, PVector p2) {
    return new PVector((p1.x + p2.x) / 2, (p1.y + p2.y) / 2, (p1.z + p2.z) / 2);
}

PVector calcRotation(PVector p1, PVector p2) {
    PVector vector = new PVector();
    vector.sub(p2, p1, vector);
    float x = calcVectorAngle(vector, new PVector(1,0,0));
    float y = calcVectorAngle(vector, new PVector(0,1,0));
    float z = calcVectorAngle(vector, new PVector(0,0,1));
    return new PVector(x, y, z);
}

float calcVectorAngle(PVector p1, PVector p2) {
    return acos((p1.dot(p2)) / (mag(p1.x, p1.y, p1.z) * mag(p2.x, p2.y, p2.z)));
}

Now I am a little lost.

Unfortunately prcessing does not provide a function that rotates around an arbitrary axis. However you can specify a 4x4 rotation matrix (see Rotation matrix ) and apply the matrix with applyMatrix() :

void Rotate(float angle, float x, float y, float z) {
    float c = cos(angle);
    float s = sin(angle);
    applyMatrix(
        x*x*(1.0f-c)+c,   x*y*(1.0f-c)-z*s, x*z*(1.0f-c)+y*s, 0.0f,
        y*x*(1.0f-c)+z*s, y*y*(1.0f-c)+c,   y*z*(1.0f-c)-x*s, 0.0f,
        z*x*(1.0f-c)-y*s, z*y*(1.0f-c)+x*s, z*z*(1.0f-c)+c,   0.0f,
        0.0f,             0.0f,             0.0f,             1.0f );
}

The axis of rotation is the Cross product of the default direction of the box (1, 0, 0) and the direction along the line ( p2 - p1 ).
The angle of rotation is the acos of the Dot product of the normalized direction vectors:

PVector currentDirection = new PVector(1, 0, 0);
PVector newDirection = p2.copy().sub(p1).normalize(); 
    
PVector rotationAxis = currentDirection.cross(newDirection).normalize();
float rotationAngle = acos(currentDirection.dot(newDirection));

Rotate the box by the angle around the axis:

Rotate(rotationAngle, rotationAxis.x, rotationAxis.y, rotationAxis.z);

Complete example:

PVector p1, p2;
PVector boxSize = new PVector(500, 100, 100);

void setup() {
    size(1000,1000,P3D);
    p1 = new PVector(100, 100, 0);
    p2 = new PVector(900, 900, -1000);
}

void draw() {
    background(125);
    strokeWeight(2);
    stroke(255, 0, 0);

    line(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z);

    PVector midPoint = p1.copy().add(p2).mult(0.5);
    PVector currentDirection = new PVector(1, 0, 0);
    PVector newDirection = p2.copy().sub(p1).normalize(); 
    PVector rotationAxis = currentDirection.cross(newDirection).normalize();
    float rotationAngle = acos(currentDirection.dot(newDirection));

    pushMatrix();
    translate(midPoint.x, midPoint.y, midPoint.z);
    Rotate(rotationAngle, rotationAxis.x, rotationAxis.y, rotationAxis.z);
    box(boxSize.x, boxSize.y, boxSize.z);
    popMatrix();
}

void Rotate(float angle, float x, float y, float z) {
    float c = cos(angle);
    float s = sin(angle);
    applyMatrix(
        x*x*(1.0f-c)+c,   x*y*(1.0f-c)-z*s, x*z*(1.0f-c)+y*s, 0.0f,
        y*x*(1.0f-c)+z*s, y*y*(1.0f-c)+c,   y*z*(1.0f-c)-x*s, 0.0f,
        z*x*(1.0f-c)-y*s, z*y*(1.0f-c)+x*s, z*z*(1.0f-c)+c,   0.0f,
        0.0f,             0.0f,             0.0f,             1.0f );
}

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