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

216 lines
6.3 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @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 = (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
*/
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