005-3D数学中的多坐标系详解与实例

3D数学中的多坐标系详解与实例

1. 坐标系基础概念

在3D应用开发中,我们需要处理多种不同的坐标系统,每种坐标系都有其特定用途和优势。理解这些坐标系统及其转换是掌握3D数学的基础。

1.1 坐标系的表示

坐标系通常由原点和一组基向量组成,在3D空间中一般使用三个相互垂直的轴:X轴、Y轴和Z轴。

cpp

// 基本坐标系类
class CoordinateSystem {
protected:
    Point3D origin;      // 原点
    Vector3D xAxis;      // X轴方向
    Vector3D yAxis;      // Y轴方向
    Vector3D zAxis;      // Z轴方向
    
public:
    CoordinateSystem()
        : origin(0,0,0), 
          xAxis(1,0,0), 
          yAxis(0,1,0), 
          zAxis(0,0,1) {}
          
    CoordinateSystem(const Point3D& origin, 
                     const Vector3D& xAxis, 
                     const Vector3D& yAxis, 
                     const Vector3D& zAxis)
        : origin(origin), 
          xAxis(xAxis.normalize()), 
          yAxis(yAxis.normalize()), 
          zAxis(zAxis.normalize()) {}
    
    // 获取坐标系变换矩阵
    Matrix4x4 getTransformMatrix() const {
        Matrix4x4 matrix;
        
        // 旋转部分
        matrix.m[0][0] = xAxis.x;
        matrix.m[0][1] = xAxis.y;
        matrix.m[0][2] = xAxis.z;
        
        matrix.m[1][0] = yAxis.x;
        matrix.m[1][1] = yAxis.y;
        matrix.m[1][2] = yAxis.z;
        
        matrix.m[2][0] = zAxis.x;
        matrix.m[2][1] = zAxis.y;
        matrix.m[2][2] = zAxis.z;
        
        // 平移部分
        matrix.m[0][3] = origin.x;
        matrix.m[1][3] = origin.y;
        matrix.m[2][3] = origin.z;
        
        return matrix;
    }
    
    // 从这个坐标系转换到另一个坐标系的变换矩阵
    Matrix4x4 getTransformTo(const CoordinateSystem& target) const {
        Matrix4x4 thisToWorld = this->getTransformMatrix();
        Matrix4x4 worldToTarget = inverseMatrix(target.getTransformMatrix());
        
        return worldToTarget * thisToWorld;
    }
};

2. 世界坐标系 (World Coordinate System)

2.1 定义和用途

世界坐标系是3D场景中的全局参考系统,所有其他坐标系最终都会参照世界坐标系。它通常是固定不变的,提供了一个共同的空间来放置和测量所有物体。

cpp

class WorldCoordinateSystem : public CoordinateSystem {
public:
    WorldCoordinateSystem() : CoordinateSystem() {
        // 通常采用右手坐标系,z轴向上
        // 或者根据应用需求自定义
    }
    
    // 将点从任意坐标系转换到世界坐标系
    Point3D transformPointToWorld(const Point3D& point, const CoordinateSystem& sourceCS) const {
        Matrix4x4 transformMatrix = sourceCS.getTransformMatrix();
        return transformMatrix.transformPoint(point);
    }
    
    // 将向量从任意坐标系转换到世界坐标系
    Vector3D transformVectorToWorld(const Vector3D& vector, const CoordinateSystem& sourceCS) const {
        Matrix4x4 transformMatrix = sourceCS.getTransformMatrix();
        // 向量转换不考虑平移
        return transformMatrix.transformVector(vector);
    }
};

2.2 应用示例:管理全局场景

cpp

class Scene {
private:
    WorldCoordinateSystem worldCS;
    std::vector<GameObject*> gameObjects;
    
public:
    Scene() {}
    
    void addObject(GameObject* obj) {
        gameObjects.push_back(obj);
    }
    
    void updateAll(float deltaTime) {
        for (auto* obj : gameObjects) {
            obj->update(deltaTime);
            
            // 确保所有对象位置都相对于世界坐标系
            obj->updateWorldTransform(worldCS);
        }
    }
    
    void renderAll(Camera& camera) {
        for (auto* obj : gameObjects) {
            // 使用相机视图渲染每个对象
            obj->render(camera);
        }
    }
};

3. 物体坐标系 (Object Coordinate System)

3.1 定义和用途

