Files
ViewDesignEngine/include/vde/collision/ray_intersect.h
T

216 lines
6.3 KiB
C++
Raw Normal View History

/**
* @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"
2026-07-23 12:36:39 +00:00
#include "vde/core/aabb.h"
#include <optional>
2026-07-23 12:36:39 +00:00
#include <vector>
namespace vde::collision {
using core::Point3D;
using core::Vector3D;
using core::Triangle3D;
using core::Ray3Dd;
2026-07-23 12:36:39 +00:00
using core::AABB3D;
/**
* @brief 射线-三角形求交结果
*/
struct RayTriResult {
double t; /**< 沿射线的参数:hit_point = origin + t * dir */
double u, v; /**< 三角形重心坐标:hit_point = (1uv)*v0 + u*v1 + v*v2 */
Point3D point; /**< 命中点的世界坐标 */
};
/**
* @brief 射线-三角形求交(Möller-Trumbore 算法)
*
* 标准 MT 算法,直接计算重心坐标 u, v 和参数 t。
* 不预先计算平面方程,一步到位检测。
*
* 算法:
* 1. 计算两个边向量 e1 = v1v0, e2 = v2v0
* 2. 计算 pvec = dir × e2, det = dot(e1, pvec)
* 3. 若 |det| < ε → 射线平行于三角形(无命中)
* 4. 计算 tvec = originv0, 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
*/
2026-07-23 12:36:39 +00:00
bool ray_aabb_intersect(const Ray3Dd& ray, const AABB3D& box,
double& tmin_out, double& tmax_out);
/**
* @brief 射线-球体求交结果
*/
2026-07-23 12:36:39 +00:00
struct RaySphereResult {
double t; /**< 命中参数(通常取较小的 t) */
Point3D point; /**< 命中点世界坐标 */
2026-07-23 12:36:39 +00:00
};
/**
* @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)
*/
2026-07-23 12:36:39 +00:00
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;未命中返回空
*/
2026-07-23 12:36:39 +00:00
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;未命中(平行或反向)返回空
*/
2026-07-23 12:36:39 +00:00
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;未命中返回空
*/
2026-07-23 12:36:39 +00:00
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)
*/
2026-07-23 12:36:39 +00:00
std::optional<RayTriResult> ray_mesh_intersect(
const Point3D& origin, const Vector3D& dir,
const std::vector<Triangle3D>& triangles);
/**
* @brief 射线-网格求交(Ray3Dd 重载)
*
* @param ray 射线
* @param triangles 三角形列表
* @return 最近命中结果;无命中返回空
*/
2026-07-23 12:36:39 +00:00
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