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

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

本文关键字:定义 方向 起点 错误 计算 终点      更新时间:2023-10-16

我正在尝试为我的小&简单的三维空间计算库。但是我被下面的问题卡住了。

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

这里的orientation是指从初始的"0"方向旋转(将end置于start + [0,legth,0])。所以我首先旋转[0,length,0]的方向,然后添加start,得到end点。

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

我使用左手坐标系, y轴向上指向,但我认为这在这里不重要。

下面是代码(我试图以您可以检查步骤是否正确的方式命名方法;这里是完整的源代码,如果你想自己编译的话):

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 }

如果所有步骤都是好的,但是在方法的实现中有一些错误,我在这里发布了类的重要代码(所以你可以重现问题):Point3D, AxisAngle, Quaternion .

我高度相信问题存在于我的主要步骤或AxisAngle计算中。我认为AxisAngleQuaternion转换是可以的(但我将错误的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软件也在"掩模下"使用它)。

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

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

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