物体坐标系(也称为局部坐标系)是相对于特定物体定义的坐标系。它使得物体的操作(如旋转、缩放)更加直观。物体的所有顶点通常首先在物体坐标系中定义,然后通过变换矩阵转换到世界坐标系。

cpp

class ObjectCoordinateSystem : public CoordinateSystem {
private:
    Matrix4x4 localToWorldMatrix;
    Matrix4x4 worldToLocalMatrix;
    bool matricesNeedUpdate;
    
public:
    ObjectCoordinateSystem() 
        : CoordinateSystem(), matricesNeedUpdate(true) {}
    
    ObjectCoordinateSystem(const Point3D& position, 
                           const Quaternion& rotation, 
                           const Vector3D& scale)
        : CoordinateSystem(), matricesNeedUpdate(true) {
        // 使用位置、旋转和缩放设置坐标系
        setTransform(position, rotation, scale);
    }
    
    // 设置物体变换
    void setTransform(const Point3D& position, 
                      const Quaternion& rotation, 
                      const Vector3D& scale) {
        origin = position;
        
        // 从四元数获取旋转轴
        Matrix4x4 rotMatrix = rotation.toRotationMatrix();
        
        // 应用缩放和旋转
        xAxis = rotMatrix.transformVector(Vector3D(scale.x, 0, 0));
        yAxis = rotMatrix.transformVector(Vector3D(0, scale.y, 0));
        zAxis = rotMatrix.transformVector(Vector3D(0, 0, scale.z));
        
        matricesNeedUpdate = true;
    }
    
    // 更新变换矩阵
    void updateMatrices() {
        if (!matricesNeedUpdate) return;
        
        localToWorldMatrix = getTransformMatrix();
        worldToLocalMatrix = inverseMatrix(localToWorldMatrix);
        
        matricesNeedUpdate = false;
    }
    
    // 从局部坐标转换到世界坐标
    Point3D localToWorld(const Point3D& localPoint) {
        updateMatrices();
        return localToWorldMatrix.transformPoint(localPoint);
    }
    
    // 从世界坐标转换到局部坐标
    Point3D worldToLocal(const Point3D& worldPoint) {
        updateMatrices();
        return worldToLocalMatrix.transformPoint(worldPoint);
    }
    
    // 从局部向量转换到世界向量
    Vector3D localToWorldVector(const Vector3D& localVector) {
        updateMatrices();
        return localToWorldMatrix.transformVector(localVector);
    }
    
    // 从世界向量转换到局部向量
    Vector3D worldToLocalVector(const Vector3D& worldVector) {
        updateMatrices();
        return worldToLocalMatrix.transformVector(worldVector);
    }
};

3.2 应用示例:变换层次结构

cpp

class Transform {
private:
    Point3D position;
    Quaternion rotation;
    Vector3D scale;
    
    Transform* parent;
    std::vector<Transform*> children;
    
    ObjectCoordinateSystem localCS;
    Matrix4x4 localToWorldMatrix;
    Matrix4x4 worldToLocalMatrix;
    
    bool matricesNeedUpdate;
    
public:
    Transform() 
        : position(0,0,0), 
          rotation(), 
          scale(1,1,1), 
          parent(nullptr), 
          matricesNeedUpdate(true) {}
    
    // 设置父变换
    void setParent(Transform* newParent) {
        if (parent) {
            // 从旧父级中移除
            parent->removeChild(this);
        }
        
        parent = newParent;
        
        if (parent) {
            // 添加到新父级
            parent->addChild(this);
        }
        
        matricesNeedUpdate = true;
    }
    
    // 添加子变换
    void addChild(Transform* child) {
        children.push_back(child);
    }
    
    // 移除子变换
    void removeChild(Transform* child) {
        auto it = std::find(children.begin(), children.end(), child);
        if (it != children.end()) {
            children.erase(it);
        }
    }
    
    // 设置局部变换
    void setLocalTransform(const Point3D& pos, const Quaternion& rot, const Vector3D& scl) {
        position = pos;
        rotation = rot;
        scale = scl;
        
        localCS.setTransform(position, rotation, scale);
        matricesNeedUpdate = true;
        
        // 通知所有子级它们的全局变换需要更新
        for (auto* child : children) {
            child->matricesNeedUpdate = true;
        }
    }
    
