写在前面
两天都没手搓实现可用的凸包生成算法相关的代码,自觉无法手搓相关数学库,遂改为使用成熟数学库。
一、GLM库安装与介绍
1.1 vcpkg安装GLM
在PowerShell中执行命令:
bash
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库使用示例代码(矩阵计算、四元数计算等)
cpp
// 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中执行命令:
bash
vcpkg install cgal
注:安装过程较长,很慢。
2.2 CGAL示例代码(凸包生成、三角剖分)
cpp
#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;
}