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

两种Eigen折射实现结果不一致且不匹配Snell定律问题排查

问题核心原因
  • 两个参考实现都内置了自动判断入射方向并交换折射率的逻辑,这个逻辑的前提是默认etai为空气折射率、etat为介质折射率,与你的需求完全冲突:你已经明确传入了入射侧折射率etai和出射侧折射率etat,不需要代码自动交换二者,这是结果不符合Snell定律的核心原因。
  • 第一个实现refract的额外问题:当入射光线与法线的点积为正时,它会强制交换etai和etat、翻转法线,直接打乱了你传入的正确折射率参数,相当于把折射率比值算成了原理论值的倒数,正好对应你算出的2.1、2.2的错误比值。
  • 第二个实现refract2的额外问题:没有处理法线方向与入射方向不匹配的场景,默认法线一定指向入射侧,一旦法线方向翻转,计算出的入射角度余弦会直接为负,导致折射向量计算完全错误。
  • 两个实现结果存在细微差异的原因:第一个实现的返回值没有做归一化处理,浮点数计算的累计误差导致结果和第二个加了归一化的版本有偏差。
修正后实现(支持任意法向量、任意折射率组合)

代码约定:入参etai为入射侧折射率,etat为出射侧折射率,无需提前做方向判断,全反射场景由外部处理:

Eigen::Vector3d correct_refract(
  const Eigen::Vector3d &incVector, 
  Eigen::Vector3d surfaceNormal, 
  double etai, 
  double etat )
{
  Eigen::Vector3d incident = incVector.normalized();
  double cos_theta_i = std::clamp(-incident.dot(surfaceNormal), -1.0, 1.0);

  // 仅调整法线方向确保指向入射侧,不交换传入的折射率参数
  if (cos_theta_i < 0) {
    surfaceNormal = -surfaceNormal;
    cos_theta_i = -cos_theta_i;
  }

  double eta = etai / etat;
  double sin2_theta_i = 1.0 - cos_theta_i * cos_theta_i;
  double cos_theta_t = std::sqrt(1.0 - eta * eta * sin2_theta_i);

  return (eta * incident + (eta * cos_theta_i - cos_theta_t) * surfaceNormal).normalized();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 09:54:01