【C++游戏引擎开发】第9篇:数学计算库GLM(线性代数)、CGAL(几何计算)的安装与使用指南

news2025/4/13 2:55:51

写在前面

两天都没手搓实现可用的凸包生成算法相关的代码,自觉无法手搓相关数学库,遂改为使用成熟数学库。


一、GLM库安装与介绍

1.1 vcpkg安装GLM

跨平台C++包管理利器vcpkg完全指南

在PowerShell中执行命令:

vcpkg install glm

# 集成到系统目录,只需要执行一次,以前执行过就无需重复执行
vcpkg integrate install

1.2 GLM库基础数学对象

类型描述示例
vec2/3/42/3/4维浮点向量vec3 position(1,2,3);
mat2/3/42x2、3x3、4x4浮点矩阵mat4 view = lookAt(…);
quat四元数(旋转表示)quat rotation = angleAxis(…);
dvec*/dmat*双精度向量/矩阵dmat4 highPrecisionMat;

1.3 GLM库使用示例代码(矩阵计算、四元数计算等)

// main.cpp
// main.cpp
#include <iostream>
#include <limits>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/string_cast.hpp>  // 用于矩阵字符串输出
#include <glm/gtx/quaternion.hpp>



// 打印GLM矩阵(带标签)
template<typename T>
void print_matrix(const std::string& name, const T& mat) {
    std::cout << name << ":\n" << glm::to_string(mat) << "\n\n";
}

// 定义OBB结构体
struct OBB {
    glm::vec3 center;     // 包围盒中心
    glm::vec3 extents;    // 包围盒半长(x, y, z方向的半径)
    glm::mat3 rotation;   // 旋转矩阵(局部到世界坐标的变换)
};

// 射线与OBB相交检测(返回相交距离,未相交返回-1)
float rayOBBIntersection(
    const glm::vec3& rayOrigin,
    const glm::vec3& rayDir,
    const OBB& obb,
    float maxDistance = std::numeric_limits<float>::max()
) {
    // 将射线转换到OBB局部空间
    glm::mat3 invRotation = glm::transpose(obb.rotation); // 旋转的逆矩阵
    glm::vec3 localOrigin = invRotation * (rayOrigin - obb.center);
    glm::vec3 localDir = invRotation * rayDir;

    // 射线与AABB相交检测(在局部空间)
    float tMin = 0.0f;
    float tMax = maxDistance;

    // 分别检查每个轴
    for (int i = 0; i < 3; ++i) {
        float axisMin = -obb.extents[i] - localOrigin[i];
        float axisMax = obb.extents[i] - localOrigin[i];

        if (std::abs(localDir[i]) < 1e-6) { // 射线与轴平行
            if (localOrigin[i] < -obb.extents[i] || localOrigin[i] > obb.extents[i])
                return -1.0f;
        }
        else {
            float invDir = 1.0f / localDir[i];
            float t1 = axisMin * invDir;
            float t2 = axisMax * invDir;

            if (t1 > t2) std::swap(t1, t2);
            tMin = std::max(tMin, t1);
            tMax = std::min(tMax, t2);

            if (tMin > tMax) return -1.0f;
        }
    }

    return tMin;
}

// 在main函数中添加测试代码
void testRayOBB() {
    std::cout << "===== OBB射线检测测试 =====" << std::endl;

    // 创建一个旋转45度的OBB
    OBB obb;
    obb.center = glm::vec3(2.0f, 0.0f, 0.0f);
    obb.extents = glm::vec3(1.0f, 0.5f, 0.5f);
    obb.rotation = glm::mat3_cast(glm::angleAxis(glm::radians(45.0f), glm::vec3(0, 0, 1)));

    // 测试射线1:应相交
    glm::vec3 rayOrigin1(0.0f, 0.0f, 0.0f);
    glm::vec3 rayDir1 = glm::normalize(glm::vec3(1.0f, 0.0f, 0.0f));
    float t1 = rayOBBIntersection(rayOrigin1, rayDir1, obb);
    std::cout << "射线1结果: " << (t1 >= 0 ? "命中,距离=" + std::to_string(t1) : "未命中") << std::endl;

    // 测试射线2:应不相交
    glm::vec3 rayOrigin2(0.0f, 2.0f, 0.0f);
    glm::vec3 rayDir2 = glm::normalize(glm::vec3(1.0f, 0.0f, 0.0f));
    float t2 = rayOBBIntersection(rayOrigin2, rayDir2, obb);
    std::cout << "射线2结果: " << (t2 >= 0 ? "命中,距离=" + std::to_string(t2) : "未命中") << std::endl;

    // 测试射线3:从内部发射
    glm::vec3 rayOrigin3 = obb.center;
    glm::vec3 rayDir3 = glm::normalize(glm::vec3(1.0f, 0.0f, 0.0f));
    float t3 = rayOBBIntersection(rayOrigin3, rayDir3, obb);
    std::cout << "射线3结果: " << (t3 >= 0 ? "命中,距离=" + std::to_string(t3) : "未命中") << std::endl;

    std::cout << "\n";
}

