寫在前面
兩天都沒手搓實現可用的凸包生成算法相關的代碼,自覺無法手搓相關數學庫,遂改為使用成熟數學庫。
一、GLM庫安裝與介紹
1.1 vcpkg安裝GLM
跨平臺C++包管理利器vcpkg完全指南
在PowerShell中執行命令:
vcpkg install glm# 集成到系統目錄,只需要執行一次,以前執行過就無需重復執行
vcpkg integrate install
1.2 GLM庫基礎數學對象
類型 | 描述 | 示例 |
---|---|---|
vec2/3/4 | 2/3/4維浮點向量 | vec3 position(1,2,3); |
mat2/3/4 | 2x2、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度的OBBOBB 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 ≈ Identityglm::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;
}