003-3D数学在计算机图形应用领域的详解与实例

3D数学在计算机图形应用领域的详解与实例

目录

  1. 向量基础及应用
  2. 矩阵变换
  3. 四元数与旋转
  4. 射线追踪与交叉检测
  5. 参数曲线与曲面
  6. 碰撞检测
  7. 物理模拟
  8. 光照与着色
  9. 三维重建
  10. 图形渲染管线

1. 向量基础及应用

向量是3D数学的基础,用于表示位置、方向、速度等。

1.1 向量的基本操作

cpp

#include <iostream>
#include <cmath>

class Vector3 {
public:
    float x, y, z;
    
    Vector3() : x(0.0f), y(0.0f), z(0.0f) {}
    Vector3(float x, float y, float z) : x(x), y(y), z(z) {}
    
    // 向量加法
    Vector3 operator+(const Vector3& v) const {
        return Vector3(x + v.x, y + v.y, z + v.z);
    }
    
    // 向量减法
    Vector3 operator-(const Vector3& v) const {
        return Vector3(x - v.x, y - v.y, z - v.z);
    }
    
    // 标量乘法
    Vector3 operator*(float scalar) const {
        return Vector3(x * scalar, y * scalar, z * scalar);
    }
    
    // 点积
    float dot(const Vector3& v) const {
        return x * v.x + y * v.y + z * v.z;
    }
    
    // 叉积
    Vector3 cross(const Vector3& v) const {
        return Vector3(
            y * v.z - z * v.y,
            z * v.x - x * v.z,
            x * v.y - y * v.x
        );
    }
    
    // 向量长度
    float length() const {
        return std::sqrt(x * x + y * y + z * z);
    }
    
    // 单位化向量
    Vector3 normalize() const {
        float len = length();
        if (len < 1e-6f) return Vector3(0, 0, 0);
        return Vector3(x / len, y / len, z / len);
    }
};

1.2 实际应用:摄像机朝向计算

cpp

// 计算摄像机朝向向量
Vector3 calculateCameraDirection(const Vector3& cameraPosition, const Vector3& targetPosition) {
    return (targetPosition - cameraPosition).normalize();
}

// 计算摄像机的右向量和上向量
void calculateCameraVectors(const Vector3& forward, Vector3& right, Vector3& up) {
    // 约定的世界空间上向量
    Vector3 worldUp(0.0f, 1.0f, 0.0f);
    
    // 右向量是前向量与世界上向量的叉积
    right = forward.cross(worldUp).normalize();
    
    // 上向量是右向量与前向量的叉积
    up = right.cross(forward).normalize();
}

1.3 应用实例:物体跟随行为

cpp

// 游戏或模拟中的跟随行为
Vector3 calculateFollowVector(const Vector3& followerPos, const Vector3& targetPos, float maxSpeed) {
    Vector3 direction = targetPos - followerPos;
    float distance = direction.length();
    
    // 如果足够接近,停止移动
    if (distance < 0.1f) {
        return Vector3(0, 0, 0);
    }
    
    // 计算速度,可以根据距离调整
    float speed = std::min(maxSpeed, distance);
    return direction.normalize() * speed;
}

2. 矩阵变换

矩阵用于表示三维空间中的变换,如平移、旋转和缩放。

2.1 4x4变换矩阵

cpp

class Matrix4x4 {
public:
    float m[4][4];
    
    Matrix4x4() {
        // 初始化为单位矩阵
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                m[i][j] = (i == j) ? 1.0f : 0.0f;
            }
        }
    }
    
    // 矩阵乘法
    Matrix4x4 operator*(const Matrix4x4& matrix) const {
        Matrix4x4 result;
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                result.m[i][j] = 0.0f;
                for (int k = 0; k < 4; k++) {
                    result.m[i][j] += m[i][k] * matrix.m[k][j];
                }
            }
        }
        return result;
    }
    
    // 变换点(向量)
    Vector3 transformPoint(const Vector3& point) const {
        float x = m[0][0] * point.x + m[0][1] * point.y + m[0][2] * point.z + m[0][3];
        float y = m[1][0] * point.x + m[1][1] * point.y + m[1][2] * point.z + m[1][3];
        float z = m[2][0] * point.x + m[2][1] * point.y + m[2][2] * point.z + m[2][3];
        float w = m[3][0] * point.x + m[3][1] * point.y + m[3][2] * point.z + m[3][3];
        
        // 执行透视除法
        if (fabs(w) > 1e-6f) {
            return Vector3(x/w, y/w, z/w);
        }
        return Vector3(x, y, z);
    }
    
    // 变换方向向量(不考虑平移)
    Vector3 transformDirection(const Vector3& direction) const {
        float x = m[0][0] * direction.x + m[0][1] * direction.y + m[0][2] * direction.z;
        float y = m[1][0] * direction.x + m[1][1] * direction.y + m[1][2] * direction.z;
        float z = m[2][0] * direction.x + m[2][1] * direction.y + m[2][2] * direction.z;
        return Vector3(x, y, z);
    }
};

2.2 创建基本变换矩阵

cpp

// 创建平移矩阵
Matrix4x4 createTranslationMatrix(float x, float y, float z) {
    Matrix4x4 result;
    result.m[0][3] = x;
    result.m[1][3] = y;
    result.m[2][3] = z;
    return result;
}

// 创建缩放矩阵
Matrix4x4 createScaleMatrix(float x, float y, float z) {
    Matrix4x4 result;
    result.m[0][0] = x;
    result.m[1][1] = y;
    result.m[2][2] = z;
    return result;
}

// 创建X轴旋转矩阵
Matrix4x4 createRotationXMatrix(float angleInRadians) {
    Matrix4x4 result;
    float cosA = cos(angleInRadians);
    float sinA = sin(angleInRadians);
    
    result.m[1][1] = cosA;
    result.m[1][2] = -sinA;
    result.m[2][1] = sinA;
    result.m[2][2] = cosA;
    
    return result;
}

// 创建Y轴旋转矩阵
Matrix4x4 createRotationYMatrix(float angleInRadians) {
    Matrix4x4 result;
    float cosA = cos(angleInRadians);
    float sinA = sin(angleInRadians);
    
    result.m[0][0] = cosA;
    result.m[0][2] = sinA;
    result.m[2][0] = -sinA;
    result.m[2][2] = cosA;
    
    return result;
}

// 创建Z轴旋转矩阵
Matrix4x4 createRotationZMatrix(float angleInRadians) {
    Matrix4x4 result;
    float cosA = cos(angleInRadians);
    float sinA = sin(angleInRadians);
    
    result.m[0][0] = cosA;
    result.m[0][1] = -sinA;
    result.m[1][0] = sinA;
    result.m[1][1] = cosA;
    
    return result;
}

2.3 视图和投影矩阵

cpp

// 创建视图矩阵(摄像机位置和朝向)
Matrix4x4 createViewMatrix(const Vector3& position, const Vector3& target, const Vector3& up) {
    Vector3 forward = (position - target).normalize(); // 注意:视图空间中z轴通常指向视图后方
    Vector3 right = up.cross(forward).normalize();
    Vector3 newUp = forward.cross(right).normalize();
    
    Matrix4x4 result;
    
    // 设置旋转部分
    result.m[0][0] = right.x;
    result.m[0][1] = right.y;
    result.m[0][2] = right.z;
    
    result.m[1][0] = newUp.x;
    result.m[1][1] = newUp.y;
    result.m[1][2] = newUp.z;
    
    result.m[2][0] = forward.x;
    result.m[2][1] = forward.y;
    result.m[2][2] = forward.z;
    
    // 设置平移部分
    result.m[0][3] = -right.dot(position);
    result.m[1][3] = -newUp.dot(position);
    result.m[2][3] = -forward.dot(position);
    
    return result;
}

// 创建透视投影矩阵
Matrix4x4 createPerspectiveMatrix(float fovY, float aspectRatio, float nearZ, float farZ) {
    float tanHalfFovY = tan(fovY / 2.0f);
    
    Matrix4x4 result;
    result.m[0][0] = 1.0f / (aspectRatio * tanHalfFovY);
    result.m[1][1] = 1.0f / tanHalfFovY;
    result.m[2][2] = farZ / (nearZ - farZ);
    result.m[2][3] = (nearZ * farZ) / (nearZ - farZ);
    result.m[3][2] = -1.0f;
    result.m[3][3] = 0.0f;
    
    return result;
}

2.4 实际应用:骨骼动画的变换矩阵

cpp

struct Bone {
    Matrix4x4 localTransform;
    Matrix4x4 globalTransform;
    int parentIndex;
};

// 计算骨骼全局变换
void calculateGlobalTransforms(std::vector<Bone>& skeleton) {
    for (size_t i = 0; i < skeleton.size(); i++) {
        if (skeleton[i].parentIndex == -1) {
            // 根骨骼
            skeleton[i].globalTransform = skeleton[i].localTransform;
        } else {
            // 子骨骼的全局变换是父骨骼的全局变换乘以自身的局部变换
            skeleton[i].globalTransform = 
                skeleton[skeleton[i].parentIndex].globalTransform * skeleton[i].localTransform;
        }
    }
}

3. 四元数与旋转

四元数提供了一种紧凑且无万向节锁问题的旋转表示方法。

3.1 四元数的基本操作

cpp

class Quaternion {
public:
    float x, y, z, w;  // w是实部,x,y,z是虚部
    