    // 更新全局变换矩阵
    void updateGlobalMatrices() {
        if (!matricesNeedUpdate) return;
        
        // 更新局部坐标系矩阵
        localCS.updateMatrices();
        
        if (parent) {
            // 确保父级矩阵是最新的
            parent->updateGlobalMatrices();
            
            // 局部到世界 = 父级局部到世界 * 自身局部到父级
            Matrix4x4 parentLocalToWorld = parent->getLocalToWorldMatrix();
            Matrix4x4 localToParent = localCS.getTransformMatrix();
            
            localToWorldMatrix = parentLocalToWorld * localToParent;
        } else {
            // 没有父级,局部空间等同于世界空间
            localToWorldMatrix = localCS.getTransformMatrix();
        }
        
        // 计算逆矩阵
        worldToLocalMatrix = inverseMatrix(localToWorldMatrix);
        
        matricesNeedUpdate = false;
    }
    
    // 获取局部到世界变换矩阵
    Matrix4x4 getLocalToWorldMatrix() {
        updateGlobalMatrices();
        return localToWorldMatrix;
    }
    
    // 获取世界到局部变换矩阵
    Matrix4x4 getWorldToLocalMatrix() {
        updateGlobalMatrices();
        return worldToLocalMatrix;
    }
    
    // 将点从局部坐标转换到世界坐标
    Point3D transformPointToWorld(const Point3D& localPoint) {
        updateGlobalMatrices();
        return localToWorldMatrix.transformPoint(localPoint);
    }
    
    // 将点从世界坐标转换到局部坐标
    Point3D transformPointToLocal(const Point3D& worldPoint) {
        updateGlobalMatrices();
        return worldToLocalMatrix.transformPoint(worldPoint);
    }
};

4. 相机坐标系 (Camera Coordinate System)

4.1 定义和用途

相机坐标系是以相机为原点的坐标系,通常也称为视图空间。在这个坐标系中,相机位于原点,看向特定的方向(通常是-Z轴),上方向是Y轴。

cpp

class CameraCoordinateSystem : public CoordinateSystem {
public:
    CameraCoordinateSystem() : CoordinateSystem() {
        // 默认相机看向-Z方向,上方向是Y轴
        xAxis = Vector3D(1, 0, 0);
        yAxis = Vector3D(0, 1, 0);
        zAxis = Vector3D(0, 0, -1);  // 注意:通常相机看向-Z方向
    }
    
    CameraCoordinateSystem(const Point3D& position, 
                          const Point3D& target, 
                          const Vector3D& up) 
        : CoordinateSystem() {
        lookAt(position, target, up);
    }
    
    // 设置相机观察方向
    void lookAt(const Point3D& position, 
                const Point3D& target, 
                const Vector3D& worldUp) {
        origin = position;
        
        // 计算z轴(观察方向的反方向)
        zAxis = Vector3D(
            position.x - target.x,
            position.y - target.y,
            position.z - target.z
        ).normalize();
        
        // 计算x轴(右方向)
        xAxis = cross(worldUp.normalize(), zAxis).normalize();
        
        // 计算y轴(上方向)
        yAxis = cross(zAxis, xAxis).normalize();
    }
    
    // 获取视图矩阵(世界到相机空间的变换)
    Matrix4x4 getViewMatrix() const {
        Matrix4x4 result;
        
        // 旋转部分(使用坐标轴的转置)
        result.m[0][0] = xAxis.x;
        result.m[0][1] = yAxis.x;
        result.m[0][2] = zAxis.x;
        
        result.m[1][0] = xAxis.y;
        result.m[1][1] = yAxis.y;
        result.m[1][2] = zAxis.y;
        
        result.m[2][0] = xAxis.z;
        result.m[2][1] = yAxis.z;
        result.m[2][2] = zAxis.z;
        
        // 平移部分
        result.m[0][3] = -dot(xAxis, Vector3D(origin.x, origin.y, origin.z));
        result.m[1][3] = -dot(yAxis, Vector3D(origin.x, origin.y, origin.z));
        result.m[2][3] = -dot(zAxis, Vector3D(origin.x, origin.y, origin.z));
        
        return result;
    }
    
    // 世界坐标点转换到相机坐标系
    Point3D worldToCamera(const Point3D& worldPoint) const {
        Matrix4x4 viewMatrix = getViewMatrix();
        return viewMatrix.transformPoint(worldPoint);
    }
    
