You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Möller-Trumbore算法射线追踪三角形单维度相交像素缺失问题

排查Möller-Trumbore三角形相交检测的异常渲染问题

从你描述的症状(部分三角形像素漏渲染、仅特定维度出现、与相机/射线方向强相关)以及提供的代码来看,问题大概率出在射线方向的处理浮点精度阈值或者背面/正面的判断逻辑上。下面是具体的排查步骤和修复建议:

1. 优先检查射线方向的归一化

你提到更换射线计算方式后缺失像素减少,这是一个关键线索——未归一化的射线方向会彻底打乱Möller-Trumbore算法的精度:

  • 算法中的叉积pvec = vec_cross(dir, v2)和后续的点积计算,结果会随dir的长度缩放,导致s.delta的阈值判断(0.00001)在不同尺度下失效。比如dir过长时,原本应该被判定为平行的情况没触发;过短时又会误判平行,跳过相交检测。
  • 未归一化的dir还会影响t1(射线到三角形的距离)的计算精度,导致原本应该更近的三角形被错误判定为更远,显示后方物体。

修复建议:在调用这个相交检测函数之前,强制归一化射线方向:

// 假设vec_normalize是你的向量归一化函数
dir = vec_normalize(dir);

2. 调整浮点精度阈值的合理性

你的代码中用了固定的0.00001作为阈值,但这个值可能和你的场景尺度不匹配,同时没有考虑浮点计算的误差:

  • fabs(s.delta) < 0.00001:如果场景单位很小(比如厘米级),这个阈值太大,会误判很多非平行的三角形为平行;如果场景很大,阈值又可能太小,导致精度问题。
  • s.a < 0 || s.a > 1s.b < 0 || s.a + s.b > 1:浮点计算的误差可能导致合法的a/b值略小于0或略大于1,被错误过滤,比如a-1e-8时,本该被保留却被返回了。

修复建议:定义一个可配置的全局epsilon,并且给判断加上误差容忍:

#define RAYTRACER_EPSILON 1e-6

// 替换原有的阈值判断
if (fabs(s.delta) < RAYTRACER_EPSILON) return;
// ...
if (s.a < -RAYTRACER_EPSILON || s.a > 1.0 + RAYTRACER_EPSILON) return;
// ...
if (s.b < -RAYTRACER_EPSILON || (s.a + s.b) > 1.0 + RAYTRACER_EPSILON) return;
// ...
if (s.t1 < RAYTRACER_EPSILON) return; // 避免射线原点在三角形表面的自相交

3. 检查背面剔除的逻辑一致性

你提到转动相机看三角形背面时,问题出在另一侧,这说明三角形的正面定义和射线方向的匹配度有问题

  • Möller-Trumbore算法中,s.delta的符号对应射线入射的面:如果你的三角形顶点是按逆时针顺序定义的(面向相机为正面),那么s.delta > 0表示射线从正面入射,s.delta < 0表示从背面入射。
  • 你的代码目前只跳过了平行的情况(fabs(delta) < 阈值),没有处理背面的情况。如果你的渲染管线默认只渲染正面,或者射线方向的定义和顶点顺序相反,就会导致某些角度下的相交被错误处理。

修复建议

  • 如果需要双面渲染:保持现有逻辑,但确保所有三角形的顶点顺序一致。
  • 如果只需要正面渲染:在判断平行后,加上背面过滤:
if (s.delta < RAYTRACER_EPSILON) return; // 跳过背面入射的情况

4. 验证射线原点的计算正确性

问题与相机位置相关,还要确认射线原点ori的计算是否正确:

  • 检查相机的世界坐标转换是否正确,有没有把局部坐标和世界坐标搞混。
  • 确保ori是相机的实际3D位置,而不是屏幕空间的2D坐标(很多初学者会在这里出错)。

调试技巧

如果以上修复还没解决问题,可以用以下方法定位具体错误:

  • 打印出现问题的像素对应的dirori和三角形顶点,手动计算Möller-Trumbore的每一步,看哪个判断条件错误地触发了返回。
  • 暂时注释掉所有的阈值判断(除了s.t1 < 0),看看三角形是否能完整渲染——如果能,说明是阈值的问题;如果还是有缺失,说明是射线dir/ori的计算错误。

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

火山引擎 最新活动