    Quaternion() : x(0.0f), y(0.0f), z(0.0f), w(1.0f) {}
    Quaternion(float x, float y, float z, float w) : x(x), y(y), z(z), w(w) {}
    
    // 四元数乘法
    Quaternion operator*(const Quaternion& q) const {
        return Quaternion(
            w * q.x + x * q.w + y * q.z - z * q.y,
            w * q.y - x * q.z + y * q.w + z * q.x,
            w * q.z + x * q.y - y * q.x + z * q.w,
            w * q.w - x * q.x - y * q.y - z * q.z
        );
    }
    
    // 共轭
    Quaternion conjugate() const {
        return Quaternion(-x, -y, -z, w);
    }
    
    // 模长
    float length() const {
        return sqrt(x*x + y*y + z*z + w*w);
    }
    
    // 单位化
    Quaternion normalize() const {
        float len = length();
        if (len < 1e-6f) return Quaternion(0, 0, 0, 1);
        return Quaternion(x/len, y/len, z/len, w/len);
    }
    
    // 四元数转旋转矩阵
    Matrix4x4 toRotationMatrix() const {
        Matrix4x4 result;
        
        float xx = x * x;
        float xy = x * y;
        float xz = x * z;
        float xw = x * w;
        float yy = y * y;
        float yz = y * z;
        float yw = y * w;
        float zz = z * z;
        float zw = z * w;
        
        result.m[0][0] = 1.0f - 2.0f * (yy + zz);
        result.m[0][1] = 2.0f * (xy - zw);
        result.m[0][2] = 2.0f * (xz + yw);
        
        result.m[1][0] = 2.0f * (xy + zw);
        result.m[1][1] = 1.0f - 2.0f * (xx + zz);
        result.m[1][2] = 2.0f * (yz - xw);
        
        result.m[2][0] = 2.0f * (xz - yw);
        result.m[2][1] = 2.0f * (yz + xw);
        result.m[2][2] = 1.0f - 2.0f * (xx + yy);
        
        return result;
    }
};

3.2 构建旋转四元数

cpp

// 从轴角构建四元数
Quaternion fromAxisAngle(const Vector3& axis, float angleInRadians) {
    Vector3 normalizedAxis = axis.normalize();
    float sinHalfAngle = sin(angleInRadians * 0.5f);
    
    return Quaternion(
        normalizedAxis.x * sinHalfAngle,
        normalizedAxis.y * sinHalfAngle,
        normalizedAxis.z * sinHalfAngle,
        cos(angleInRadians * 0.5f)
    );
}

// 从欧拉角(YXZ顺序)构建四元数
Quaternion fromEulerAngles(float pitch, float yaw, float roll) {
    // 将欧拉角转换为弧度
    float halfPitch = pitch * 0.5f;
    float halfYaw = yaw * 0.5f;
    float halfRoll = roll * 0.5f;
    
    float cosHalfPitch = cos(halfPitch);
    float sinHalfPitch = sin(halfPitch);
    float cosHalfYaw = cos(halfYaw);
    float sinHalfYaw = sin(halfYaw);
    float cosHalfRoll = cos(halfRoll);
    float sinHalfRoll = sin(halfRoll);
    
    return Quaternion(
        cosHalfYaw * sinHalfPitch * cosHalfRoll + sinHalfYaw * cosHalfPitch * sinHalfRoll,
        cosHalfYaw * cosHalfPitch * sinHalfRoll - sinHalfYaw * sinHalfPitch * cosHalfRoll,
        sinHalfYaw * cosHalfPitch * cosHalfRoll - cosHalfYaw * sinHalfPitch * sinHalfRoll,
        cosHalfYaw * cosHalfPitch * cosHalfRoll + sinHalfYaw * sinHalfPitch * sinHalfRoll
    );
}

3.3 四元数插值

cpp

// 四元数球面线性插值(SLERP)
Quaternion slerp(const Quaternion& q1, const Quaternion& q2, float t) {
    // 确保t在[0,1]范围内
    t = std::max(0.0f, std::min(1.0f, t));
    
    // 计算四元数点积,表示两个旋转的接近程度
    float dot = q1.x * q2.x + q1.y * q2.y + q1.z * q2.z + q1.w * q2.w;
    
    // 如果点积为负,取反其中一个四元数以确保最短路径插值
    Quaternion q2Adjusted = q2;
    if (dot < 0.0f) {
        q2Adjusted.x = -q2.x;
        q2Adjusted.y = -q2.y;
        q2Adjusted.z = -q2.z;
        q2Adjusted.w = -q2.w;
        dot = -dot;
    }
    
    // 当四元数几乎平行时,使用线性插值以避免数值问题
    if (dot > 0.9995f) {
        Quaternion result = Quaternion(
            q1.x + t * (q2Adjusted.x - q1.x),
            q1.y + t * (q2Adjusted.y - q1.y),
            q1.z + t * (q2Adjusted.z - q1.z),
            q1.w + t * (q2Adjusted.w - q1.w)
        );
        return result.normalize();
    }
    
    // 球面线性插值
    float angle = acos(dot);
    float sinAngle = sin(angle);
    
    float scale0 = sin((1.0f - t) * angle) / sinAngle;
    float scale1 = sin(t * angle) / sinAngle;
    
    return Quaternion(
        scale0 * q1.x + scale1 * q2Adjusted.x,
        scale0 * q1.y + scale1 * q2Adjusted.y,
        scale0 * q1.z + scale1 * q2Adjusted.z,
        scale0 * q1.w + scale1 * q2Adjusted.w
    );
}

3.4 实际应用:角色旋转

cpp

class Character {
private:
    Vector3 position;
    Quaternion orientation;
    
public:
    Character() : position(0,0,0), orientation(0,0,0,1) {}
    
    // 按指定角度旋转角色
    void rotate(float angle, const Vector3& axis) {
        Quaternion rotation = fromAxisAngle(axis, angle);
        orientation = rotation * orientation;
        orientation = orientation.normalize(); // 防止累积误差
    }
    
    // 平滑转向目标方向
    void smoothTurnTowards(const Vector3& targetDirection, float turnSpeed, float deltaTime) {
        // 计算当前朝向
        Vector3 forward = Vector3(0, 0, 1);
        Matrix4x4 rotMatrix = orientation.toRotationMatrix();
        Vector3 currentDirection = rotMatrix.transformDirection(forward);
        
        // 如果当前方向已接近目标方向,则无需旋转
        if ((targetDirection - currentDirection).length() < 0.01f) {
            return;
        }
        
        // 计算旋转轴和角度
        Vector3 rotationAxis = currentDirection.cross(targetDirection).normalize();
        float angle = acos(currentDirection.dot(targetDirection));
        
        // 限制旋转速度
        float maxAngle = turnSpeed * deltaTime;
        float actualAngle = std::min(angle, maxAngle);
        
        // 应用旋转
        Quaternion rotation = fromAxisAngle(rotationAxis, actualAngle);
        orientation = rotation * orientation;
        orientation = orientation.normalize();
    }
};

4. 射线追踪与交叉检测

射线追踪用于检测物体间的交叉,是许多图形和游戏算法的基础。

4.1 射线的表示

cpp

class Ray {
public:
    Vector3 origin;     // 射线起点
    Vector3 direction;  // 射线方向(单位向量)
    
    Ray() : origin(0,0,0), direction(0,0,1) {}
    Ray(const Vector3& origin, const Vector3& direction) 
        : origin(origin), direction(direction.normalize()) {}
    
    // 根据参数t返回射线上的点
    Vector3 getPoint(float t) const {
        return origin + direction * t;
    }
};

4.2 射线与球体相交测试

cpp

struct Sphere {
    Vector3 center;
    float radius;
    
    Sphere(const Vector3& center, float radius) 
        : center(center), radius(radius) {}
};

// 检测射线与球体的交点
bool rayIntersectsSphere(const Ray& ray, const Sphere& sphere, float& t1, float& t2) {
    Vector3 oc = ray.origin - sphere.center;
    
    float a = ray.direction.dot(ray.direction);  // 通常为1,如果方向已归一化
    float b = 2.0f * oc.dot(ray.direction);
    float c = oc.dot(oc) - sphere.radius * sphere.radius;
    
    float discriminant = b*b - 4*a*c;
    
    if (discriminant < 0) {
        // 没有实数解,射线不与球体相交
        return false;
    }
    
    float sqrtDiscriminant = sqrt(discriminant);
    
    // 两个交点距离
    t1 = (-b - sqrtDiscriminant) / (2*a);
    t2 = (-b + sqrtDiscriminant) / (2*a);
    
    return true;
}

4.3 射线与三角形相交测试

cpp

struct Triangle {
    Vector3 vertices[3];
    
    Triangle(const Vector3& v1, const Vector3& v2, const Vector3& v3) {
        vertices[0] = v1;
        vertices[1] = v2;
        vertices[2] = v3;
    }
};

