You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

光线求交计算中如何选择合适的Epsilon值?

光线追踪中Epsilon值的选择与三角形漏检问题解决

Epsilon值的核心选择原则

  • 绝对不要用固定死的极小值一刀切,优先根据场景尺度和物体尺寸动态调整:
    • 单精度浮点场景下,基础Epsilon可以从1e-6f开始测试;针对极小尺寸的三角形,可缩小到1e-8f,但注意不要过小引发新的精度问题。
    • 优先使用相对Epsilon:比如结合光线交点的t值(距离)计算,epsilon = t * 1e-6f——距离越远,浮点误差越大,需要的容差也越大。
  • 所有浮点数比较必须用绝对值小于Epsilon替代直接的==、<、>硬判断,这是解决漏检的核心。

你的代码中的关键问题

  1. 硬浮点数比较:pointInTriangle和intersectRayWithPlane中直接用==判断平面方程结果,浮点运算的微小误差会直接导致漏检,尤其是小三角形。
  2. 面积法的稳定性问题:面积法依赖多次除法和长度计算,会放大浮点误差,对小三角形极不友好。
  3. 冗余的平面检查:intersectRayWithPlane中计算出t后再次检查点是否在平面上完全多余——t本身就是通过平面方程推导的,这一步反而会引入额外误差。

修改后的代码实现

首先定义可调整的Epsilon常量:

const float EPSILON = 1e-6f; // 针对常规场景,小物体可改为1e-8f

改进的三角形内点判断(重心坐标法,更稳定)

bool pointInTriangle(const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, const glm::vec3& n, const glm::vec3& p)
{
    Plane plane = trianglePlane(v0,v1,v2);
    // 用Epsilon判断点是否在平面容差范围内
    if (std::abs(glm::dot(plane.normal, p) - plane.D) > EPSILON)
        return false;
    
    // 重心坐标法计算,避免面积法的除法误差
    glm::vec3 v0v1 = v1 - v0;
    glm::vec3 v0v2 = v2 - v0;
    glm::vec3 v0p = p - v0;

    float dot00 = glm::dot(v0v1, v0v1);
    float dot01 = glm::dot(v0v1, v0v2);
    float dot02 = glm::dot(v0v1, v0p);
    float dot11 = glm::dot(v0v2, v0v2);
    float dot12 = glm::dot(v0v2, v0p);

    float denom = dot00 * dot11 - dot01 * dot01;
    // 处理退化三角形(面积接近0)
    if (std::abs(denom) < EPSILON)
        return false;
    
    float invDenom = 1.0f / denom;
    float u = (dot11 * dot02 - dot01 * dot12) * invDenom;
    float v = (dot00 * dot12 - dot01 * dot02) * invDenom;

    // 用Epsilon容差判断重心坐标是否在三角形范围内
    return (u >= -EPSILON) && (v >= -EPSILON) && (u + v <= 1.0f + EPSILON);
}

改进的光线-平面相交检测

bool intersectRayWithPlane(const Plane& plane, Ray& ray)
{
    float denom = glm::dot(plane.normal, ray.direction);
    // 避免除以0(光线与平面平行或重合)
    if (std::abs(denom) < EPSILON)
        return false;
    
    float t = (plane.D - glm::dot(ray.origin, plane.normal)) / denom;
    // 用Epsilon过滤t的极小值(避免光线原点在平面上的误判),同时保留最近交点
    if (t > EPSILON && t < ray.t)
    {
        ray.t = t;
        return true;
    }
    return false;
}

额外优化建议

  • 对于极小三角形,可在计算时临时切换为double精度,减少浮点误差。
  • 动态调整Epsilon:比如在光线相交后,根据t值(交点到原点的距离)计算当前需要的容差,dynamic_epsilon = t * 1e-6f,适配不同距离的误差。

内容的提问来源于stack exchange,提问作者Cristian Cutitei

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 17:15:40