216 lines
6.3 KiB
C++
216 lines
6.3 KiB
C++
/**
|
||
* @file ray_intersect.h
|
||
* @brief 射线求交工具集
|
||
*
|
||
* 射线与基本几何图元(三角形、AABB、球体、平面、网格)的求交函数。
|
||
* 包含 Möller-Trumbore 算法、slab 方法、解析求交等。
|
||
*
|
||
* @ingroup collision
|
||
*/
|
||
|
||
#pragma once
|
||
#include "vde/core/point.h"
|
||
#include "vde/core/line.h"
|
||
#include "vde/core/triangle.h"
|
||
#include "vde/core/aabb.h"
|
||
#include <optional>
|
||
#include <vector>
|
||
|
||
namespace vde::collision {
|
||
using core::Point3D;
|
||
using core::Vector3D;
|
||
using core::Triangle3D;
|
||
using core::Ray3Dd;
|
||
using core::AABB3D;
|
||
|
||
/**
|
||
* @brief 射线-三角形求交结果
|
||
*/
|
||
struct RayTriResult {
|
||
double t; /**< 沿射线的参数:hit_point = origin + t * dir */
|
||
double u, v; /**< 三角形重心坐标:hit_point = (1−u−v)*v0 + u*v1 + v*v2 */
|
||
Point3D point; /**< 命中点的世界坐标 */
|
||
};
|
||
|
||
/**
|
||
* @brief 射线-三角形求交(Möller-Trumbore 算法)
|
||
*
|
||
* 标准 MT 算法,直接计算重心坐标 u, v 和参数 t。
|
||
* 不预先计算平面方程,一步到位检测。
|
||
*
|
||
* 算法:
|
||
* 1. 计算两个边向量 e1 = v1−v0, e2 = v2−v0
|
||
* 2. 计算 pvec = dir × e2, det = dot(e1, pvec)
|
||
* 3. 若 |det| < ε → 射线平行于三角形(无命中)
|
||
* 4. 计算 tvec = origin−v0, u, v, t
|
||
* 5. 验证 u≥0, v≥0, u+v≤1, t>0 → 命中
|
||
*
|
||
* 复杂度 O(1)。
|
||
*
|
||
* @param origin 射线起点
|
||
* @param dir 射线方向(需归一化以获得正确 t 值)
|
||
* @param tri 三角形
|
||
* @return 命中返回 RayTriResult;未命中返回空
|
||
*
|
||
* @code{.cpp}
|
||
* if (auto hit = ray_triangle_intersect(origin, dir, tri)) {
|
||
* std::cout << "Hit at t=" << hit->t
|
||
* << " barycentric=(" << hit->u << "," << hit->v << ")\n";
|
||
* }
|
||
* @endcode
|
||
*
|
||
* @see ray_mesh_intersect
|
||
*/
|
||
std::optional<RayTriResult> ray_triangle_intersect(
|
||
const Point3D& origin, const Vector3D& dir, const Triangle3D& tri);
|
||
|
||
/**
|
||
* @brief 射线-三角形求交(Ray3Dd 重载)
|
||
*
|
||
* @param ray 射线
|
||
* @param tri 三角形
|
||
* @return 命中返回 RayTriResult;未命中返回空
|
||
*
|
||
* @see ray_triangle_intersect(origin, dir, tri)
|
||
*/
|
||
inline std::optional<RayTriResult> ray_triangle_intersect(
|
||
const Ray3Dd& ray, const Triangle3D& tri) {
|
||
return ray_triangle_intersect(ray.origin(), ray.direction(), tri);
|
||
}
|
||
|
||
/**
|
||
* @brief 射线-AABB 求交(slab 方法)
|
||
*
|
||
* 分别检测射线与三个坐标轴方向 slab(平板块)的交点,
|
||
* 取最大 t_min 和最小 t_max。若 t_min ≤ t_max 且 t_max ≥ 0,则命中。
|
||
*
|
||
* 复杂度 O(1)。
|
||
*
|
||
* @param ray 射线
|
||
* @param box 轴对齐包围盒
|
||
* @param tmin_out [输出] 进入包围盒的参数 t
|
||
* @param tmax_out [输出] 离开包围盒的参数 t
|
||
* @return 命中返回 true
|
||
*
|
||
* @note 常用于 BVH 遍历节点的快速 rejection test
|
||
*
|
||
* @see ray_triangle_intersect
|
||
*/
|
||
bool ray_aabb_intersect(const Ray3Dd& ray, const AABB3D& box,
|
||
double& tmin_out, double& tmax_out);
|
||
|
||
/**
|
||
* @brief 射线-球体求交结果
|
||
*/
|
||
struct RaySphereResult {
|
||
double t; /**< 命中参数(通常取较小的 t) */
|
||
Point3D point; /**< 命中点世界坐标 */
|
||
};
|
||
|
||
/**
|
||
* @brief 射线-球体求交(解析法)
|
||
*
|
||
* 解二次方程 |origin + t*dir − center|² = r²。
|
||
* 通过判别式 Δ = b² − 4ac 判断:
|
||
* - Δ < 0 → 无交点
|
||
* - Δ = 0 → 切点(一个交点)
|
||
* - Δ > 0 → 两个交点(返回较近的)
|
||
*
|
||
* 复杂度 O(1)。
|
||
*
|
||
* @param origin 射线起点
|
||
* @param dir 射线方向
|
||
* @param center 球心
|
||
* @param radius 球半径
|
||
* @return 命中返回 RaySphereResult(最近交点);未命中返回空
|
||
*
|
||
* @see ray_sphere_intersect(ray, center, radius)
|
||
*/
|
||
std::optional<RaySphereResult> ray_sphere_intersect(
|
||
const Point3D& origin, const Vector3D& dir,
|
||
const Point3D& center, double radius);
|
||
|
||
/**
|
||
* @brief 射线-球体求交(Ray3Dd 重载)
|
||
*
|
||
* @param ray 射线
|
||
* @param center 球心
|
||
* @param radius 球半径
|
||
* @return 命中返回 RaySphereResult;未命中返回空
|
||
*/
|
||
inline std::optional<RaySphereResult> ray_sphere_intersect(
|
||
const Ray3Dd& ray, const Point3D& center, double radius) {
|
||
return ray_sphere_intersect(ray.origin(), ray.direction(), center, radius);
|
||
}
|
||
|
||
/**
|
||
* @brief 射线-平面求交
|
||
*
|
||
* 解 t = dot(plane_point − origin, plane_normal) / dot(dir, plane_normal)。
|
||
* 若分母 ≈ 0(射线平行于平面)→ 无交点。
|
||
*
|
||
* @param origin 射线起点
|
||
* @param dir 射线方向
|
||
* @param plane_point 平面上一点
|
||
* @param plane_normal 平面法线(需归一化以获得正确 t)
|
||
* @return 命中返回参数 t;未命中(平行或反向)返回空
|
||
*/
|
||
std::optional<double> ray_plane_intersect(
|
||
const Point3D& origin, const Vector3D& dir,
|
||
const Point3D& plane_point, const Vector3D& plane_normal);
|
||
|
||
/**
|
||
* @brief 射线-平面求交(Ray3Dd 重载)
|
||
*
|
||
* @param ray 射线
|
||
* @param plane_point 平面上一点
|
||
* @param plane_normal 平面法线
|
||
* @return 命中返回参数 t;未命中返回空
|
||
*/
|
||
inline std::optional<double> ray_plane_intersect(
|
||
const Ray3Dd& ray, const Point3D& plane_point,
|
||
const Vector3D& plane_normal) {
|
||
return ray_plane_intersect(ray.origin(), ray.direction(),
|
||
plane_point, plane_normal);
|
||
}
|
||
|
||
/**
|
||
* @brief 射线-网格求交(暴力遍历)
|
||
*
|
||
* 遍历所有三角形,对每个三角形调用 ray_triangle_intersect,
|
||
* 跟踪最小 t 值返回最近命中。
|
||
*
|
||
* 复杂度 O(n),n 为三角形数量。
|
||
*
|
||
* @param origin 射线起点
|
||
* @param dir 射线方向
|
||
* @param triangles 三角形列表
|
||
* @return 最近命中结果;无命中返回空
|
||
*
|
||
* @note 对于大型网格建议配合 BVH 使用:
|
||
* @code{.cpp}
|
||
* BVH bvh;
|
||
* bvh.build(triangles);
|
||
* auto hit = bvh.query_ray_nearest(ray); // O(log n)
|
||
* @endcode
|
||
*
|
||
* @see ray_tri_intersect.h, query_ray_nearest (BVH)
|
||
*/
|
||
std::optional<RayTriResult> ray_mesh_intersect(
|
||
const Point3D& origin, const Vector3D& dir,
|
||
const std::vector<Triangle3D>& triangles);
|
||
|
||
/**
|
||
* @brief 射线-网格求交(Ray3Dd 重载)
|
||
*
|
||
* @param ray 射线
|
||
* @param triangles 三角形列表
|
||
* @return 最近命中结果;无命中返回空
|
||
*/
|
||
inline std::optional<RayTriResult> ray_mesh_intersect(
|
||
const Ray3Dd& ray, const std::vector<Triangle3D>& triangles) {
|
||
return ray_mesh_intersect(ray.origin(), ray.direction(), triangles);
|
||
}
|
||
|
||
} // namespace vde::collision
|