int main() {
    // ======================
    // 1. 矩阵基本操作
    // ======================
    // 创建两个4x4矩阵
    glm::mat4 A(1.0f);  // 单位矩阵
    glm::mat4 B = glm::translate(glm::mat4(1.0f), glm::vec3(2, 3, 4));  // 平移矩阵

    // 矩阵加法
    glm::mat4 C = A + B;
    print_matrix("Matrix A (Identity)", A);
    print_matrix("Matrix B (Translation)", B);
    print_matrix("Matrix C = A + B", C);

    // 矩阵减法
    glm::mat4 D = B - A;
    print_matrix("Matrix D = B - A", D);

    // 矩阵乘法(组合变换)
    glm::mat4 trans = glm::translate(glm::mat4(1.0f), glm::vec3(1, 0, 0));
    glm::mat4 scale = glm::scale(glm::mat4(1.0f), glm::vec3(2, 2, 2));
    glm::mat4 combined = trans * scale;  // 先缩放后平移
    print_matrix("Combined Matrix (Scale then Translate)", combined);

    // ======================
    // 2. 矩阵求逆
    // ======================
    glm::mat4 invB = glm::inverse(B);
    print_matrix("Inverse of Matrix B", invB);

    // 验证B * invB ≈ Identity
    glm::mat4 identityCheck = B * invB;
    print_matrix("B * invB (Should be Identity)", identityCheck);

    // ======================
    // 3. 四元数操作
    // ======================
    // 创建绕Y轴旋转45度的四元数
    glm::quat q1 = glm::angleAxis(glm::radians(45.0f), glm::vec3(0, 1, 0));

    // 创建绕X轴旋转30度的四元数
    glm::quat q2 = glm::angleAxis(glm::radians(30.0f), glm::vec3(1, 0, 0));

    // 四元数插值(球面线性插值)
    glm::quat slerped = glm::slerp(q1, q2, 0.5f);

    // 将四元数转换为旋转矩阵
    glm::mat4 rotMat = glm::mat4_cast(slerped);
    print_matrix("Rotation Matrix from Slerped Quaternion", rotMat);

    // 使用四元数旋转向量
    glm::vec3 originalVec(1, 0, 0);
    glm::vec3 rotatedVec = slerped * originalVec;
    std::cout << "Original Vector: (" << originalVec.x << ", " << originalVec.y << ", " << originalVec.z << ")\n";
    std::cout << "Rotated Vector: (" << rotatedVec.x << ", " << rotatedVec.y << ", " << rotatedVec.z << ")\n\n";

    testRayOBB();

    return 0;
}

二、CGAL库安装与使用

2.1 vcpkg安装CGAL库

在PowerShell中执行命令:

vcpkg install cgal

注:安装过程较长,很慢。

2.2 CGAL示例代码(凸包生成、三角剖分)

#include <iostream>
#include <vector>
#include <iterator>  // 添加iterator头文件
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/convex_hull_2.h>
#include <CGAL/convex_hull_3.h>
#include <CGAL/Delaunay_triangulation_2.h>
#include <CGAL/Delaunay_triangulation_3.h>
#include <CGAL/point_generators_2.h>
#include <CGAL/point_generators_3.h>
#include <CGAL/Polyhedron_3.h>  // 添加Polyhedron_3头文件

using namespace std;

// 定义内核类型(快速浮点数计算)
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::Point_2 Point_2;
typedef K::Point_3 Point_3;
typedef CGAL::Polyhedron_3<K> Polyhedron_3;  // 定义多面体类型