// 使用Möller–Trumbore算法检测射线与三角形的交点
bool rayIntersectsTriangle(const Ray& ray, const Triangle& triangle, float& t, float& u, float& v) {
    const float EPSILON = 0.0000001f;
    
    Vector3 vertex0 = triangle.vertices[0];
    Vector3 vertex1 = triangle.vertices[1]; 
    Vector3 vertex2 = triangle.vertices[2];
    
    Vector3 edge1 = vertex1 - vertex0;
    Vector3 edge2 = vertex2 - vertex0;
    
    Vector3 h = ray.direction.cross(edge2);
    float a = edge1.dot(h);
    
    if (a > -EPSILON && a < EPSILON) {
        return false;  // 射线平行于三角形
    }
    
    float f = 1.0f / a;
    Vector3 s = ray.origin - vertex0;
    u = f * s.dot(h);
    
    if (u < 0.0f || u > 1.0f) {
        return false;
    }
    
    Vector3 q = s.cross(edge1);
    v = f * ray.direction.dot(q);
    
    if (v < 0.0f || u + v > 1.0f) {
        return false;
    }
    
    // 计算t,射线交点 = 射线原点 + t * 射线方向
    t = f * edge2.dot(q);
    
    if (t > EPSILON) {
        return true;
    }
    
    // 射线交点在三角形后方
    return false;
}

4.4 实际应用:鼠标拾取

cpp

// 从屏幕坐标创建世界空间射线
Ray createRayFromScreenPoint(int screenX, int screenY, int screenWidth, int screenHeight, 
                           const Matrix4x4& projectionMatrix, const Matrix4x4& viewMatrix) {
    // 将屏幕坐标转换为规范化设备坐标 (-1 到 1)
    float x = (2.0f * screenX) / screenWidth - 1.0f;
    float y = 1.0f - (2.0f * screenY) / screenHeight;
    float z = 1.0f;  // 远平面
    
    // 从投影空间转换到视图空间
    Matrix4x4 invProj = inverse(projectionMatrix);
    Vector3 rayNds = Vector3(x, y, z);
    Vector3 rayView = invProj.transformPoint(rayNds);
    rayView.z = 1.0f;  // 重置z为1,因为我们只需要方向
    
    // 从视图空间转换到世界空间
    Matrix4x4 invView = inverse(viewMatrix);
    Vector3 rayWorld = invView.transformPoint(rayView);
    Vector3 origin = invView.transformPoint(Vector3(0, 0, 0));
    Vector3 direction = (rayWorld - origin).normalize();
    
    return Ray(origin, direction);
}

// 进行拾取测试
bool pickObject(int screenX, int screenY, const std::vector<Triangle>& triangles, 
              const Matrix4x4& projectionMatrix, const Matrix4x4& viewMatrix, int& pickedTriangleIndex) {
    // 创建射线
    Ray ray = createRayFromScreenPoint(screenX, screenY, screenWidth, screenHeight, 
                                      projectionMatrix, viewMatrix);
    
    // 测试射线与所有三角形的交点
    float closestT = FLT_MAX;
    pickedTriangleIndex = -1;
    
    for (size_t i = 0; i < triangles.size(); i++) {
        float t, u, v;
        if (rayIntersectsTriangle(ray, triangles[i], t, u, v)) {
            if (t < closestT) {
                closestT = t;
                pickedTriangleIndex = i;
            }
        }
    }
    
    return pickedTriangleIndex != -1;
}

5. 参数曲线与曲面

参数曲线和曲面用于表示平滑的形状,广泛应用于建模和动画。

5.1 贝塞尔曲线

cpp

// 计算三次贝塞尔曲线上的点
Vector3 cubicBezierPoint(const Vector3& p0, const Vector3& p1, 
                        const Vector3& p2, const Vector3& p3, float t) {
    float u = 1.0f - t;
    float tt = t * t;
    float uu = u * u;
    float uuu = uu * u;
    float ttt = tt * t;
    
    Vector3 point = p0 * uuu;  // (1-t)^3 * P0
    point = point + p1 * (3.0f * uu * t);  // + 3(1-t)^2 * t * P1
    point = point + p2 * (3.0f * u * tt);  // + 3(1-t) * t^2 * P2
    point = point + p3 * ttt;  // + t^3 * P3
    
    return point;
}

// 创建一组点,表示贝塞尔曲线
std::vector<Vector3> createBezierCurve(const Vector3& p0, const Vector3& p1, 
                                     const Vector3& p2, const Vector3& p3, int segments) {
    std::vector<Vector3> points;
    
    for (int i = 0; i <= segments; i++) {
        float t = static_cast<float>(i) / segments;
        points.push_back(cubicBezierPoint(p0, p1, p2, p3, t));
    }
    
    return points;
}

5.2 样条曲线

cpp

// Catmull-Rom样条曲线
Vector3 catmullRomPoint(const Vector3& p0, const Vector3& p1, 
                       const Vector3& p2, const Vector3& p3, float t) {
    float t2 = t * t;
    float t3 = t2 * t;
    
    Vector3 point = p0 * (-0.5f * t3 + t2 - 0.5f * t);
    point = point + p1 * (1.5f * t3 - 2.5f * t2 + 1.0f);
    point = point + p2 * (-1.5f * t3 + 2.0f * t2 + 0.5f * t);
    point = point + p3 * (0.5f * t3 - 0.5f * t2);
    
    return point;
}

// 创建一组点,表示Catmull-Rom样条曲线段
std::vector<Vector3> createCatmullRomSegment(const Vector3& p0, const Vector3& p1, 
                                           const Vector3& p2, const Vector3& p3, int segments) {
    std::vector<Vector3> points;
    
    for (int i = 0; i <= segments; i++) {
        float t = static_cast<float>(i) / segments;
        points.push_back(catmullRomPoint(p0, p1, p2, p3, t));
    }
    
    return points;
}

// 创建一条经过所有控制点的Catmull-Rom样条曲线
std::vector<Vector3> createCatmullRomSpline(const std::vector<Vector3>& controlPoints, int segmentsPerCurve) {
    if (controlPoints.size() < 4) {
        return controlPoints;  // 至少需要4个控制点
    }
    
    std::vector<Vector3> curvePoints;
    
    // 对于每个曲线段
    for (size_t i = 0; i < controlPoints.size() - 3; i++) {
        std::vector<Vector3> segment = createCatmullRomSegment(
            controlPoints[i], controlPoints[i+1], 
            controlPoints[i+2], controlPoints[i+3], 
            segmentsPerCurve
        );
        
        // 添加当前段的点(除了最后一个点,除非是最后一段)
        for (size_t j = 0; j < segment.size(); j++) {
            if (j < segment.size() - 1 || i == controlPoints.size() - 4) {
                curvePoints.push_back(segment[j]);
            }
        }
    }
    
    return curvePoints;
}

5.3 实际应用:动画路径

cpp

class PathAnimation {
private:
    std::vector<Vector3> controlPoints;
    std::vector<Vector3> pathPoints;
    float totalDistance;
    std::vector<float> distanceToParameter;
    
public:
    PathAnimation(const std::vector<Vector3>& controlPoints, int resolution = 100) 
        : controlPoints(controlPoints) {
        // 生成路径点
        pathPoints = createCatmullRomSpline(controlPoints, resolution / controlPoints.size());
        
        // 计算总距离和参数映射表
        calculateDistanceTable();
    }
    
    // 计算距离表和参数映射
    void calculateDistanceTable() {
        totalDistance = 0.0f;
        distanceToParameter.clear();
        distanceToParameter.push_back(0.0f);
        
        for (size_t i = 1; i < pathPoints.size(); i++) {
            float segmentLength = (pathPoints[i] - pathPoints[i-1]).length();
            totalDistance += segmentLength;
            distanceToParameter.push_back(totalDistance);
        }
        
        // 归一化距离参数
        for (size_t i = 0; i < distanceToParameter.size(); i++) {
            distanceToParameter[i] /= totalDistance;
        }
    }
    
    // 根据归一化参数t获取路径上的点
    Vector3 getPointAtParameter(float t) {
        if (pathPoints.size() <= 1) {
            return pathPoints.empty() ? Vector3(0,0,0) : pathPoints[0];
        }
        
        // 将t限制在[0,1]范围内
        t = std::max(0.0f, std::min(1.0f, t));
        
        // 二分查找找到对应的段
        size_t low = 0;
        size_t high = distanceToParameter.size() - 1;
        
        while (low <= high) {
            size_t mid = (low + high) / 2;
            
            if (distanceToParameter[mid] < t) {
                low = mid + 1;
            } else if (distanceToParameter[mid] > t) {
                if (mid == 0) break;
                high = mid - 1;
            } else {
                return pathPoints[mid];
            }
        }
        
        // 线性插值获取精确位置
        size_t index = high;
        float localT = 0.0f;
        
        if (index < distanceToParameter.size() - 1) {
            float d0 = distanceToParameter[index];
            float d1 = distanceToParameter[index + 1];
            localT = (t - d0) / (d1 - d0);
        }
        
        Vector3 p0 = pathPoints[index];
        Vector3 p1 = (index < pathPoints.size() - 1) ? pathPoints[index + 1] : p0;
        
        return p0 + (p1 - p0) * localT;
    }
};

6. 碰撞检测

碰撞检测在游戏和物理模拟中起着关键作用。

6.1 包围体

cpp

struct AABB {  // 轴对齐包围盒
    Vector3 min;
    Vector3 max;
    
    AABB() : min(0,0,0), max(0,0,0) {}
    AABB(const Vector3& min, const Vector3& max) : min(min), max(max) {}
    
    // 测试点是否在AABB内
    bool containsPoint(const Vector3& point) const {
        return point.x >= min.x && point.x <= max.x &&
               point.y >= min.y && point.y <= max.y &&
               point.z >= min.z && point.z <= max.z;
    }
    
