我正试图从Eric Haines' Standard Procedural Database (SPD)渲染“装载”场景,但折射部分只是不想合作。我已经尝试了一切我能想到的解决方法。
这是我的渲染(用Watt的公式):
My render. http://www.philosoraptor.co.za/programming/SPD/mount_mine.png
这是我使用“普通”公式的渲染:
Normal formula. http://www.philosoraptor.co.za/programming/SPD/mount_mine_normal.png
这个是正确的渲染:
Correct render. http://www.philosoraptor.co.za/programming/SPD/mount_haines.png
正如你所看到的,只有几个错误,主要是围绕球体的两极。这让我觉得折射或一些精确错误是罪魁祸首。
请注意,场景中实际上有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
也就是说,前景中更明显的三个背后有第四个球体。可以在这三个球体之间留下的空隙中看到。
以下是第四个球体的图片:
'Fourth' sphere. http://www.philosoraptor.co.za/programming/SPD/mount_sphere_2.png
以下是第一个球体的图片:
'First' sphere. http://www.philosoraptor.co.za/programming/SPD/mount_sphere_1.png
您会注意到我的版本和正确版本中存在的许多奇怪内容都缺失了。我们可以得出结论,这些影响是球体之间相互作用的结果,问题是哪些相互作用?
我做错了什么?以下是我已经考虑过的一些潜在错误:
据我所知,这是正确的。这是几个网站使用的相同公式,我个人验证了推导。这是我计算它的方式:
<击> 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计算机图形学中找到了另一个公式。它更接近正确的图像:
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)
我使用类似堆栈的方法来解决这个问题:
/* 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,但这并没有解决问题。
我很确定我正在正确计算球体的法线。我取交点,减去球体的中心并除以半径。
答案 0 :(得分:3)
只是基于做图像差异的猜测(并且没有阅读你的问题的其余部分)。这个问题在我看来是球体背面的折射。你可能是:
检查这一点的一种方法是通过几乎面向相机的立方体来查看底座。如果折射是正确的,图片应略微偏移,否则不会改变。如果不对,那么图片看起来会略微倾斜。
答案 1 :(得分: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是法线。
还有另一个问题,它使问题更加困惑。退出对象时我不得不翻转 normal (很明显 - 我错过了它,因为其他错误掩盖了它)。
最后,我的视线算法(确定表面是否被点光源照射)未正确穿过透明表面。
所以现在我的图像正确排列了:)