    // 相机坐标点转换到世界坐标系
    Point3D cameraToWorld(const Point3D& cameraPoint) const {
        Matrix4x4 viewMatrix = getViewMatrix();
        Matrix4x4 worldMatrix = inverseMatrix(viewMatrix);
        return worldMatrix.transformPoint(cameraPoint);
    }
};

4.2 投影变换

相机坐标系中的点需要进一步通过投影变换转换到裁剪空间,然后再到标准化设备坐标系(NDC)。

cpp

class ProjectionTransform {
private:
    Matrix4x4 projectionMatrix;
    
public:
    // 构建透视投影矩阵
    void setPerspectiveProjection(float fovY, float aspectRatio, 
                                 float nearPlane, float farPlane) {
        float tanHalfFovY = tan(fovY / 2.0f);
        
        projectionMatrix = Matrix4x4();  // 重置为单位矩阵
        
        projectionMatrix.m[0][0] = 1.0f / (aspectRatio * tanHalfFovY);
        projectionMatrix.m[1][1] = 1.0f / tanHalfFovY;
        projectionMatrix.m[2][2] = -(farPlane + nearPlane) / (farPlane - nearPlane);
        projectionMatrix.m[2][3] = -(2.0f * farPlane * nearPlane) / (farPlane - nearPlane);
        projectionMatrix.m[3][2] = -1.0f;
        projectionMatrix.m[3][3] = 0.0f;
    }
    
    // 构建正交投影矩阵
    void setOrthographicProjection(float left, float right, float bottom, 
                                  float top, float nearPlane, float farPlane) {
        projectionMatrix = Matrix4x4();  // 重置为单位矩阵
        
        float width = right - left;
        float height = top - bottom;
        float depth = farPlane - nearPlane;
        
        projectionMatrix.m[0][0] = 2.0f / width;
        projectionMatrix.m[1][1] = 2.0f / height;
        projectionMatrix.m[2][2] = -2.0f / depth;
        
        projectionMatrix.m[0][3] = -(right + left) / width;
        projectionMatrix.m[1][3] = -(top + bottom) / height;
        projectionMatrix.m[2][3] = -(farPlane + nearPlane) / depth;
    }
    
    // 获取投影矩阵
    Matrix4x4 getProjectionMatrix() const {
        return projectionMatrix;
    }
    
    // 相机空间点转换到裁剪空间
    Point4D cameraToClip(const Point3D& cameraPoint) const {
        return projectionMatrix.transformPoint4D(
            Point4D(cameraPoint.x, cameraPoint.y, cameraPoint.z, 1.0f)
        );
    }
    
    // 裁剪空间点转换到标准化设备坐标(NDC)
    Point3D clipToNDC(const Point4D& clipPoint) const {
        if (fabs(clipPoint.w) < 1e-6f) {
            // 防止除以零
            return Point3D(
                clipPoint.x > 0 ? 1.0f : -1.0f,
                clipPoint.y > 0 ? 1.0f : -1.0f,
                clipPoint.z > 0 ? 1.0f : -1.0f
            );
        }
        
        float invW = 1.0f / clipPoint.w;
        return Point3D(
            clipPoint.x * invW,
            clipPoint.y * invW,
            clipPoint.z * invW
        );
    }
};

4.3 应用示例:渲染管线

cpp

class Camera {
private:
    CameraCoordinateSystem cameraCS;
    ProjectionTransform projection;
    
    int viewportWidth;
    int viewportHeight;
    
public:
    Camera(int width, int height) 
        : viewportWidth(width), viewportHeight(height) {
        // 设置默认投影
        projection.setPerspectiveProjection(
            3.14159f/4.0f,  // 45度视场角
            (float)width / height,  // 宽高比
            0.1f,  // 近平面
            1000.0f  // 远平面
        );
    }
    
    void setLookAt(const Point3D& position, 
                  const Point3D& target, 
                  const Vector3D& up) {
        cameraCS.lookAt(position, target, up);
    }
    
    // 获取视图矩阵
    Matrix4x4 getViewMatrix() const {
        return cameraCS.getViewMatrix();
    }
    
    // 获取投影矩阵
    Matrix4x4 getProjectionMatrix() const {
        return projection.getProjectionMatrix();
    }
    
