【发布时间】:2011-03-29 09:55:28
【问题描述】:
我正在尝试从Eric Haines' Standard Procedural Database (SPD) 渲染“mount”场景,但折射部分只是不想合作。我已经尝试了所有我能想到的来修复它。
这是我的渲染图(使用瓦特公式):
(来源: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,但这并没有解决问题。
- 法线。
我很确定我正确计算了球体的法线。我取交点,减去球心,除以半径。
【问题讨论】:
-
我不知道它是否有帮助,因为我做光线追踪已经有一年半了。是否存在射线应该相交两次以上的情况?如果没有,那么您也许可以特殊情况并避免处理重铸偏移量。
-
我想我明白你的意思:当我用射线相交球体时,我应该检查 t0 == 0。如果是,那么我应该使用 t1。那我根本不用移动交点?除非我理解错了。 “相交两次以上”和“重铸偏移”是什么意思?
-
图片不可用 :'(
标签: graphics raytracing