定义为起点和长度 + 方向的线与起点和终点定义不同 - 方向计算错误?

Line defined as start and lenght + orientation differs from start and end point definition - wrong orientation calculation?

我正在尝试为我的小而简单的 3d-space 计算库编写一些 position/orientation 方法。但是我遇到了以下问题。

我将 3d 线存储为 startend 点。但是,也应该可以将其存储为 start 点和线的 length + orientation(这只是测试方向计算是否有效的一个很好的例子)。

orientation 我的意思是从初始“0”方向旋转(将 end 置于 start + [0,legth,0])。所以我首先按方向旋转 [0,length,0],然后向其添加 start 以获得 end 点。

问题是,我的方向计算在某处失败了。计算方向后我得到不同的终点。

我用的是Y轴朝上的左手坐标系,不过我觉得这里不重要

这是代码(我尝试按照你可以检查步骤是否正确的方式命名方法;如果你想自己编译,这里是full source code):

Point3D start = { 5.0f, 4.0f, 7.0f };
Point3D end = { 15.0f, 6.0f, 14.0f };

Point3D direction = (end - start);
std::wcout << L"Direction: "; direction.output();

float angle = Point3D(0.0f, 1.0f, 0.0f).getAngleToAnotherVectorInRadians(direction);
Point3D axis = direction.getCrossProduct(Point3D(0.0f, 1.0f, 0.0f)).getNormalized();
Quaternion o = Quaternion(AxisAngle(axis, angle));

std::wcout << L"\nAxisAngle: ";  AxisAngle(axis, angle).output();
std::wcout << L"\nOrientation: "; o.output();

//test - end2 should be equal to end
Point3D offset(0.0f, (end - start).getLengthAsVector(), 0.0f);
offset = o.rotatePoint(offset);
std::wcout << L"\nOffset: "; offset.output();
Point3D end2 = start + offset;
std::wcout << L"\nEnd2: "; end2.output();

代码产生这样的输出(当然没有注释):

Direction: {10, 2, 7} //looks ok
AxisAngle: {{-0.573462, 0, 0.819232}, 1.40839}
Orientation: {-0.371272, 0, 0.530388, 0.762132}
Offset: {-10, 2, -7} //Almost! It should be {10, 2, 7}
End2: {-5, 6, -9.53674e-07} //Wrong! It should be { 15, 6, 14 }

如果所有步骤都正常,但方法的实现有一些错误,我post这里是类的重要代码(这样你就可以重现问题):Point3D, AxisAngle, Quaternion.

我坚信问题出在我的主要步骤或 AxisAngle 计算中。 我认为 AxisAngle Quaternion 转换没问题(但我将错误的 AxisAngle 传递给 Quaternion 构造函数)。

Point3D:

struct Point3D {
protected:
    float x, y, z;

public:
    Point3D() : x(0.0f), y(0.0f), z(0.0f) {}
    Point3D(float x, float y, float z) : x(x), y(y), z(z) {}
    void output() { std::wcout << L"{" << x << L", " << y << L", " << z << L"}"; }
    Point3D operator-(const Point3D &point) const {
        Point3D temp;
        temp.setX(getX() - point.getX());
        temp.setY(getY() - point.getY());
        temp.setZ(getZ() - point.getZ());
        return temp;
    }
    Point3D operator+ (const Point3D &value) const {
        Point3D temp;
        temp.setX(getX() + value.getX());
        temp.setY(getY() + value.getY());
        temp.setZ(getZ() + value.getZ());
        return temp;
    }
    inline float getX() const { return x; } inline float getY() const { return y; } inline float getZ() const { return z; }
    inline void setX(float x) { this->x = x; } inline void setY(float y) { this->y = y; } inline void setZ(float z) { this->z = z; }

    inline float getLengthAsVector() const {
        return sqrt(x*x + y*y + z*z);
    }
    inline Point3D getCrossProduct(const Point3D &anotherVector) const {
        //based on: http://www.sciencehq.com/physics/vector-product-multiplying-vectors.html
        return Point3D(
            y * anotherVector.z - anotherVector.y * z,
            z * anotherVector.x - anotherVector.z * x,
            x * anotherVector.y - anotherVector.x * y
            );
    }
    inline float getDotProduct(const Point3D &anotherVector) const {
        //based on: https://www.ltcconline.net/greenl/courses/107/Vectors/DOTCROS.HTM
        return x * anotherVector.x + y * anotherVector.y + z * anotherVector.z;
    }
    inline float getAngleToAnotherVectorInRadians(const Point3D &anotherVector) const {
        //based on: http://math.stackexchange.com/questions/974178/how-to-calculate-the-angle-between-2-vectors-in-3d-space-given-a-preset-function
        return acos(getDotProduct(anotherVector) / (getLengthAsVector() * anotherVector.getLengthAsVector()));
    }
    Point3D getNormalized() const {
        float length = std::abs(sqrt(x*x + y*y + z*z));
        Point3D result(x / length, y / length, z / length);
        return result;
    }
};