//------- 二维凸包测试 -------
void test_2d_convex_hull() {
    cout << "===== 二维凸包测试 =====" << endl;

    // 生成100个随机二维点(坐标范围[0, 100))
    CGAL::Random_points_in_square_2<Point_2> gen(50.0);
    vector<Point_2> points;
    const int NUM_POINTS = 20;
    CGAL::cpp11::copy_n(gen, NUM_POINTS, back_inserter(points));

    // 计算凸包
    vector<Point_2> hull;
    CGAL::convex_hull_2(points.begin(), points.end(), back_inserter(hull));

    // 输出结果
    cout << "原始点集(" << points.size() << "):" << endl;
    for (const auto& p : points)
        cout << "(" << p.x() << ", " << p.y() << ") ";

    cout << "\n\n凸包顶点(" << hull.size() << "):" << endl;
    for (const auto& p : hull)
        cout << "(" << p.x() << ", " << p.y() << ") ";
    cout << "\n\n";
}

//------- 三维凸包测试 -------
void test_3d_convex_hull() {
    cout << "===== 三维凸包测试 =====" << endl;

    // 生成20个随机三维点
    CGAL::Random_points_in_sphere_3<Point_3> gen(50.0);
    vector<Point_3> points;
    const int NUM_POINTS = 20;
    copy_n(gen, NUM_POINTS, back_inserter(points));  // 移除非必要的CGAL::cpp11::

    // 计算三维凸包
    Polyhedron_3 hull;
    CGAL::convex_hull_3(points.begin(), points.end(), hull);

    // 输出结果
    cout << "三维凸包面数: " << std::distance(hull.facets_begin(), hull.facets_end()) << endl;

    int faceCount = 0;
    for (auto face = hull.facets_begin(); face != hull.facets_end(); ++face, ++faceCount) {
        cout << "面 " << faceCount << ": ";
        auto he = face->halfedge();
        for (int i = 0; i < 3; ++i) {  // 假设所有面都是三角形
            const auto& p = he->vertex()->point();
            cout << "(" << p.x() << ", " << p.y() << ", " << p.z() << ") ";
            he = he->next();
        }
        cout << endl;
    }
    cout << "\n";
}
//------- 二维Delaunay三角剖分测试 -------
void test_2d_delaunay() {
    cout << "===== 二维Delaunay三角剖分测试 =====" << endl;

    // 生成100个随机二维点
    CGAL::Random_points_in_square_2<Point_2> gen(50.0);
    vector<Point_2> points;
    const int NUM_POINTS = 10;
    copy_n(gen, NUM_POINTS, back_inserter(points));  // 移除非必要的CGAL::cpp11::

    // 构建Delaunay三角网
    CGAL::Delaunay_triangulation_2<K> dt;
    dt.insert(points.begin(), points.end());

    // 输出统计信息
    cout << "顶点数: " << dt.number_of_vertices() << endl;
    cout << "面数: " << dt.number_of_faces() << endl;

    // 遍历所有边(正确方式)
    cout << "\n边列表:" << endl;
    for (auto edge = dt.finite_edges_begin(); edge != dt.finite_edges_end(); ++edge) {
        auto segment = dt.segment(*edge);  // 直接获取边对应的线段
        cout << "(" << segment.source().x() << ", " << segment.source().y() << ") - "
            << "(" << segment.target().x() << ", " << segment.target().y() << ")\n";
    }
    cout << "\n";
}

//------- 三维Delaunay三角剖分测试 -------
void test_3d_delaunay() {
    cout << "===== 三维Delaunay三角剖分测试 =====" << endl;

    // 生成10个随机三维点
    CGAL::Random_points_in_sphere_3<Point_3> gen(50.0);
    vector<Point_3> points;
    const int NUM_POINTS = 10;
    CGAL::cpp11::copy_n(gen, NUM_POINTS, back_inserter(points));

    // 构建三维Delaunay三角网
    CGAL::Delaunay_triangulation_3<K> dt;
    dt.insert(points.begin(), points.end());

    // 输出统计信息
    cout << "顶点数: " << dt.number_of_vertices() << endl;
    cout << "边数: " << dt.number_of_edges() << endl;
    cout << "面数: " << dt.number_of_facets() << endl;
    cout << "四面体数: " << dt.number_of_cells() << endl;

    // 遍历所有四面体
    cout << "\n四面体列表:" << endl;
    for (auto cell = dt.finite_cells_begin(); cell != dt.finite_cells_end(); ++cell) {
        const Point_3& p0 = cell->vertex(0)->point();
        const Point_3& p1 = cell->vertex(1)->point();
        const Point_3& p2 = cell->vertex(2)->point();
        const Point_3& p3 = cell->vertex(3)->point();
        cout << "四面体: \n";
        cout << " (" << p0.x() << ", " << p0.y() << ", " << p0.z() << ")\n"
            << " (" << p1.x() << ", " << p1.y() << ", " << p1.z() << ")\n"
            << " (" << p2.x() << ", " << p2.y() << ", " << p2.z() << ")\n"
            << " (" << p3.x() << ", " << p3.y() << ", " << p3.z() << ")\n";
    }
    cout << "\n";
}

