2

我正在尝试从Eric Haines 的标准程序数据库 (SPD)中渲染“安装”场景,但折射部分只是不想合作。我已经尝试了我能想到的一切来修复它。

这是我的渲染(使用瓦特公式):

我的渲染。
(来源:philosoraptor.co.za

这是我使用“正常”公式的渲染:

正常公式。
(来源:philosoraptor.co.za

这是正确的渲染:

正确渲染。
(来源:philosoraptor.co.za

如您所见,只有几个错误,主要是在球体的两极附近。这让我认为折射或一些精度误差是罪魁祸首。

请注意,场景中实际上有 4 个球体,它们的 NFF 定义 ( s x_coord y_coord z_coord radius) 是:

s -0.8 0.8 1.20821 0.17
s -0.661196 0.661196 0.930598 0.17
s -0.749194 0.98961 0.930598 0.17
s -0.98961 0.749194 0.930598 0.17

也就是说,在前景中更明显的三个后面有第四个球体。从这三个球体之间留下的空隙中可以看出。

这是仅第四个球体的图片:

“第四”球体。
(来源:philosoraptor.co.za

这是仅第一个球体的图片:

“第一”领域。
(来源:philosoraptor.co.za

您会注意到我的版本和正确版本中存在的许多奇怪之处都丢失了。我们可以得出结论,这些影响是球体之间相互作用的结果,问题是哪些相互作用?

我究竟做错了什么?以下是我已经考虑过的一些潜在错误:

  • 折射矢量公式。

据我所知,这是正确的。这是几个网站使用的相同公式,我亲自验证了推导。这是我的计算方法:

double sinI2 = eta * eta * (1.0f - cosI * cosI);

Vector transmit = (v * eta) + (n * (eta * cosI - sqrt(1.0f - sinI2)));

transmit = transmit.normalise();

我在 Alan Watt 的第三版 3D Computer Graphics 中找到了一个替代公式。它更接近正确的图像:

double etaSq = eta * eta;
double sinI2 = etaSq * (1.0f - cosI * cosI);
Vector transmit = (v * eta) + (n * (eta * cosI - (sqrt(1.0f - sinI2) / etaSq)));
transmit = transmit.normalise();

唯一的区别是我最后除以 eta^2。

  • 全内反射。

我对此进行了测试,在我的其余交集代码之前使用以下条件:

if (sinI2 <= 1)
  • eta的计算。

我使用类似堆栈的方法来解决这个问题:

        /* Entering object. */
        if (r.normal.dot(r.dir) < 0)
        {
           double eta1 = r.iorStack.back();
           double eta2 = m.ior;
           eta = eta1 / eta2;

           r.iorStack.push_back(eta2);
        }
        /* Exiting object. */
        else
        {
           double eta1 = r.iorStack.back();
           r.iorStack.pop_back();
           double eta2 = r.iorStack.back();

           eta = eta1 / eta2;
        }

如您所见,这将包含此射线的先前对象存储在堆栈中。退出代码时,将当前 IOR 从堆栈中弹出并使用它以及它下面的 IOR 来计算 eta。据我所知,这是最正确的方法。

这适用于嵌套传输对象。但是,它会因相交传输对象而崩溃。这里的问题是您需要为交叉点独立定义 IOR,而 NFF 文件格式不这样做。那时还不清楚什么是“正确”的行动方案。

  • 移动新射线的原点。

新光线的原点必须沿着传输路径稍微移动,这样它就不会与前一条光线在同一点相交。

p = r.intersection + transmit * 0.0001f;

p += transmit * 0.01f;

我尝试使这个值更小(0.001f)和(0.0001f),但这会使球体看起来很实心。我猜这些值不会使光线远离前一个交点。

编辑:这里的问题是反射代码在做同样的事情。因此,当一个物体既具有反射性又具有折射性时,光线的起源就会完全出现在错误的位置。

  • 光线反弹量。

我人为地将光线反弹的数量限制为 4。我测试了将此限制提高到 10,但这并没有解决问题。

  • 法线。

我很确定我正在正确计算球体的法线。我取交点,减去球体的中心并除以半径。

4

2 回答 2

3

只是基于做图像差异的猜测(并且没有阅读你的问题的其余部分)。在我看来,这个问题是球体背面的折射。你可能是:

  • 倒退:例如倒转(或不倒转)折射率。
  • 完全错过了吗?

检查这一点的一种方法是通过一个几乎面向相机的立方体来观察底座。如果折射是正确的,图片应该稍微偏移,否则保持不变。如果它不正确,那么图片会显得略微倾斜。

于 2010-08-23T04:18:22.933 回答
0

因此,经过一年多的时间,我终于弄清楚了这里发生了什么。头脑清醒等等。我完全偏离了公式。我现在使用的是 Heckbert 的公式,我确信它是正确的,因为我自己使用几何和离散数学证明了它。

这是正确的向量计算:

double c1 = v.dot(n) * -1;
double c1Sq = pow(c1, 2);

/* Heckbert's formula requires eta to be eta2 / eta1, so I have to flip it here. */
eta = 1 / eta;
double etaSq = pow(eta, 2);

if (etaSq + c1Sq >= 1)
{
   Vector transmit = (v / eta) + (n / eta) * (c1 - sqrt(etaSq - 1 + c1Sq));
   transmit = transmit.normalise();
...
}
else
{
   /* Total internal reflection. */
}

在上面的代码中,eta 是 eta1(光线来自的表面的 IOR)超过 eta2(目标表面的 IOR),v 是入射光线,n 是法线。

还有一个问题,使问题更加混乱。退出对象时我不得不翻转法线(这很明显 - 我错过了它,因为其他错误掩盖了它)。

最后,我的视线算法(确定一个表面是否被点光源照亮)没有正确地穿过透明表面。

所以现在我的图像正确排列:)

于 2012-02-03T16:16:36.333 回答