AxisAngle:

class AxisAngle {
protected:
    Point3D axis;
    float angleInRadians;

public:
    AxisAngle(const AxisAngle &other) { axis = other.axis; angleInRadians = other.angleInRadians; }
    AxisAngle::AxisAngle(float x, float y, float z, float angleInRadians) {
        this->axis = Point3D(x, y, z);
        this->angleInRadians = angleInRadians;
    }
    AxisAngle::AxisAngle(const Point3D &axis, float angleInRadians) {
        this->axis = axis;
        this->angleInRadians = angleInRadians;
    }

    Point3D getAxis() const { return axis; }
    float getAngleInRadians() const { return angleInRadians; }

    void output() { std::wcout << L"{";  axis.output(); std::wcout << L", " << angleInRadians << L"}"; }

};

最后但同样重要的是,Quaternion

class Quaternion {
protected:
    float x; float y; float z; float w;

public:
    Quaternion() { x = 0.0f; y = 0.0f; z = 0.0f; w = 1.0f; }
    Quaternion(const Quaternion &other) { x = other.x; y = other.y; z = other.z; w = other.w; }
    Quaternion(float x, float y, float z, float w) { this->x = x; this->y = y; this->z = z; this->w = w; }
    Quaternion(const AxisAngle &axisAngle) {
        Point3D axis = axisAngle.getAxis();
        float angleInRadians = axisAngle.getAngleInRadians();
        x = sin(angleInRadians / 2) * axis.getX();
        y = sin(angleInRadians / 2) * axis.getY();
        z = sin(angleInRadians / 2) * axis.getZ();
        w = cos(angleInRadians / 2);
        normalizeIt();
    }

    float getLength() const {
        return sqrt(x*x + y*y + z*z + w*w);
    }

    void normalizeIt() {
        float length = getLength();
        x = x / length;
        y = y / length;
        z = z / length;
        w = w / length;
    }

    Quaternion getConjugated() const {
        return Quaternion(-x, -y, -z, w);
    }

    Quaternion multiply(Quaternion by) {
        //"R" for result
        float wR = w * by.getW() - x * by.getX() - y * by.getY() - z * by.getZ();
        float xR = x * by.getW() + w * by.getX() + y * by.getZ() - z * by.getY();
        float yR = y * by.getW() + w * by.getY() + z * by.getX() - x * by.getZ();
        float zR = z * by.getW() + w * by.getZ() + x * by.getY() - y * by.getX();
        return Quaternion(xR, yR, zR, wR);
    }

    //rotate Point3D p around [0,0,0] with this Quaternion
    Point3D rotatePoint(Point3D p) const {
        Quaternion temp = multiply(p).multiply(getConjugated());
        return Point3D(temp.getX(), temp.getY(), temp.getZ());

        //G: P' = Q(P-G)Q' + G <- to rotate P around G with Quaternion
    }

    Quaternion multiply(Point3D r) const {
        float wR = -x * r.getX() - y * r.getY() - z * r.getZ();
        float xR = w * r.getX() + y * r.getZ() - z * r.getY();
        float yR = w * r.getY() + z * r.getX() - x * r.getZ();
        float zR = w * r.getZ() + x * r.getY() - y * r.getX();
        return Quaternion(xR, yR, zR, wR);
    }

    inline float getX() const { return x; } inline void setX(float x) { this->x = x; }
    inline float getY() const { return y; } inline void setY(float y) { this->y = y; }
    inline float getZ() const { return z; } inline void setZ(float z) { this->z = z; }
    inline float getW() const { return w; } inline void setW(float w) { this->w = w; }

    void output() { std::wcout << L"{" << x << L", " << y << L", " << z << L", " << w << L"}"; }
};

以防有人问:我确实想使用四元数。它们在这里看起来可能不是 100% 需要的,但是将 3d 对象的方向存储为四元数在更复杂的情况下有很多好处计算(大多数游戏引擎/3d 软件也使用它 "under the mask")。

您的坐标轴方向错误。应该是:

Point3D axis = Point3D(0.0f, 1.0f, 0.0f).getCrossProduct(direction).getNormalized();

使用两个左手规则找出正确的顺序。