    // 测试两个AABB是否相交
    bool intersects(const AABB& other) const {
        return min.x <= other.max.x && max.x >= other.min.x &&
               min.y <= other.max.y && max.y >= other.min.y &&
               min.z <= other.max.z && max.z >= other.min.z;
    }
    
    // 计算两个AABB的相交体积
    AABB getIntersection(const AABB& other) const {
        Vector3 intersectionMin(
            std::max(min.x, other.min.x),
            std::max(min.y, other.min.y),
            std::max(min.z, other.min.z)
        );
        
        Vector3 intersectionMax(
            std::min(max.x, other.max.x),
            std::min(max.y, other.max.y),
            std::min(max.z, other.max.z)
        );
        
        return AABB(intersectionMin, intersectionMax);
    }
};

6.2 球体碰撞

cpp

// 检测两个球体是否相交
bool spheresIntersect(const Sphere& a, const Sphere& b) {
    float distanceSquared = (a.center - b.center).dot(a.center - b.center);
    float radiusSum = a.radius + b.radius;
    return distanceSquared <= radiusSum * radiusSum;
}

// 计算两个相交球体的碰撞信息
struct CollisionInfo {
    bool collision;
    Vector3 normal;  // 从a指向b的碰撞法线
    float depth;     // 穿透深度
};

CollisionInfo sphereCollisionInfo(const Sphere& a, const Sphere& b) {
    CollisionInfo info;
    
    Vector3 direction = b.center - a.center;
    float distanceSquared = direction.dot(direction);
    float radiusSum = a.radius + b.radius;
    
    info.collision = distanceSquared <= radiusSum * radiusSum;
    
    if (info.collision) {
        float distance = sqrt(distanceSquared);
        
        // 避免除以零
        if (distance < 1e-6f) {
            info.normal = Vector3(0, 1, 0);  // 任意选择一个向上的法线
            info.depth = a.radius + b.radius;
        } else {
            info.normal = direction * (1.0f / distance);  // 单位化方向向量
            info.depth = radiusSum - distance;
        }
    } else {
        info.normal = Vector3(0, 0, 0);
        info.depth = 0.0f;
    }
    
    return info;
}

6.3 分离轴定理(SAT)

cpp

struct OBB {  // 有向包围盒
    Vector3 center;      // 中心点
    Vector3 halfSize;    // 半尺寸
    Matrix4x4 rotation;  // 旋转矩阵
    
    // 获取OBB的8个顶点
    std::vector<Vector3> getVertices() const {
        std::vector<Vector3> vertices(8);
        
        for (int i = 0; i < 8; i++) {
            Vector3 direction(
                ((i & 1) ? 1 : -1) * halfSize.x,
                ((i & 2) ? 1 : -1) * halfSize.y,
                ((i & 4) ? 1 : -1) * halfSize.z
            );
            
            vertices[i] = center + rotation.transformDirection(direction);
        }
        
        return vertices;
    }
    
    // 获取OBB的3个轴
    void getAxes(Vector3& xAxis, Vector3& yAxis, Vector3& zAxis) const {
        xAxis = rotation.transformDirection(Vector3(1, 0, 0));
        yAxis = rotation.transformDirection(Vector3(0, 1, 0));
        zAxis = rotation.transformDirection(Vector3(0, 0, 1));
    }
};

// 使用分离轴定理检测两个OBB是否相交
bool obbIntersectsSAT(const OBB& a, const OBB& b) {
    Vector3 aXAxis, aYAxis, aZAxis;
    Vector3 bXAxis, bYAxis, bZAxis;
    
    a.getAxes(aXAxis, aYAxis, aZAxis);
    b.getAxes(bXAxis, bYAxis, bZAxis);
    
    // 15个需要测试的轴: 3 + 3 + 9 (轴的交叉乘积)
    Vector3 axes[15] = {
        aXAxis, aYAxis, aZAxis,
        bXAxis, bYAxis, bZAxis,
        aXAxis.cross(bXAxis), aXAxis.cross(bYAxis), aXAxis.cross(bZAxis),
        aYAxis.cross(bXAxis), aYAxis.cross(bYAxis), aYAxis.cross(bZAxis),
        aZAxis.cross(bXAxis), aZAxis.cross(bYAxis), aZAxis.cross(bZAxis)
    };
    
    // 获取顶点
    std::vector<Vector3> verticesA = a.getVertices();
    std::vector<Vector3> verticesB = b.getVertices();
    
    // 中心偏移
    Vector3 centerDifference = b.center - a.center;
    
    // 测试每个轴
    for (int i = 0; i < 15; i++) {
        // 跳过接近于零的轴(交叉乘积可能为零)
        if (axes[i].length() < 1e-6f) continue;
        
        // 归一化轴
        Vector3 axis = axes[i].normalize();
        
        // 计算两个OBB在该轴上的投影
        float minA = FLT_MAX, maxA = -FLT_MAX;
        float minB = FLT_MAX, maxB = -FLT_MAX;
        
        for (const auto& vertex : verticesA) {
            float projection = axis.dot(vertex);
            minA = std::min(minA, projection);
            maxA = std::max(maxA, projection);
        }
        
        for (const auto& vertex : verticesB) {
            float projection = axis.dot(vertex);
            minB = std::min(minB, projection);
            maxB = std::max(maxB, projection);
        }
        
        // 测试投影是否分离
        if (maxA < minB || maxB < minA) {
            return false;  // 找到分离轴,不相交
        }
    }
    
    // 没有找到分离轴,两个OBB相交
    return true;
}

6.4 空间分区

cpp

// 简单的八叉树实现
class OctreeNode {
public:
    AABB bounds;
    std::vector<int> objectIndices;  // 存储在该节点中的对象索引
    OctreeNode* children[8];
    bool isLeaf;
    
    OctreeNode(const AABB& bounds) : bounds(bounds), isLeaf(true) {
        for (int i = 0; i < 8; i++) {
            children[i] = nullptr;
        }
    }
    
    ~OctreeNode() {
        for (int i = 0; i < 8; i++) {
            delete children[i];
        }
    }
    
    // 将节点分割为8个子节点
    void split() {
        if (!isLeaf) return;
        
        isLeaf = false;
        Vector3 center = (bounds.min + bounds.max) * 0.5f;
        
        // 创建8个子节点
        for (int i = 0; i < 8; i++) {
            Vector3 min = bounds.min;
            Vector3 max = bounds.max;
            
            if (i & 1) min.x = center.x; else max.x = center.x;
            if (i & 2) min.y = center.y; else max.y = center.y;
            if (i & 4) min.z = center.z; else max.z = center.z;
            
            children[i] = new OctreeNode(AABB(min, max));
        }
        
        // 重新分配对象到子节点
        for (int objIndex : objectIndices) {
            insertObject(objIndex);
        }
        
        objectIndices.clear();
    }
    
    // 插入对象到适当的节点
    void insertObject(int objIndex, const std::vector<AABB>& objectBounds) {
        if (isLeaf) {
            // 如果是叶节点,直接添加对象
            objectIndices.push_back(objIndex);
            
            // 如果对象数量超过阈值,考虑分割
            if (objectIndices.size() > 8) {
                split();
            }
        } else {
            // 找到对象应该放入的子节点
            for (int i = 0; i < 8; i++) {
                if (children[i]->bounds.intersects(objectBounds[objIndex])) {
                    children[i]->insertObject(objIndex, objectBounds);
                }
            }
        }
    }
    
    // 查询给定AABB与哪些对象相交
    void query(const AABB& queryAABB, std::vector<int>& result, 
              const std::vector<AABB>& objectBounds) {
        if (!bounds.intersects(queryAABB)) {
            return;  // 查询区域与当前节点不相交
        }
        
        if (isLeaf) {
            // 检查叶节点中的每个对象
            for (int objIndex : objectIndices) {
                if (objectBounds[objIndex].intersects(queryAABB)) {
                    result.push_back(objIndex);
                }
            }
        } else {
            // 递归查询子节点
            for (int i = 0; i < 8; i++) {
                children[i]->query(queryAABB, result, objectBounds);
            }
        }
    }
};

7. 物理模拟

物理模拟为游戏和虚拟现实提供了真实感和交互性。

7.1 质点系统

cpp

struct Particle {
    Vector3 position;
    Vector3 velocity;
    Vector3 acceleration;
    float mass;
    bool isFixed;  // 是否固定不动
    
    Particle() 
        : position(0,0,0), velocity(0,0,0), acceleration(0,0,0), 
          mass(1.0f), isFixed(false) {}
    
    Particle(const Vector3& pos, float mass = 1.0f) 
        : position(pos), velocity(0,0,0), acceleration(0,0,0), 
          mass(mass), isFixed(false) {}
    
    // 应用力
    void applyForce(const Vector3& force) {
        if (!isFixed) {
            acceleration = acceleration + force * (1.0f / mass);
        }
    }
    
    // 更新位置和速度
    void update(float deltaTime) {
        if (!isFixed) {
            velocity = velocity + acceleration * deltaTime;
            position = position + velocity * deltaTime;
            
            // 重置加速度
            acceleration = Vector3(0, 0, 0);
        }
    }
};

7.2 弹簧和约束

cpp

struct Spring {
    int particleIndexA;
    int particleIndexB;
    float restLength;
    float stiffness;
    float damping;
    