int main() {
    // 运行各测试用例
    test_2d_convex_hull();
    test_3d_convex_hull();
    test_2d_delaunay();
    test_3d_delaunay();

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2333516.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

我可能用到的网站和软件

我可能用到的网站和软件 程序员交流的网站代码管理工具前端组件库前端框架在线工具人工智能问答工具学习的网站Windows系统电脑的常用工具 程序员交流的网站 csdn博客博客园 - 开发者的网上家园InfoQ - 软件开发及相关领域-极客邦掘金 (juejin.cn) 代码管理工具 GitHub 有时…

FPGA状态机设计:流水灯实现、Modelsim仿真、HDLBits练习

一、状态机思想 1.概念 状态机&#xff08;Finite State Machine, FSM&#xff09;是计算机科学和工程领域中的一种抽象模型&#xff0c;用于描述系统在不同状态之间的转换逻辑。其核心思想是将复杂的行为拆解为有限的状态&#xff0c;并通过事件触发状态间的转移。 2.状态机…

2024年第十五届蓝桥杯CC++大学A组--成绩统计

2024年第十五届蓝桥杯C&C大学A组--成绩统计 题目&#xff1a; 动态规划&#xff0c; 对于该题&#xff0c;考虑动态规划解法&#xff0c;先取前k个人的成绩计算其方差&#xff0c;并将成绩记录在数组中&#xff0c;记录当前均值&#xff0c;设小蓝已检查前i-1个人的成绩&…

Kotlin 学习-集合

/*** kotlin 集合* List:是一个有序列表&#xff0c;可通过索引&#xff08;下标&#xff09;访问元素。元素可以在list中出现多次、元素可重复* Set:是元素唯一的集合。一般来说 set中的元素顺序并不重要、无序集合* Map:&#xff08;字典&#xff09;是一组键值对。键是唯一的…

自动驾驶的未来:多模态感知融合技术最新进展

作为自动驾驶领域的专业人士&#xff0c;我很高兴与大家分享关于多模态感知融合技术的前沿研究和实践经验。在迅速发展的自动驾驶领域&#xff0c;多模态感知融合已成为提升系统性能的关键技术。本文将深入探讨基于摄像头和激光雷达的多模态感知融合技术&#xff0c;重点关注最…

亮相2025全球分布式云大会,火山引擎边缘云落地AI新场景

4 月 9 日&#xff0c;2025 全球分布式云大会暨 AI 基础设施大会在深圳成功举办&#xff0c;火山引擎边缘云产品解决方案高级总监沈建发出席并以《智启边缘&#xff0c;畅想未来&#xff1a;边缘计算新场景落地与 Al 趋势新畅想》为主题&#xff0c;分享了边缘计算在 AI 技术趋…

无损分区管理,硬盘管理的“瑞士军刀”!

打工人们你们好&#xff01;这里是摸鱼 特供版~ 今天给大家带来一款简单易用、功能强大的无损分区软件——分区助手技术员版&#xff0c;让你的硬盘管理变得轻松又高效&#xff01; 推荐指数&#xff1a;★★★★★ 软件简介 分区助手技术员版是一款功能强大的硬盘分区工具&…

VS Code下开发FPGA——FPGA开发体验提升__下

上一篇&#xff1a;IntelliJ IDEA下开发FPGA-CSDN博客 Type&#xff1a;Quartus 一、安装插件 在应用商店先安装Digtal IDE插件 安装后&#xff0c;把其他相关的Verilog插件禁用&#xff0c;避免可能的冲突。重启后&#xff0c;可能会弹出下面提示 这是插件默认要求的工具链&a…

ffmpeg播放音视频流程

文章目录 &#x1f3ac; FFmpeg 解码播放流程概览&#xff08;以音视频文件为例&#xff09;1️⃣ 创建结构体2️⃣ 打开音视频文件3️⃣ 查找解码器并打开解码器4️⃣ 循环读取数据包&#xff08;Packet&#xff09;5️⃣ 解码成帧&#xff08;Frame&#xff09;6️⃣ 播放 / …

SpringCloud微服务: 分布式架构实战

# SpringCloud微服务: 分布式架构实战 第一章&#xff1a;理解SpringCloud微服务架构 什么是SpringCloud微服务架构&#xff1f; 在当今互联网应用开发中&#xff0c;微服务架构已经成为业界的主流趋势。SpringCloud是一个基于Spring Boot的快速开发微服务架构的工具&#xff0…

AI预测3D新模型百十个定位预测+胆码预测+去和尾2025年4月11日第49弹

从今天开始&#xff0c;咱们还是暂时基于旧的模型进行预测&#xff0c;好了&#xff0c;废话不多说&#xff0c;按照老办法&#xff0c;重点8-9码定位&#xff0c;配合三胆下1或下2&#xff0c;杀1-2个和尾&#xff0c;再杀6-8个和值&#xff0c;可以做到100-300注左右。 (1)定…

【models】Transformer 之 各种 Attention 原理和实现

Transformer 之 各种 Attention 原理和实现 本文将介绍Transformer 中常见的Attention的原理和实现&#xff0c;其中包括&#xff1a; Self Attention、Spatial Attention、Temporal Attention、Cross Attention、Grouped Attention、Tensor Product Attention、FlashAttentio…

老硬件也能运行的Win11 IoT LTSC (OEM)物联网版

#记录工作 Windows 11 IoT Enterprise LTSC 2024 属于物联网相关的版本。 Windows 11 IoT Enterprise 是为物联网设备和场景设计的操作系统版本。它通常针对特定的工业控制、智能设备等物联网应用进行了优化和定制&#xff0c;以满足这些领域对稳定性、安全性和长期支持的需求…

Git开发

目录 Linux下Git安装Git基本指令分支管理远程仓库与本地仓库标签管理多人协作同一分支下不同分支下 企业级开发模型 -- git flow 模型 在现实中&#xff0c;当我们完成一个文档的初稿后&#xff0c;后面可能还需要对初稿进行反复修改&#xff0c;从而形成不同版本的文档。显然&…

verilog有符号数的乘法

无符号整数的乘法 1、单周期乘法器&#xff08; 无符号整数 &#xff09; 对于低速要求的乘法器&#xff0c;可以简单的使用 * 实现。 module Mult(input wire [7:0] multiplicand ,input wire [7:0] multipliter ,output wire [7:0] product);as…

DevDocs:抓取并整理技术文档的MCP服务

GitHub&#xff1a;https://github.com/cyberagiinc/DevDocs 更多AI开源软件&#xff1a;发现分享好用的AI工具、AI开源软件、AI模型、AI变现 - 小众AI DevDocs 是一个完全免费的开源工具&#xff0c;由 CyberAGI 团队开发&#xff0c;托管在 GitHub 上。它专为程序员和软件开发…

第十四届蓝桥杯大赛软件赛国赛Python大学B组题解

文章目录 弹珠堆放划分偶串交易账本背包问题翻转最大阶梯最长回文前后缀贸易航线困局 弹珠堆放 递推式 a i a i − 1 i a_ia_{i-1}i ai​ai−1​i&#xff0c; n 20230610 n20230610 n20230610非常小&#xff0c;直接模拟 答案等于 494 494 494 划分 因为总和为 1 e 6 1e6…

折叠屏手机:技术进步了,柔性OLED面板测试技术需求跟上了吗?

全球智能手机市场陷入创新焦虑&#xff0c;折叠屏手机被寄予厚望&#xff0c;2023 年出货量同比增长 62%。但在供应链技术狂欢背后&#xff0c;存在诸多问题。消费端数据显示&#xff0c;用户使用频率低&#xff0c;定价策略反常。产业链重构虽让部分企业获利&#xff0c;却推高…

30天学Java第九天——线程

并行与并发的区别 并行是多核 CPU 上的多任务处理&#xff0c;多个任务在同一时间真正的同时执行并发是单核 CPU 上的多任务处理&#xff0c;多个任务在同一时间段内交替执行&#xff0c;通过时间片轮转实现交替执行&#xff0c;用于解决 IO 密集型任务的瓶颈 线程的创建方式…

自行搭建一个Git仓库托管平台

1.安装Git sudo apt install git 2.Git本地仓库创建&#xff08;自己选择一个文件夹&#xff09; git init 这里我在 /home/test 下面初始化了代码仓库 1. 首先在仓库中新建一个txt文件&#xff0c;并输入一些内容 2. 将文件添加到仓库 git add test.txt 执行之后没有任何输…