    // 将世界坐标转换为屏幕坐标
    Point2D worldToScreen(const Point3D& worldPoint) {
        // 世界 -> 相机
        Point3D cameraPoint = cameraCS.worldToCamera(worldPoint);
        
        // 相机 -> 裁剪
        Point4D clipPoint = projection.cameraToClip(cameraPoint);
        
        // 裁剪 -> NDC
        Point3D ndcPoint = projection.clipToNDC(clipPoint);
        
        // NDC -> 屏幕
        // NDC空间是[-1,1],屏幕空间是[0,width]x[0,height]
        float screenX = (ndcPoint.x + 1.0f) * 0.5f * viewportWidth;
        float screenY = (1.0f - ndcPoint.y) * 0.5f * viewportHeight;  // Y轴翻转
        
        return Point2D(screenX, screenY);
    }
    
    // 从屏幕坐标和深度值重建世界坐标
    Point3D screenToWorld(const Point2D& screenPoint, float depth) {
        // 屏幕 -> NDC
        float ndcX = (2.0f * screenPoint.x / viewportWidth) - 1.0f;
        float ndcY = 1.0f - (2.0f * screenPoint.y / viewportHeight);  // Y轴翻转
        float ndcZ = depth;  // 假设depth已经在[-1,1]范围内
        
        // NDC -> 裁剪
        Matrix4x4 invProj = inverseMatrix(projection.getProjectionMatrix());
        Point4D clipPoint(ndcX, ndcY, ndcZ, 1.0f);
        Point4D worldPoint = invProj.transformPoint4D(clipPoint);
        
        // 将w分量归一化回来
        worldPoint.x /= worldPoint.w;
        worldPoint.y /= worldPoint.w;
        worldPoint.z /= worldPoint.w;
        worldPoint.w = 1.0f;
        
        // 裁剪 -> 相机 -> 世界
        Matrix4x4 invView = inverseMatrix(cameraCS.getViewMatrix());
        return invView.transformPoint(Point3D(worldPoint.x, worldPoint.y, worldPoint.z));
    }
};

5. 惯性坐标系 (Inertial Coordinate System)

5.1 定义和用途

惯性坐标系是用于物理模拟的参考系统,在该系统中牛顿运动定律适用。通常,世界坐标系被视为惯性坐标系。

cpp

class InertialCoordinateSystem : public CoordinateSystem {
private:
    Vector3D gravity;  // 重力加速度
    
public:
    InertialCoordinateSystem() 
        : CoordinateSystem(), gravity(0, -9.81f, 0) {}  // 默认重力方向为-Y
    
    InertialCoordinateSystem(const Vector3D& gravityDirection, float gravityStrength) 
        : CoordinateSystem() {
        setGravity(gravityDirection, gravityStrength);
    }
    
    // 设置重力
    void setGravity(const Vector3D& direction, float strength) {
        gravity = direction.normalize() * strength;
    }
    
    // 获取重力加速度
    Vector3D getGravity() const {
        return gravity;
    }
    
    // 应用物理定律的基本方法
    void applyForce(PhysicsObject& object, const Vector3D& force, float deltaTime) {
        // F = ma,所以a = F/m
        Vector3D acceleration = force / object.getMass();
        
        // 更新速度:v = v0 + a*t
        object.setVelocity(object.getVelocity() + acceleration * deltaTime);
        
        // 更新位置:p = p0 + v*t
        object.setPosition(object.getPosition() + object.getVelocity() * deltaTime);
    }
    
    // 应用重力
    void applyGravity(PhysicsObject& object, float deltaTime) {
        Vector3D gravityForce = gravity * object.getMass();
        applyForce(object, gravityForce, deltaTime);
    }
};

5.2 应用示例:物理模拟

cpp

class PhysicsEngine {
private:
    InertialCoordinateSystem physicsCS;
    std::vector<PhysicsObject*> physicsObjects;
    
public:
    PhysicsEngine() {}
    
    void addObject(PhysicsObject* obj) {
        physicsObjects.push_back(obj);
    }
    
    void update(float deltaTime) {
        for (auto* obj : physicsObjects) {
            if (!obj->isStatic()) {
                // 应用重力
                physicsCS.applyGravity(*obj, deltaTime);
                
                // 应用其他作用力
                for (const auto& force : obj->getForces()) {
                    physicsCS.applyForce(*obj, force, deltaTime);
                }
                
                // 清除力
       
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小宝哥Code

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

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

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

打赏作者

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

抵扣说明:

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

余额充值