光线追踪三角形
Ray-tracing triangles
我正在 java 中编写光线追踪器,我能够追踪球体,但我认为我追踪三角形的方式有问题。
这是我理解的基本算法:
- 首先确定射线是否与三角形所在的平面相交。
- 剪切所有点,使它们与三角形位于同一平面上(例如
xy
平面)。
- 确定潜在交点是落在三角形内部还是外部,基于您在沿新平面以任意方向发出光线时穿过的多边形边的数量。
现在,这是我的实现(特别是第一点):
public Vector getIntersectionVector(Ray ray)
{
Vector planeIntersectionVector = getPlaneIntersectionVector(ray, getPlaneNormal());
if (planeIntersectionVector != null)
{
if (isIntersectionVectorInsideTriangle(planeIntersectionVector))
{
return planeIntersectionVector;
}
else
{
return null;
}
}
else
{
return null;
}
}
其中 getPlaceIntersectionVector()
是:
private Vector getPlaneIntersectionVector(Ray ray, Vector planeNormal)
{
double vd = planeNormal.dotProduct(ray.getDirection());
//(p_n \dot r_d) == 0, parallel. (p_n \dot r_d) > 0 Plane normal pointing away from ray.
if (vd >= 0)
{
return null;
}
double distance = planeNormal.distance(0d, 0d, 0d);
double vo = -(planeNormal.dotProduct(ray.getOrigin()) + distance);
double intersectionDistance = vo / vd;
//intersectionDistance <= 0 means the "intersection" is behind the ray, so not a real intersection
return (intersectionDistance <= 0) ? null : ray.getLocation(intersectionDistance);
}
这基本上是在模仿这个:
还有这个:
其中t
为点沿射线命中的距离,ro为射线原点,rd是光线的方向,pn是指triangle/plane的平面法线,d
是三角形到平面的距离在原点 (0,0,0)
我做错了吗?当我从图像 (0,0)
中的第一个像素发出光线时,我看到 intersectionDistance
(或 t
)几乎是 1100
,这在直觉上似乎是错误的我。我认为交点会更近。
相关数据如下:
射线原点 (0,0,1)
,射线方向大致为 (0.000917, -0.4689, -0.8833)
.
三角形的顶点为(-0.2, 0.1, 0.1)
、(-0.2, -0.5, 0.2)
、(-0.2, 0.1, -0.3)
,这使得平面正常(-1, 0, 0)
。
根据我的代码,射线与平面相交 1090
距离,正如我之前提到的,这对我来说似乎是错误的。场景在每个方向上都只有-1.0到1.0,这意味着路口在距离上非常非常远。
我做的平面相交错了吗?
如果您需要更多信息,请告诉我在哪里澄清要点。
问题出在这一行:
double distance = planeNormal.distance(0d, 0d, 0d);
平面由法线和平面到原点的距离定义。向量到原点的距离就是向量的长度,所以你计算的只是平面法线的长度(如果已经归一化,它总是 1.0)。
平面到原点的距离是一个额外的信息,需要传递给你的函数,你不能只从法线计算它,因为它独立于法线(你可以有很多法线指向同一方向但距离原点不同的平面)。
所以像这样定义你的函数:
private Vector getPlaneIntersectionVector(Ray ray, Vector planeNormal, double planeDistance)
并这样称呼它:
Vector planeIntersectionVector = getPlaneIntersectionVector(ray, getPlaneNormal(), getPlaneDistance());
然后你可以这样计算vo
:
double vo = -(planeNormal.dotProduct(ray.getOrigin()) + planeDistance);
方法略有不同:
让三角形的顶点是V0, V1, V2
边向量是
A = V1-V0
B = V2 - V0
Ray 有参数方程(如您所写)
P = R0 + t * Rd
从另一边看,交点在三角形平面内有参数坐标u,v
P = V0 + u * A + v * B
所以你可以写出x、y、z坐标的三个线性方程组并求解t, u, v
。如果行列式 ius 非零(射线不平行于平面),并且 t >=0
和 u, v, u+v
位于范围 0..1
内,则 P 在三角形内部。
R0.X + t * Rd.X = V0.X + u * A.X + v * B.X
R0.Y + t * Rd.Y = V0.Y + u * A.Y + v * B.Y
R0.Z + t * Rd.Z = V0.Z + u * A.Z + v * B.Z
我正在 java 中编写光线追踪器,我能够追踪球体,但我认为我追踪三角形的方式有问题。
这是我理解的基本算法:
- 首先确定射线是否与三角形所在的平面相交。
- 剪切所有点,使它们与三角形位于同一平面上(例如
xy
平面)。 - 确定潜在交点是落在三角形内部还是外部,基于您在沿新平面以任意方向发出光线时穿过的多边形边的数量。
现在,这是我的实现(特别是第一点):
public Vector getIntersectionVector(Ray ray)
{
Vector planeIntersectionVector = getPlaneIntersectionVector(ray, getPlaneNormal());
if (planeIntersectionVector != null)
{
if (isIntersectionVectorInsideTriangle(planeIntersectionVector))
{
return planeIntersectionVector;
}
else
{
return null;
}
}
else
{
return null;
}
}
其中 getPlaceIntersectionVector()
是:
private Vector getPlaneIntersectionVector(Ray ray, Vector planeNormal)
{
double vd = planeNormal.dotProduct(ray.getDirection());
//(p_n \dot r_d) == 0, parallel. (p_n \dot r_d) > 0 Plane normal pointing away from ray.
if (vd >= 0)
{
return null;
}
double distance = planeNormal.distance(0d, 0d, 0d);
double vo = -(planeNormal.dotProduct(ray.getOrigin()) + distance);
double intersectionDistance = vo / vd;
//intersectionDistance <= 0 means the "intersection" is behind the ray, so not a real intersection
return (intersectionDistance <= 0) ? null : ray.getLocation(intersectionDistance);
}
这基本上是在模仿这个:
还有这个:
其中t
为点沿射线命中的距离,ro为射线原点,rd是光线的方向,pn是指triangle/plane的平面法线,d
是三角形到平面的距离在原点 (0,0,0)
我做错了吗?当我从图像 (0,0)
中的第一个像素发出光线时,我看到 intersectionDistance
(或 t
)几乎是 1100
,这在直觉上似乎是错误的我。我认为交点会更近。
相关数据如下:
射线原点 (0,0,1)
,射线方向大致为 (0.000917, -0.4689, -0.8833)
.
三角形的顶点为(-0.2, 0.1, 0.1)
、(-0.2, -0.5, 0.2)
、(-0.2, 0.1, -0.3)
,这使得平面正常(-1, 0, 0)
。
根据我的代码,射线与平面相交 1090
距离,正如我之前提到的,这对我来说似乎是错误的。场景在每个方向上都只有-1.0到1.0,这意味着路口在距离上非常非常远。
我做的平面相交错了吗?
如果您需要更多信息,请告诉我在哪里澄清要点。
问题出在这一行:
double distance = planeNormal.distance(0d, 0d, 0d);
平面由法线和平面到原点的距离定义。向量到原点的距离就是向量的长度,所以你计算的只是平面法线的长度(如果已经归一化,它总是 1.0)。
平面到原点的距离是一个额外的信息,需要传递给你的函数,你不能只从法线计算它,因为它独立于法线(你可以有很多法线指向同一方向但距离原点不同的平面)。
所以像这样定义你的函数:
private Vector getPlaneIntersectionVector(Ray ray, Vector planeNormal, double planeDistance)
并这样称呼它:
Vector planeIntersectionVector = getPlaneIntersectionVector(ray, getPlaneNormal(), getPlaneDistance());
然后你可以这样计算vo
:
double vo = -(planeNormal.dotProduct(ray.getOrigin()) + planeDistance);
方法略有不同:
让三角形的顶点是V0, V1, V2
边向量是
A = V1-V0
B = V2 - V0
Ray 有参数方程(如您所写)
P = R0 + t * Rd
从另一边看,交点在三角形平面内有参数坐标u,v
P = V0 + u * A + v * B
所以你可以写出x、y、z坐标的三个线性方程组并求解t, u, v
。如果行列式 ius 非零(射线不平行于平面),并且 t >=0
和 u, v, u+v
位于范围 0..1
内,则 P 在三角形内部。
R0.X + t * Rd.X = V0.X + u * A.X + v * B.X
R0.Y + t * Rd.Y = V0.Y + u * A.Y + v * B.Y
R0.Z + t * Rd.Z = V0.Z + u * A.Z + v * B.Z