如何正确处理光线追踪中的折射

Tel*_*kis 22 c++ raytracing

我目前正在研究光线跟踪器,只是为了好玩而且我在折射处理方面遇到了麻烦.

整个光线跟踪器的代码源可以在Github上找到.

这是渲染的图像:

折射bug

右侧球体的折射率为1.5(玻璃).

在折射的基础上,我想处理一个"透明度"系数,其定义如下:

  • 0 - >对象是100%不透明的
  • 1 - >对象是100%透明(没有原始对象颜色的痕迹)

该球体的透明度为1.

这是处理折射部分的代码.它可以在github上找到.

Color handleTransparency(const Scene& scene,
                         const Ray& ray,
                         const IntersectionData& data,
                         uint8 depth)
{
  Ray refracted(RayType::Transparency, data.point, ray.getDirection());
  Float_t eta = data.material->getRefraction();

  if (eta != 1 && eta > Globals::Epsilon)
    refracted.setDirection(Tools::Refract(ray.getDirection(), data.normal, eta));
  refracted.setOrigin(data.point + Globals::Epsilon * refracted.getDirection());
  return inter(scene, refracted, depth + 1);
}

// http://graphics.stanford.edu/courses/cs148-10-summer/docs/2006--degreve--reflection_refraction.pdf
Float_t getFresnelReflectance(const IntersectionData& data, const Ray& ray)
{
  Float_t n = data.material->getRefraction();
  Float_t cosI = -Tools::DotProduct(ray.getDirection(), data.normal);
  Float_t sin2T = n * n * (Float_t(1.0) - cosI * cosI);

  if (sin2T > 1.0)
    return 1.0;

  using std::sqrt;
  Float_t cosT = sqrt(1.0 - sin2T);
  Float_t rPer = (n * cosI - cosT) / (n * cosI + cosT);
  Float_t rPar = (cosI - n * cosT) / (cosI + n * cosT);
  return (rPer * rPer + rPar * rPar) / Float_t(2.0);
}

Color handleReflectionAndRefraction(const Scene& scene,
                                    const Ray& ray,
                                    const IntersectionData& data,
                                    uint8 depth)
{
  bool hasReflexion = data.material->getReflexion() > Globals::Epsilon;
  bool hasTransparency = data.material->getTransparency() > Globals::Epsilon;

  if (!(hasReflexion || hasTransparency) || depth >= MAX_DEPTH)
    return 0;

  Float_t reflectance = data.material->getReflexion();
  Float_t transmittance = data.material->getTransparency();

  Color reflexion;
  Color transparency;

  if (hasReflexion && hasTransparency)
  {
    reflectance = getFresnelReflectance(data, ray);
    transmittance = 1.0 - reflectance;
  }

  if (hasReflexion)
    reflexion = handleReflection(scene, ray, data, depth) * reflectance;

  if (hasTransparency)
    transparency = handleTransparency(scene, ray, data, depth) * transmittance;

  return reflexion + transparency;
}
Run Code Online (Sandbox Code Playgroud)

Tools::Refract只是在glm::refract内部打电话.(如果我愿意,我可以轻松改变)

我不处理的概念n1n2:n2被认为永远是1的空气.

我有什么明显的东西吗?


编辑

添加了一种方法来了解光线是否在一个物体内(如果是这样则否定正常)我有这个:

折射问题

在四处寻找帮助时,我偶然发现了这篇文章,但我认为答案没有回答.通过阅读它,我不明白我应该做什么.


编辑2

我已经尝试了很多东西,目前我正处于这个阶段:

当前

它更好,但我仍然不确定它是否正确.我正在使用这个图像作为灵感:

例

但是这个使用两个折射率(更接近现实),而我想简化并始终将空气视为第二个(进出)材料.

我在代码中实质上改变的是:

inline Vec_t Refract(Vec_t v, const IntersectionData& data, Float_t eta)
{
  Float_t n = eta;

  if (data.isInside)
    n = 1.0 / n;
  double cosI = Tools::DotProduct(v, data.normal);

  return v * n - data.normal * (-cosI + n * cosI);
}
Run Code Online (Sandbox Code Playgroud)

以下是同一球体的另一种观点:

球

Mat*_*tso 6

编辑:我认为以前的版本并不完全正确所以我编辑答案.

在阅读了所有评论,问题的新版本并自己做了一些实验后,我制作了以下版本的refract例程:

float3 refract(float3 i, float3 n, float eta)
{
    eta = 2.0f - eta;
    float cosi = dot(n, i);
    float3 o = (i * eta - n * (-cosi + eta * cosi));
    return o;
}
Run Code Online (Sandbox Code Playgroud)

这次调用它不需要任何额外的操作:

float3 refr = refract(rayDirection, normal, refrIdx);
Run Code Online (Sandbox Code Playgroud)

我唯一不确定的是在进行内部光线交叉时反射折射率.在我的测试中,无论我是否反转索引,所产生的图像都没有太大差别.

下面是一些不同指数的图像:

在此输入图像描述 在此输入图像描述

有关更多图片,请参阅链接,因为该网站不允许我在此处添加更多图片.


sta*_*e27 5

我是作为物理学家而不是程序员来回答这个问题的,因为没有时间阅读所有代码,所以不会给出代码来进行修复,只是一般的想法。

根据你上面所说的,当 n_object 小于 n_air 时,黑色环是用来表示的。这通常仅在您在物体内部时才适用,例如您是否在水中或类似物体中,但材料已被构造为具有类似这样的奇怪属性,并且应该得到支持。

在这种情况下,有些光线不能被衍射,因为衍射公式将折射光线放在材料之间界面的同一侧,这显然没有衍射意义。在这种情况下,表面会表现得像一个反射表面。这种情况通常被称为全内反射。

如果完全精确,那么几乎所有折射物体也将部分反射,反射或透射(因此折射)的光的分数由菲涅耳方程给出。对于这种情况,如果角度太远,则将其视为反射性仍然是一个很好的近似值,否则会透射(因此是折射)。

也有一些情况,如果反射是不可能的(因为它在这些方向上是暗的),但可以看到透射的光,则可以看到这种黑环效果。这可以通过说取一管紧紧贴在物体边缘并直接指向外的卡片管来完成,并且只在管内发光而不在外面发光。