    Spring(int a, int b, float restLength, float stiffness, float damping)
        : particleIndexA(a), particleIndexB(b), 
          restLength(restLength), stiffness(stiffness), damping(damping) {}
    
    // 应用弹簧力
    void applyForce(std::vector<Particle>& particles) {
        Particle& a = particles[particleIndexA];
        Particle& b = particles[particleIndexB];
        
        Vector3 delta = b.position - a.position;
        float currentLength = delta.length();
        
        if (currentLength < 1e-6f) return;  // 避免除以零
        
        // 弹簧力方向
        Vector3 direction = delta * (1.0f / currentLength);
        
        // 弹簧力大小 (Hooke's law: F = -k * x)
        float displacement = currentLength - restLength;
        
        // 计算相对速度在弹簧方向上的投影,用于阻尼
        Vector3 relativeVelocity = b.velocity - a.velocity;
        float dampingFactor = direction.dot(relativeVelocity) * damping;
        
        float forceMagnitude = stiffness * displacement + dampingFactor;
        Vector3 force = direction * forceMagnitude;
        
        // 施加力(作用力与反作用力)
        if (!a.isFixed) a.applyForce(force);
        if (!b.isFixed) b.applyForce(force * -1.0f);
    }
};

7.3 简单布料模拟

cpp

class ClothSimulation {
private:
    std::vector<Particle> particles;
    std::vector<Spring> springs;
    int gridWidth, gridHeight;
    float gravity;
    
public:
    ClothSimulation(int width, int height, float particleSpacing, 
                   float stiffness, float damping, float particleMass, float gravityValue = -9.8f)
        : gridWidth(width), gridHeight(height), gravity(gravityValue) {
        
        // 创建粒子网格
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                Vector3 position(x * particleSpacing, 0, y * particleSpacing);
                particles.push_back(Particle(position, particleMass));
            }
        }
        
        // 固定顶部两角
        particles[0].isFixed = true;
        particles[width-1].isFixed = true;
        
        // 创建弹簧连接 (格子结构)
        // 水平方向弹簧
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width - 1; x++) {
                int index = y * width + x;
                springs.push_back(Spring(index, index + 1, particleSpacing, stiffness, damping));
            }
        }
        
        // 垂直方向弹簧
        for (int y = 0; y < height - 1; y++) {
            for (int x = 0; x < width; x++) {
                int index = y * width + x;
                springs.push_back(Spring(index, index + width, particleSpacing, stiffness, damping));
            }
        }
        
        // 对角线弹簧 (增加稳定性)
        for (int y = 0; y < height - 1; y++) {
            for (int x = 0; x < width - 1; x++) {
                int index = y * width + x;
                springs.push_back(Spring(index, index + width + 1, 
                                       particleSpacing * 1.414f, stiffness * 0.7f, damping));
                springs.push_back(Spring(index + 1, index + width, 
                                       particleSpacing * 1.414f, stiffness * 0.7f, damping));
            }
        }
    }
    
    // 更新模拟
    void update(float deltaTime) {
        // 施加重力
        for (auto& particle : particles) {
            particle.applyForce(Vector3(0, gravity * particle.mass, 0));
        }
        
        // 应用弹簧力
        for (auto& spring : springs) {
            spring.applyForce(particles);
        }
        
        // 更新粒子位置
        for (auto& particle : particles) {
            particle.update(deltaTime);
        }
    }
    
    // 获取粒子位置,用于渲染
    std::vector<Vector3> getParticlePositions() const {
        std::vector<Vector3> positions;
        for (const auto& particle : particles) {
            positions.push_back(particle.position);
        }
        return positions;
    }
    
    // 获取布料网格的三角形索引,用于渲染
    std::vector<int> getTriangleIndices() const {
        std::vector<int> indices;
        for (int y = 0; y < gridHeight - 1; y++) {
            for (int x = 0; x < gridWidth - 1; x++) {
                int topLeft = y * gridWidth + x;
                int topRight = topLeft + 1;
                int bottomLeft = (y + 1) * gridWidth + x;
                int bottomRight = bottomLeft + 1;
                
                // 每个格子两个三角形
                indices.push_back(topLeft);
                indices.push_back(bottomLeft);
                indices.push_back(topRight);
                
                indices.push_back(topRight);
                indices.push_back(bottomLeft);
                indices.push_back(bottomRight);
            }
        }
        return indices;
    }
};

7.4 刚体动力学

cpp

class RigidBody {
private:
    Vector3 position;        // 质心位置
    Quaternion orientation;  // 旋转四元数
    Vector3 linearVelocity;  // 线性速度
    Vector3 angularVelocity; // 角速度
    
    float mass;              // 质量
    Matrix4x4 inertiaTensor; // 惯性张量
    Matrix4x4 inverseInertiaTensor; // 惯性张量的逆
    
    // 转换为世界空间的惯性张量的逆
    Matrix4x4 getWorldInverseInertiaTensor() const {
        Matrix4x4 rotMatrix = orientation.toRotationMatrix();
        Matrix4x4 rotMatrixT = transpose(rotMatrix);
        
        // R * I^-1 * R^T
        return rotMatrix * inverseInertiaTensor * rotMatrixT;
    }
    
public:
    RigidBody(float mass, const Matrix4x4& inertiaTensor) 
        : position(0,0,0), orientation(0,0,0,1), 
          linearVelocity(0,0,0), angularVelocity(0,0,0),
          mass(mass), inertiaTensor(inertiaTensor) {
        
        // 计算惯性张量的逆
        inverseInertiaTensor = inverse(inertiaTensor);
    }
    
    // 应用力
    void applyForce(const Vector3& force, const Vector3& worldPoint) {
        // 线性加速度 a = F/m
        Vector3 linearAcceleration = force * (1.0f / mass);
        linearVelocity = linearVelocity + linearAcceleration;
        
        // 计算力矩 τ = r × F
        Vector3 torque = (worldPoint - position).cross(force);
        
        // 计算角加速度 α = I^-1 * τ
        Matrix4x4 worldInvInertia = getWorldInverseInertiaTensor();
        Vector3 angularAcceleration = worldInvInertia.transformDirection(torque);
        
        angularVelocity = angularVelocity + angularAcceleration;
    }
    
    // 更新位置和旋转
    void update(float deltaTime) {
        // 更新位置
        position = position + linearVelocity * deltaTime;
        
        // 更新旋转
        // 将角速度转换为四元数变化率
        Quaternion spin(
            angularVelocity.x * 0.5f,
            angularVelocity.y * 0.5f,
            angularVelocity.z * 0.5f,
            0
        );
        
        // 四元数微分方程: dq/dt = 0.5 * w * q
        Quaternion velocityQ = spin * orientation;
        
        // 更新方向四元数
        orientation = orientation + velocityQ * deltaTime;
        orientation = orientation.normalize();
    }
    
    // 获取位置和旋转
    Vector3 getPosition() const { return position; }
    Quaternion getOrientation() const { return orientation; }
    
    // 从局部坐标转换到世界坐标
    Vector3 localToWorld(const Vector3& localPoint) const {
        Matrix4x4 rotMatrix = orientation.toRotationMatrix();
        return position + rotMatrix.transformDirection(localPoint);
    }
    
    // 从世界坐标转换到局部坐标
    Vector3 worldToLocal(const Vector3& worldPoint) const {
        Matrix4x4 rotMatrix = orientation.toRotationMatrix();
        Matrix4x4 invRotMatrix = transpose(rotMatrix);
        return invRotMatrix.transformDirection(worldPoint - position);
    }
};

8. 光照与着色

光照和着色使3D场景看起来更加逼真和视觉上令人愉悦。

8.1 基本光照模型

cpp

struct Material {
    Vector3 ambient;
    Vector3 diffuse;
    Vector3 specular;
    float shininess;
    
    Material() : ambient(0.1f, 0.1f, 0.1f), diffuse(0.8f, 0.8f, 0.8f),
                 specular(1.0f, 1.0f, 1.0f), shininess(32.0f) {}
};

struct Light {
    enum LightType { DIRECTIONAL, POINT, SPOT };
    
    LightType type;
    Vector3 position;    // 点光源和聚光灯
    Vector3 direction;   // 方向光和聚光灯
    Vector3 color;
    float intensity;
    
    // 点光源和聚光灯的衰减参数
    float constant;
    float linear;
    float quadratic;
    
    // 聚光灯的参数
    float cutoff;        // 内切角余弦值
    float outerCutoff;   // 外切角余弦值
    
    Light() : type(DIRECTIONAL), position(0,0,0), direction(0,-1,0),
              color(1,1,1), intensity(1.0f), constant(1.0f),
              linear(0.09f), quadratic(0.032f), cutoff(cos(12.5f * 3.14159f / 180.0f)),
              outerCutoff(cos(17.5f * 3.14159f / 180.0f)) {}
};

