004-3D数学中的笛卡尔坐标系详解及实例

3D数学中的笛卡尔坐标系详解及实例

1. 笛卡尔坐标系基础

1.1 定义与起源

笛卡尔坐标系是由法国数学家笛卡尔(René Descartes)提出的空间表示方法,是现代计算机图形学、游戏开发和其他3D应用的基础。在三维空间中,笛卡尔坐标系由三个相互垂直的坐标轴组成:

  • X轴:通常表示水平方向
  • Y轴:在2D中表示垂直方向;在3D中,根据使用的约定不同可表示垂直或深度方向
  • Z轴:在3D中增加的第三个维度,通常表示深度

1.2 右手坐标系与左手坐标系

在3D笛卡尔坐标系中,有两种主要的约定:

右手坐标系:用右手握住Z轴,拇指指向Z轴正方向,食指指向X轴正方向,此时中指自然指向的方向为Y轴正方向。

左手坐标系:用左手握住Z轴,拇指指向Z轴正方向,食指指向X轴正方向,此时中指自然指向的方向为Y轴正方向。

cpp

// 坐标系类型枚举
enum CoordinateSystemType {
    RIGHT_HANDED,
    LEFT_HANDED
};

// 确定使用的坐标系类型
const CoordinateSystemType COORDINATE_SYSTEM = RIGHT_HANDED;

1.3 不同领域的坐标系约定

不同的领域和软件系统使用不同的坐标系约定:

  • 计算机图形学/OpenGL:通常使用右手坐标系,Z轴指向屏幕外(远离观察者)
  • DirectX:使用左手坐标系,Z轴指向屏幕内(朝向观察者)
  • Unity3D:使用左手坐标系,Y轴向上,Z轴向前
  • Unreal Engine:使用左手坐标系,Z轴向上,Y轴向前
  • 航空航天:通常使用右手坐标系,Z轴向上,X轴向前

2. 点与向量

2.1 点与向量的区别

在3D数学中,点和向量是两个基本概念:

  • 点(Point):表示空间中的一个位置,有具体的坐标值
  • 向量(Vector):表示空间中的方向和大小,没有具体的位置

虽然它们在代码实现中可能使用相似的结构,但在概念和操作上有明显区别。

cpp

struct Point3D {
    float x, y, z;
    
    Point3D() : x(0.0f), y(0.0f), z(0.0f) {}
    Point3D(float x, float y, float z) : x(x), y(y), z(z) {}
};

struct Vector3D {
    float x, y, z;
    
    Vector3D() : x(0.0f), y(0.0f), z(0.0f) {}
    Vector3D(float x, float y, float z) : x(x), y(y), z(z) {}
    
    // 向量可以有长度
    float length() const {
        return std::sqrt(x*x + y*y + z*z);
    }
    
    // 向量可以被归一化
    Vector3D normalize() const {
        float len = length();
        if (len < 1e-6f) return Vector3D(0, 0, 0);
        return Vector3D(x/len, y/len, z/len);
    }
};

// 点与点之间的减法产生向量
Vector3D vectorFromPoints(const Point3D& from, const Point3D& to) {
    return Vector3D(to.x - from.x, to.y - from.y, to.z - from.z);
}

// 点加向量得到新的点
Point3D translatePoint(const Point3D& point, const Vector3D& displacement) {
    return Point3D(point.x + displacement.x, 
                  point.y + displacement.y, 
                  point.z + displacement.z);
}

2.2 向量操作

在3D数学中,向量有几个基本操作:

2.2.1 向量加法和减法

cpp

Vector3D operator+(const Vector3D& a, const Vector3D& b) {
    return Vector3D(a.x + b.x, a.y + b.y, a.z + b.z);
}

Vector3D operator-(const Vector3D& a, const Vector3D& b) {
    return Vector3D(a.x - b.x, a.y - b.y, a.z - b.z);
}
2.2.2 向量标量乘法

cpp

Vector3D operator*(const Vector3D& v, float scalar) {
    return Vector3D(v.x * scalar, v.y * scalar, v.z * scalar);
}

Vector3D operator*(float scalar, const Vector3D& v) {
    return v * scalar;
}
2.2.3 向量点积

点积(也称为内积)产生一个标量值,表示两个向量的"相似度"。

cpp

float dot(const Vector3D& a, const Vector3D& b) {
    return a.x * b.x + a.y * b.y + a.z * b.z;
}

点积的几个重要性质:

  • 如果两个向量垂直,点积为0
  • 如果两个向量方向相同,点积为正
  • 如果两个向量方向相反,点积为负
  • 公式:a·b = |a|·|b|·cos(θ),其中θ是两个向量之间的角度
2.2.4 向量叉积

叉积(也称为外积)产生一个新的向量,该向量垂直于原始两个向量所在的平面。

cpp

Vector3D cross(const Vector3D& a, const Vector3D& b) {
    return Vector3D(
        a.y * b.z - a.z * b.y,
        a.z * b.x - a.x * b.z,
        a.x * b.y - a.y * b.x
    );
}

叉积在右手坐标系和左手坐标系中的方向是相反的。在右手坐标系中,a×b的方向遵循右手法则。

2.2.5 求两个向量间的角度

cpp

float angleBetweenVectors(const Vector3D& a, const Vector3D& b) {
    float dotProduct = dot(a, b);
    float magnitudeA = a.length();
    float magnitudeB = b.length();
    
    // 防止除以零和浮点精度问题
    if (magnitudeA < 1e-6f || magnitudeB < 1e-6f) {
        return 0.0f;
    }
    
    float cosTheta = dotProduct / (magnitudeA * magnitudeB);
    // 确保cosTheta在[-1, 1]范围内,避免因精度问题导致的acos域错误
    cosTheta = std::max(-1.0f, std::min(1.0f, cosTheta));
    
    return std::acos(cosTheta);
}

3. 坐标变换

3.1 坐标系之间的转换

在不同的坐标系之间转换坐标是常见操作。比如,从右手坐标系转换到左手坐标系,或从一种约定转换到另一种约定。

cpp

// 从右手坐标系(OpenGL风格)转换到左手坐标系(DirectX风格)
Point3D convertRightToLeftHanded(const Point3D& point) {
    // 典型转换是翻转Z轴
    return Point3D(point.x, point.y, -point.z);
}

// 从Unity坐标系(Y向上,Z向前)转换到Unreal坐标系(Z向上,Y向前)
Point3D convertUnityToUnreal(const Point3D& unityPoint) {
    // Unity: X右,Y上,Z前
    // Unreal: X前,Y右,Z上
    return Point3D(unityPoint.z, unityPoint.x, unityPoint.y);
}

3.2 局部坐标系与世界坐标系

在3D应用中,通常会使用多个坐标系:

  • 局部坐标系:相对于对象自身的坐标系
  • 世界坐标系:全局参考坐标系

cpp

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小宝哥Code

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值