我试图在没有对圆环进行三角测量的情况下对光环进行光线跟踪,并且仅通过相交光线和圆环分析方程。我用以下代码做到了:
void circularTorusIntersectFunc(const CircularTorus* circularToruses, RTCRay& ray, size_t item)
{
const CircularTorus& torus = circularToruses[item];
Vec3fa O = ray.org /*- sphere.p*/;
Vec3fa Dir = ray.dir;
O.w = 1.0f;
Dir.w = 0.0f;
O = torus.inv_transform.mult(O);
Dir = torus.inv_transform.mult(Dir);
// r1: cross section of torus
// r2: the ring's radius
// _____ ____
// / r1 \------->r2<--------/ \
// \_____/ \____/
float r2 = sqr(torus.r1);
float R2 = sqr(torus.r2);
double a4 = sqr(dot(Dir, Dir));
double a3 = 4 * dot(Dir, Dir) * dot(O, Dir);
double a2 = 4 * sqr(dot(O, Dir)) + 2 * dot(Dir, Dir) * (dot(O, O) - r2 - R2) + 4 * R2 * sqr(Dir.z);
double a1 = 4 * dot(O, Dir) * (dot(O, O) - r2 - R2) + 8 * R2 * O.z * Dir.z;
double a0 = sqr(dot(O, O) - r2 - R2) + 4 * R2 * sqr(O.z) - 4 * R2 * r2;
a3 /= a4; a2 /= a4; a1 /= a4; a0 /= a4;
double roots[4];
int n_real_roots;
n_real_roots = SolveP4(roots, a3, a2, a1, a0);
if (n_real_roots == 0) return;
Vec3fa intersect_point;
for (int i = 0; i < n_real_roots; i++)
{
float root = static_cast<float>(roots[i]);
intersect_point = root * Dir + O;
if ((ray.tnear <= root) && (root <= ray.tfar)) {
ray.u = 0.0f;
ray.v = 0.0f;
ray.tfar = root;
ray.geomID = torus.geomID;
ray.primID = item;
Vec3fa normal(
4.0 * intersect_point.x * (sqr(intersect_point.x) + sqr(intersect_point.y) + sqr(intersect_point.z) - r2 - R2),
4.0 * intersect_point.y * (sqr(intersect_point.x) + sqr(intersect_point.y) + sqr(intersect_point.z) - r2 - R2),
4.0 * intersect_point.z * (sqr(intersect_point.x) + sqr(intersect_point.y) + sqr(intersect_point.z) - r2 - R2) + 8 * R2*intersect_point.z,
0.0f
);
ray.Ng = normalize(torus.transform.mult(normal));
}
}
}
解决SolveP4
函数等式的代码取自Solution of cubic and quatric functions。
问题是当我们仔细观察圆环时,它的工作原理非常好,如下:
但是当我缩小相机时,所以相机正在看着远离它的圆环,它突然变得如此嘈杂,而且它的形状还没有很好地识别出来。我试图每像素使用1个以上的样本,但我仍然遇到同样的问题。它如下:
我似乎面临一个数字问题,但我不知道如何解决它。任何人都可以帮助我吗?
另外,最好提一下,我正在使用英特尔的Embree Lib对光环进行测试。
更新(单色):
答案 0 :(得分:1)
我认为很多问题是使用单精度浮点而不是双精度。
定义两个函数
double dsqr(double x) { return x*x; }
double ddot(const Vec3fa &a,Vec3fa &b) {
double x1 = a.x, y1 = a.y, z1 = a.z;
double x2 = b.x, y2 = b.y, z2 = b.z;
return x1*x2 + y1*y2 + z1*z2;
}
找到正方形和点积但使用双精度。更改r2 R2 a4 a3 a2 a1和a0的计算以使用这些
double r2 = dsqr(torus.r1);
double R2 = dsqr(torus.r2);
double a4 = dsqr(ddot(Dir, Dir));
double a3 = 4 * ddot(Dir, Dir) * ddot(O, Dir);
double a2 = 4 * dsqr(ddot(O, Dir)) + 2 * ddot(Dir, Dir) * (ddot(O, O) - r2 - R2)
+ 4 * R2 * dsqr(Dir.z);
double a1 = 4 * ddot(O, Dir) * (ddot(O, O) - r2 - R2) + 8 * R2 * O.z * Dir.z;
double a0 = dsqr(ddot(O, O) - r2 - R2) + 4 * R2 * dsqr(O.z) - 4 * R2 * r2;
所有剩余的代码都是一样的。在我的测试中,这使得模糊的图像看起来非常清晰。
答案 1 :(得分:1)
当我写我的光线跟踪器时(顺便说一下,我正在使用一本名为&#34的好书;来自Ground Up&#34的Ray Tracing;)我也遇到了Toruses的一些问题。
那时我用Graphics Gems github repo的算法来计算光线环面交叉点。解决方案只是使用较小的环节,例如当我的圆环外半径大于100.0
并且光线从(0,0,0)
开始时,我的光线跟踪器经历了大量的数值误差。 使用像1.0
这样较小的圆环半径解决了我的问题。
这些数值误差的来源在于建立环面多项式的系数,其中大小为100.0
的环面在计算期间生成的某些系数可能超过1e20
。使用double
精度可以保证大约15位有效数字,这会造成很大的精度损失。
答案 2 :(得分:1)
PovRay为此提供了有趣而有效的解决方案。 只要将射线的原点移到非常接近圆环的位置,系数对于多项式解算器将具有很好的值。 接近程度:原点应在半径Mayor + 2 * minor的球面上。 ...并按照@csharpfolk
的建议将市长半径保持为一