// 计算Phong光照模型
Vector3 calculatePhongLighting(const Vector3& position, const Vector3& normal, const Vector3& viewDirection,
                              const Material& material, const Light& light) {
    Vector3 result(0, 0, 0);
    
    // 计算环境光
    Vector3 ambient = material.ambient * light.color;
    
    // 光照方向
    Vector3 lightDir;
    float attenuation = 1.0f;
    
    if (light.type == Light::DIRECTIONAL) {
        lightDir = -light.direction.normalize();
    } else {
        // 点光源或聚光灯
        lightDir = (light.position - position).normalize();
        
        // 计算距离和衰减
        float distance = (light.position - position).length();
        attenuation = 1.0f / (light.constant + light.linear * distance + 
                             light.quadratic * distance * distance);
        
        // 聚光灯效果
        if (light.type == Light::SPOT) {
            float theta = lightDir.dot(-light.direction.normalize());
            float epsilon = light.cutoff - light.outerCutoff;
            float intensity = std::clamp((theta - light.outerCutoff) / epsilon, 0.0f, 1.0f);
            attenuation *= intensity;
        }
    }
    
    // 计算漫反射
    float diff = std::max(normal.dot(lightDir), 0.0f);
    Vector3 diffuse = material.diffuse * diff * light.color;
    
    // 计算镜面反射
    Vector3 reflectDir = reflect(-lightDir, normal);
    float spec = std::pow(std::max(viewDirection.dot(reflectDir), 0.0f), material.shininess);
    Vector3 specular = material.specular * spec * light.color;
    
    // 合并结果
    result = ambient + (diffuse + specular) * attenuation * light.intensity;
    
    // 确保颜色值在[0,1]范围内
    result.x = std::min(1.0f, result.x);
    result.y = std::min(1.0f, result.y);
    result.z = std::min(1.0f, result.z);
    
    return result;
}

// 辅助函数:计算反射向量
Vector3 reflect(const Vector3& incident, const Vector3& normal) {
    return incident - normal * (2.0f * incident.dot(normal));
}

8.2 基于物理的渲染(PBR)

cpp

struct PBRMaterial {
    Vector3 albedo;      // 基础颜色
    float metallic;      // 金属度 (0: 非金属, 1: 金属)
    float roughness;     // 粗糙度 (0: 光滑, 1: 粗糙)
    float ao;            // 环境光遮蔽
    
    PBRMaterial() : albedo(0.8f, 0.8f, 0.8f), metallic(0.0f),
                   roughness(0.5f), ao(1.0f) {}
};

// PBR 着色函数
Vector3 calculatePBRLighting(const Vector3& position, const Vector3& normal, const Vector3& viewDirection,
                           const PBRMaterial& material, const std::vector<Light>& lights) {
    Vector3 N = normal.normalize();
    Vector3 V = viewDirection.normalize();
    
    // 计算基础反射率 F0
    Vector3 F0(0.04f, 0.04f, 0.04f);  // 典型的非金属F0值
    F0 = F0 * (1.0f - material.metallic) + material.albedo * material.metallic;
    
    // 初始化辐射率
    Vector3 Lo(0.0f, 0.0f, 0.0f);
    
    // 计算所有光源的贡献
    for (const auto& light : lights) {
        // 计算每个光源的方向和强度
        Vector3 L;
        float attenuation = 1.0f;
        Vector3 radiance = light.color * light.intensity;
        
        if (light.type == Light::DIRECTIONAL) {
            L = -light.direction.normalize();
        } else {
            // 点光源或聚光灯
            L = (light.position - position).normalize();
            
            // 计算距离和衰减
            float distance = (light.position - position).length();
            attenuation = 1.0f / (light.constant + light.linear * distance + 
                                 light.quadratic * distance * distance);
            
            radiance = radiance * attenuation;
            
            // 聚光灯效果
            if (light.type == Light::SPOT) {
                float theta = L.dot(-light.direction.normalize());
                float epsilon = light.cutoff - light.outerCutoff;
                float intensity = std::clamp((theta - light.outerCutoff) / epsilon, 0.0f, 1.0f);
                radiance = radiance * intensity;
            }
        }
        
        // 计算半角向量
        Vector3 H = (V + L).normalize();
        
        // Cook-Torrance BRDF
        float NdotV = std::max(N.dot(V), 0.0f);
        float NdotL = std::max(N.dot(L), 0.0f);
        float NdotH = std::max(N.dot(H), 0.0f);
        float HdotV = std::max(H.dot(V), 0.0f);
        
        // 法线分布函数 (GGX)
        float alpha = material.roughness * material.roughness;
        float alpha2 = alpha * alpha;
        float denom = NdotH * NdotH * (alpha2 - 1.0f) + 1.0f;
        float D = alpha2 / (3.14159f * denom * denom);
        
        // 几何遮蔽函数 (Smith GGX)
        float k = ((material.roughness + 1.0f) * (material.roughness + 1.0f)) / 8.0f;
        float G1_V = NdotV / (NdotV * (1.0f - k) + k);
        float G1_L = NdotL / (NdotL * (1.0f - k) + k);
        float G = G1_V * G1_L;
        
        // 菲涅尔方程 (Schlick近似)
        Vector3 F = F0 + (Vector3(1.0f, 1.0f, 1.0f) - F0) * std::pow(1.0f - HdotV, 5.0f);
        
        // 计算镜面反射项
        Vector3 numerator = D * G * F;
        float denominator = 4.0f * NdotV * NdotL + 0.001f;  // 防止除以零
        Vector3 specular = numerator / denominator;
        
        // 计算漫反射项
        Vector3 kD = Vector3(1.0f, 1.0f, 1.0f) - F;
        kD = kD * (1.0f - material.metallic);  // 金属没有漫反射
        
        // 添加到出射辐射率
        Lo = Lo + (kD * material.albedo / 3.14159f + specular) * radiance * NdotL;
    }
    
    // 添加环境光
    Vector3 ambient = Vector3(0.03f, 0.03f, 0.03f) * material.albedo * material.ao;
    
    Vector3 color = ambient + Lo;
    
    // HDR色调映射和gamma校正
    color = color / (color + Vector3(1.0f, 1.0f, 1.0f));  // Reinhard算子
    color = Vector3(std::pow(color.x, 1.0f/2.2f),         // gamma校正 (gamma=2.2)
                  std::pow(color.y, 1.0f/2.2f),
                  std::pow(color.z, 1.0f/2.2f));
    
    return color;
}

8.3 实际应用:渲染法线贴图

cpp

struct Vertex {
    Vector3 position;
    Vector3 normal;
    Vector2 texCoord;
    Vector3 tangent;
    Vector3 bitangent;
};

// 使用法线贴图计算切线空间中的法线
Vector3 calculateTangentSpaceNormal(const Vertex& vertex, const Vector3& normalMapColor) {
    // 将法线贴图颜色从[0,1]转换到[-1,1]范围
    Vector3 normalTS(
        normalMapColor.x * 2.0f - 1.0f,
        normalMapColor.y * 2.0f - 1.0f,
        normalMapColor.z * 2.0f - 1.0f
    );
    
    // 构建TBN矩阵
    Matrix4x4 TBN;
    TBN.m[0][0] = vertex.tangent.x;
    TBN.m[0][1] = vertex.tangent.y;
    TBN.m[0][2] = vertex.tangent.z;
    
    TBN.m[1][0] = vertex.bitangent.x;
    TBN.m[1][1] = vertex.bitangent.y;
    TBN.m[1][2] = vertex.bitangent.z;
    
    TBN.m[2][0] = vertex.normal.x;
    TBN.m[2][1] = vertex.normal.y;
    TBN.m[2][2] = vertex.normal.z;
    
    // 将切线空间法线转换到世界空间
    Vector3 worldNormal = TBN.transformDirection(normalTS);
    return worldNormal.normalize();
}

// 计算顶点的切线和副切线
void calculateTangentSpace(Vertex& v1, Vertex& v2, Vertex& v3) {
    Vector3 edge1 = v2.position - v1.position;
    Vector3 edge2 = v3.position - v1.position;
    Vector2 deltaUV1 = v2.texCoord - v1.texCoord;
    Vector2 deltaUV2 = v3.texCoord - v1.texCoord;
    
    float f = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV2.x * deltaUV1.y);
    
    Vector3 tangent, bitangent;
    
    tangent.x = f * (deltaUV2.y * edge1.x - deltaUV1.y * edge2.x);
    tangent.y = f * (deltaUV2.y * edge1.y - deltaUV1.y * edge2.y);
    tangent.z = f * (deltaUV2.y * edge1.z - deltaUV1.y * edge2.z);
    
    bitangent.x = f * (-deltaUV2.x * edge1.x + deltaUV1.x * edge2.x);
    bitangent.y = f * (-deltaUV2.x * edge1.y + deltaUV1.x * edge2.y);
    bitangent.z = f * (-deltaUV2.x * edge1.z + deltaUV1.x * edge2.z);
    
    // 确保切线系统形成右手坐标系
    Vector3 normal = v1.normal;
    Vector3 T = tangent;
    Vector3 B = normal.cross(T).normalize();
    
    v1.tangent = tangent.normalize();
    v2.tangent = tangent.normalize();
    v3.tangent = tangent.normalize();
    
    v1.bitangent = bitangent.normalize();
    v2.bitangent = bitangent.normalize();
    v3.bitangent = bitangent.normalize();
}

9. 三维重建

三维重建从图像或深度信息中构建3D模型。

9.1 从点云到网格

cpp

struct Point {
    Vector3 position;
    Vector3 normal;
};

// 从点云创建网格的简化算法(基于Marching Cubes的概念)
class PointCloudToMesh {
private:
    std::vector<Point> pointCloud;
    float voxelSize;
    float isoLevel;
    
    // 三维网格结构
    struct Voxel {
        bool hasPoints;
        std::vector<int> pointIndices;
    };
    
    std::vector<std::vector<std::vector<Voxel>>> voxelGrid;
    int gridSizeX, gridSizeY, gridSizeZ;
    Vector3 minBounds, maxBounds;
    
public:
    PointCloudToMesh(const std::vector<Point>& points, float voxelSize, float isoLevel = 0.5f)
        : pointCloud(points), voxelSize(voxelSize), isoLevel(isoLevel) {
        
        // 计算点云包围盒
        calculateBounds();
        
        // 初始化体素网格
        initializeVoxelGrid();
        
        // 将点分配到体素
        assignPointsToVoxels();
    }
    
    // 计算点云的包围盒
    void calculateBounds() {
        if (pointCloud.empty()) return;
        
        minBounds = maxBounds = pointCloud[0].position;
        
        for (const auto& point : pointCloud) {
            minBounds.x = std::min(minBounds.x, point.position.x);
            minBounds.y = std::min(minBounds.y, point.position.y);
            minBounds.z = std::min(minBounds.z, point.position.z);
            
            maxBounds.x = std::max(maxBounds.x, point.position.x);
            maxBounds.y = std::max(maxBounds.y, point.position.y);
            maxBounds.z = std::max(maxBounds.z, point.position.z);
        }
        
        // 添加一些边距
        const float padding = voxelSize * 2.0f;
        minBounds = minBounds - Vector3(padding, padding, padding);
        maxBounds = maxBounds + Vector3(padding, padding, padding);
        
        // 计算网格尺寸
        Vector3 size = maxBounds - minBounds;
        gridSizeX = static_cast<int>(std::ceil(size.x / voxelSize));
        gridSizeY = static_cast<int>(std::ceil(size.y / voxelSize));
        gridSizeZ = static_cast<int>(std::ceil(size.z / voxelSize));
    }
    
    // 初始化体素网格
    void initializeVoxelGrid() {
        voxelGrid.resize(gridSizeX);
        for (int x = 0; x < gridSizeX; x++) {
            voxelGrid[x].resize(gridSizeY);
            for (int y = 0; y < gridSizeY; y++) {
                voxelGrid[x][y].resize(gridSizeZ);
                for (int z = 0; z < gridSizeZ; z++) {
                    voxelGrid[x][y][z].hasPoints = false;
                }
            }
        }
    }
    
    // 将点分配到体素
    void assignPointsToVoxels() {
        for (size_t i = 0; i < pointCloud.size(); i++) {
            const auto& point = pointCloud[i];
            int x = static_cast<int>((point.position.x - minBounds.x) / voxelSize);
            int y = static_cast<int>((point.position.y - minBounds.y) / voxelSize);
            int z = static_cast<int>((point.position.z - minBounds.z) / voxelSize);
            
            // 确保索引在范围内
            if (x >= 0 && x < gridSizeX && y >= 0 && y < gridSizeY && z >= 0 && z < gridSizeZ) {
                voxelGrid[x][y][z].hasPoints = true;
                voxelGrid[x][y][z].pointIndices.push_back(i);
            }
        }
    }
    
    // 创建网格(简化版,只提取包含点的体素面)
    void createMesh(std::vector<Vector3>& vertices, std::vector<int>& indices) {
        vertices.clear();
        indices.clear();
        
        for (int x = 0; x < gridSizeX; x++) {
            for (int y = 0; y < gridSizeY; y++) {
                for (int z = 0; z < gridSizeZ; z++) {
                    if (voxelGrid[x][y][z].hasPoints) {
                        addVoxelFaces(x, y, z, vertices, indices);
                    }
                }
            }
        }
    }
    
    // 添加一个体素的可见面
    void addVoxelFaces(int x, int y, int z, std::vector<Vector3>& vertices, std::vector<int>& indices) {
        Vector3 voxelMin = minBounds + Vector3(x * voxelSize, y * voxelSize, z * voxelSize);
        
        // 检查六个相邻体素,如果不包含点,则添加面
        // 前面 (x+1)
        if (x + 1 >= gridSizeX || !voxelGrid[x+1][y][z].hasPoints) {
            addFace(voxelMin, Vector3(voxelSize, 0, 0), Vector3(0, voxelSize, 0), 
                   Vector3(0, 0, voxelSize), false, vertices, indices);
        }
        
        // 后面 (x-1)
        if (x - 1 < 0 || !voxelGrid[x-1][y][z].hasPoints) {
            addFace(voxelMin, Vector3(0, 0, 0), Vector3(0, voxelSize, 0), 
                   Vector3(0, 0, voxelSize), true, vertices, indices);
        }
        
        // 右面 (y+1)
        if (y + 1 >= gridSizeY || !voxelGrid[x][y+1][z].hasPoints) {
            addFace(voxelMin, Vector3(0, voxelSize, 0), Vector3(voxelSize, 0, 0), 
                   Vector3(0, 0, voxelSize), true, vertices, indices);
        }
        
        // 左面 (y-1)
        if (y - 1 < 0 || !voxelGrid[x][y-1][z].hasPoints) {
            addFace(voxelMin, Vector3(0, 0, 0), Vector3(voxelSize, 0, 0), 
                   Vector3(0, 0, voxelSize), false, vertices, indices);
        }
        
        // 上面 (z+1)
        if (z + 1 >= gridSizeZ || !voxelGrid[x][y][z+1].hasPoints) {
            addFace(voxelMin, Vector3(0, 0, voxelSize), Vector3(voxelSize, 0, 0), 
                   Vector3(0, voxelSize, 0), false, vertices, indices);
        }
        
        // 下面 (z-1)
        if (z - 1 < 0 || !voxelGrid[x][y][z-1].hasPoints) {
            addFace(voxelMin, Vector3(0, 0, 0), Vector3(voxelSize, 0, 0), 
                   Vector3(0, voxelSize, 0), true, vertices, indices);
        }
    }
    
    // 添加一个面(两个三角形)
    void addFace(const Vector3& origin, const Vector3& dirX, const Vector3& dirY, 
                const Vector3& dirZ, bool flipFace, std::vector<Vector3>& vertices, 
                std::vector<int>& indices) {
        size_t baseIndex = vertices.size();
        
        // 添加四个顶点
        vertices.push_back(origin);
        vertices.push_back(origin + dirX);
        vertices.push_back(origin + dirX + dirY);
        vertices.push_back(origin + dirY);
        
        // 添加两个三角形
        if (!flipFace) {
            indices.push_back(baseIndex);
            indices.push_back(baseIndex + 1);
            indices.push_back(baseIndex + 2);
            
            indices.push_back(baseIndex);
            indices.push_back(baseIndex + 2);
            indices.push_back(baseIndex + 3);
        } else {
            indices.push_back(baseIndex);
            indices.push_back(baseIndex + 2);
            indices.push_back(baseIndex + 1);
            
            indices.push_back(baseIndex);
            indices.push_back(baseIndex + 3);
            indices.push_back(baseIndex + 2);
        }
    }
};

9.2 从深度图像到点云

cpp

// 从深度图像到点云
std::vector<Vector3> depthImageToPointCloud(
    const std::vector<float>& depthImage,  // 深度值(米)
    int width, int height,                 // 图像尺寸
    float fovX, float fovY,                // 水平和垂直视场角(弧度)
    float nearPlane, float farPlane        // 近平面和远平面距离
) {
    std::vector<Vector3> pointCloud;
    pointCloud.reserve(width * height);
    
    float aspectRatio = static_cast<float>(width) / height;
    float tanHalfFovX = tan(fovX * 0.5f);
    float tanHalfFovY = tan(fovY * 0.5f);
    
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            float depth = depthImage[y * width + x];
            
            // 跳过无效深度值
            if (depth <= nearPlane || depth >= farPlane) {
                continue;
            }
            
            // 计算归一化设备坐标 (NDC)
            float ndcX = (2.0f * ((x + 0.5f) / width) - 1.0f) * tanHalfFovX * aspectRatio;
            float ndcY = (1.0f - 2.0f * ((y + 0.5f) / height)) * tanHalfFovY;
            
            // 转换为3D点
            Vector3 point(ndcX * depth, ndcY * depth, -depth);
            pointCloud.push_back(point);
        }
    }
    
    return pointCloud;
}

// 从点云估计表面法线
std::vector<Point> estimateNormals(const std::vector<Vector3>& pointPositions, int kNeighbors = 20) {
    std::vector<Point> pointCloud;
    pointCloud.reserve(pointPositions.size());
    
    // 初始化KD树(此处假设有KD树实现)
    KDTree kdTree;
    kdTree.build(pointPositions);
    
    for (const auto& position : pointPositions) {
        // 查找k个最近邻
        std::vector<int> neighbors = kdTree.findKNearestNeighbors(position, kNeighbors);
        
        // 计算质心
        Vector3 centroid(0, 0, 0);
        for (int idx : neighbors) {
            centroid = centroid + pointPositions[idx];
        }
        centroid = centroid * (1.0f / neighbors.size());
        
        // 构建协方差矩阵
        float cxx = 0, cxy = 0, cxz = 0, cyy = 0, cyz = 0, czz = 0;
        
        for (int idx : neighbors) {
            Vector3 diff = pointPositions[idx] - centroid;
            
            cxx += diff.x * diff.x;
            cxy += diff.x * diff.y;
            cxz += diff.x * diff.z;
            cyy += diff.y * diff.y;
            cyz += diff.y * diff.z;
            czz += diff.z * diff.z;
        }
        
        // 对3x3协方差矩阵进行特征值分解
        // 这里简化实现,实际应使用如Eigen库
        // 最小特征值对应的特征向量即为表面法线
        
        // 假设我们已经计算出法线
        Vector3 normal = calculateNormalFromCovarianceMatrix(cxx, cxy, cxz, cyy, cyz, czz);
        
        // 确保法线指向传感器
        if (normal.z > 0) {
            normal = normal * -1.0f;
        }
        
        // 添加点和法线
        Point point;
        point.position = position;
        point.normal = normal;
        pointCloud.push_back(point);
    }
    
    return pointCloud;
}

// 计算协方差矩阵的特征向量(简化版,实际应使用数值库)
Vector3 calculateNormalFromCovarianceMatrix(
    float cxx, float cxy, float cxz, float cyy, float cyz, float czz) {
    // 实际实现应该解决3x3特征值问题
    // 此处返回一个随机法线代替
    Vector3 randomNormal(
        static_cast<float>(rand()) / RAND_MAX - 0.5f,
        static_cast<float>(rand()) / RAND_MAX - 0.5f,
        static_cast<float>(rand()) / RAND_MAX - 0.5f
    );
    return randomNormal.normalize();
}

10. 图形渲染管线

图形渲染管线是实时3D图形的核心处理流程。

10.1 简单的软件渲染器

cpp

class SoftwareRenderer {
private:
    std::vector<unsigned char> colorBuffer;  // 颜色缓冲区 (RGBA)
    std::vector<float> depthBuffer;          // 深度缓冲区
    int width, height;
    
    Matrix4x4 modelMatrix;          // 模型变换
    Matrix4x4 viewMatrix;           // 视图变换
    Matrix4x4 projectionMatrix;     // 投影变换
    
public:
    SoftwareRenderer(int width, int height) 
        : width(width), height(height) {
        // 初始化颜色缓冲区
        colorBuffer.resize(width * height * 4, 0);
        
        // 初始化深度缓冲区(初始化为最远)
        depthBuffer.resize(width * height, 1.0f);
        
        // 初始化变换矩阵为单位矩阵
        modelMatrix = Matrix4x4();
        viewMatrix = Matrix4x4();
        projectionMatrix = Matrix4x4();
    }
    
    // 设置变换矩阵
    void setModelMatrix(const Matrix4x4& matrix) { modelMatrix = matrix; }
    void setViewMatrix(const Matrix4x4& matrix) { viewMatrix = matrix; }
    void setProjectionMatrix(const Matrix4x4& matrix) { projectionMatrix = matrix; }
    
    // 清除缓冲区
    void clear(const Vector3& color, float depth = 1.0f) {
        for (int i = 0; i < width * height; i++) {
            colorBuffer[i * 4] = static_cast<unsigned char>(color.x * 255);
            colorBuffer[i * 4 + 1] = static_cast<unsigned char>(color.y * 255);
            colorBuffer[i * 4 + 2] = static_cast<unsigned char>(color.z * 255);
            colorBuffer[i * 4 + 3] = 255;  // Alpha设为不透明
            
            depthBuffer[i] = depth;
        }
    }
    
    // 获取颜色缓冲区
    const std::vector<unsigned char>& getColorBuffer() const { return colorBuffer; }
    
    // 渲染三角形
    void drawTriangle(const Vertex& v1, const Vertex& v2, const Vertex& v3, 
                     const Material& material, const std::vector<Light>& lights) {
        // 顶点着色:应用变换
        Vertex tv1 = transformVertex(v1);
        Vertex tv2 = transformVertex(v2);
        Vertex tv3 = transformVertex(v3);
        
        // 背面剔除(简化实现)
        Vector3 e1 = tv2.position - tv1.position;
        Vector3 e2 = tv3.position - tv1.position;
        Vector3 normal = e1.cross(e2).normalize();
        
        if (normal.z < 0) return;  // 背面,不渲染
        
        // 透视除法和视口变换
        Vertex sv1 = perspectiveDivideAndViewportTransform(tv1);
        Vertex sv2 = perspectiveDivideAndViewportTransform(tv2);
        Vertex sv3 = perspectiveDivideAndViewportTransform(tv3);
        
        // 计算包围盒
        int minX = std::max(0, static_cast<int>(std::min(std::min(sv1.position.x, sv2.position.x), sv3.position.x)));
        int minY = std::max(0, static_cast<int>(std::min(std::min(sv1.position.y, sv2.position.y), sv3.position.y)));
        int maxX = std::min(width - 1, static_cast<int>(std::max(std::max(sv1.position.x, sv2.position.x), sv3.position.x)));
        int maxY = std::min(height - 1, static_cast<int>(std::max(std::max(sv1.position.y, sv2.position.y), sv3.position.y)));
        
        // 遍历包围盒中的每个像素
        for (int y = minY; y <= maxY; y++) {
            for (int x = minX; x <= maxX; x++) {
                // 计算重心坐标
                Vector3 bc = calculateBarycentricCoord(
                    Vector3(x, y, 0), 
                    Vector3(sv1.position.x, sv1.position.y, 0),
                    Vector3(sv2.position.x, sv2.position.y, 0),
                    Vector3(sv3.position.x, sv3.position.y, 0)
                );
                
                // 如果点在三角形外,跳过
                if (bc.x < 0 || bc.y < 0 || bc.z < 0) continue;
                
                // 计算深度值
                float depth = bc.x * sv1.position.z + bc.y * sv2.position.z + bc.z * sv3.position.z;
                
                // 深度测试
                int index = y * width + x;
                if (depth < depthBuffer[index]) {
                    // 更新深度缓冲区
                    depthBuffer[index] = depth;
                    
                    // 插值法线
                    Vector3 normal = (bc.x * sv1.normal + bc.y * sv2.normal + bc.z * sv3.normal).normalize();
                    
                    // 插值纹理坐标
                    Vector2 texCoord = bc.x * sv1.texCoord + bc.y * sv2.texCoord + bc.z * sv3.texCoord;
                    
                    // 计算片段位置
                    Vector3 worldPos = bc.x * v1.position + bc.y * v2.position + bc.z * v3.position;
                    
                    // 视线方向
                    Vector3 viewDir = (Vector3(0, 0, 0) - worldPos).normalize();  // 假设相机在原点
                    
                    // 片段着色:计算光照
                    Vector3 color(0, 0, 0);
                    for (const auto& light : lights) {
                        color = color + calculatePhongLighting(worldPos, normal, viewDir, material, light);
                    }
                    
                    // 写入颜色缓冲区
                    colorBuffer[index * 4] = static_cast<unsigned char>(std::min(color.x * 255.0f, 255.0f));
                    colorBuffer[index * 4 + 1] = static_cast<unsigned char>(std::min(color.y * 255.0f, 255.0f));
                    colorBuffer[index * 4 + 2] = static_cast<unsigned char>(std::min(color.z * 255.0f, 255.0f));
                    colorBuffer[index * 4 + 3] = 255;  // Alpha
                }
            }
        }
    }
    
private:
    // 应用模型-视图-投影变换
    Vertex transformVertex(const Vertex& vertex) {
        Vertex transformed = vertex;
        
        // 应用模型变换
        transformed.position = modelMatrix.transformPoint(vertex.position);
        transformed.normal = modelMatrix.transformDirection(vertex.normal).normalize();
        
        // 应用视图变换
        transformed.position = viewMatrix.transformPoint(transformed.position);
        transformed.normal = viewMatrix.transformDirection(transformed.normal).normalize();
        
        // 应用投影变换
        transformed.position = projectionMatrix.transformPoint(transformed.position);
        
        return transformed;
    }
    
    // 透视除法和视口变换
    Vertex perspectiveDivideAndViewportTransform(const Vertex& vertex) {
        Vertex result = vertex;
        
        // 透视除法
        float invW = 1.0f / vertex.position.z;
        result.position.x *= invW;
        result.position.y *= invW;
        result.position.z *= invW;
        
        // 视口变换
        result.position.x = (result.position.x + 1.0f) * 0.5f * width;
        result.position.y = (1.0f - result.position.y) * 0.5f * height;
        
        return result;
    }
    
    // 计算重心坐标
    Vector3 calculateBarycentricCoord(
        const Vector3& p, const Vector3& a, const Vector3& b, const Vector3& c) {
        Vector3 v0 = b - a;
        Vector3 v1 = c - a;
        Vector3 v2 = p - a;
        
        float d00 = v0.dot(v0);
        float d01 = v0.dot(v1);
        float d11 = v1.dot(v1);
        float d20 = v2.dot(v0);
        float d21 = v2.dot(v1);
        
        float denom = d00 * d11 - d01 * d01;
        
        float v = (d11 * d20 - d01 * d21) / denom;
        float w = (d00 * d21 - d01 * d20) / denom;
        float u = 1.0f - v - w;
        
        return Vector3(u, v, w);
    }
};

以上代码示例涵盖了3D数学在计算机图形学、游戏开发、虚拟现实和物理模拟等领域的核心应用。实际项目中通常会使用更优化的算法和数据结构,并且会利用GPU加速来提高性能。这些基础概念和实现方法为理解和开发复杂的3D应用程序提供了坚实的基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小宝哥Code

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

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

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

打赏作者

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

抵扣说明:

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

余额充值