我没有看到另一篇与我的问题类似的帖子,所以希望这不是多余的。
我一直在读一本关于计算机图形学基础的书(第三版),我一直在实施基于我从中学到的原理的基本光线追踪程序。我在实现平行和透视投影时没有遇到什么困难,但在进入Lambertian和Blinn-Phong Shading之后,我遇到了一个障碍,我自己无法搞清楚。
我相信我的问题与我如何计算光线球体交叉点以及相机/光线的矢量有关。当我运行简单的透视投影而没有阴影时,我附上了一张输出的图片。
但是,当我尝试使用Lambertian着色的相同场景时,球体会消失。
在尝试自己调试时,我注意到如果我否定计算为命中点的x,y,z坐标,则球体会再次出现。而且我相信光线来自我期望的相反方向。
我通过将投影方向向量和 t 值的乘积(由光线球面交点公式计算)添加到原点(我的“相机”所在的位置,计算生命点), 0,0,0)或只是 e + td 。
从命中点到光源的矢量 l ,我设置为灯光的位置减去击中点的位置(所以击中点的坐标减去灯光的坐标)。
v ,从命中点到相机的矢量,我只是通过简单地否定投影视图矢量来获得;
表面正常我通过命中点减去球体的位置。
我认为所有这些都是正确的。然而,当逐步通过计算表面法线的部分时,我注意到我认为奇怪的东西。当从球体的位置减去击中点的位置以从球体的中心到击中点获得向量时,我相信我应该期望得到一个向量,其中所有值都在范围内(-r,r);但事情并没有发生。
这是逐步完成我的代码的一个例子:
计算的生命值:( - 0.9971,0.1255,-7.8284)
球体中心:(0,0,8)(半径为1)
减去后,我得到一个矢量,其中z值为-15.8284。这对我来说似乎不对;但我不知道是什么导致了它。 -15.8284的z值是否意味着球面中心和击中位置在z平面上彼此相距约16个单位?显然,这两个数字在绝对值方面彼此相差1,这就是我认为我的问题与此有关的原因。
这是主要的光线追踪循环:
auto origin = Position3f(0, 0, 0);
for (int i = 0; i < numPixX; i++)
{
for (int j = 0; j < numPixY; j++)
{
for (SceneSurface* object : objects)
{
float imgPlane_u = left + (right - left) * (i + 0.5f) / numPixX;
float imgPlane_v = bottom + (top - bottom) * (j + 0.5f) / numPixY;
Vector3f direction = (w.negated() * focal_length) + (u * imgPlane_u) + (v * imgPlane_v);
Ray viewingRay(origin, eye, direction);
RayTestResult testResult = object->TestViewRay(viewingRay);
if (testResult.m_bRayHit)
{
Position3f hitPoint = (origin + (direction) * testResult.m_fDist);//.negated();
Vector3f light_direction = (light - hitPoint).toVector().normalized();
Vector3f view_direction = direction.negated().normalized();
Vector3f surface_normal = object->GetNormalAt(hitPoint);
image[j][i] = object->color * intensity * fmax(0, surface_normal * light_direction);
}
}
}
}
GetNormalAt简单地说:
Vector3f Sphere::GetNormalAt(Position3f &surface)
{
return (surface - position).toVector().normalized();
}
我的球体位于(0,0,8)和(-1.5,-1,6),rad为1.0f。 我的光在(-3,-3,0),强度为1.0f;
我忽略了 t 不大于0的任何交集,所以我不相信这会导致这个问题。
我认为在将位置和矢量保持在相同的坐标系中时,我可能会犯一些错误(相同的转换?),但我仍然在学习,并且当然不能理解这一点。如果视图方向始终位于 - w 方向,为什么我们将场景对象放在正 w 方向?
非常感谢任何帮助或智慧。到目前为止,我正在教给自己这一切,我很高兴我接受了多少,但我的直觉告诉我这是一个相对简单的错误。
以防它有用,这里是TestViewRay函数:
RayTestResult Sphere::TestViewRay(Ray &viewRay)
{
RayTestResult result;
result.m_bRayHit = false;
Position3f &c = position;
float r = radius;
Vector3f &d = viewRay.getDirection();
Position3f &e = viewRay.getPosition();
float part = d*(e - c);
Position3f part2 = (e - c);
float part3 = d * d;
float discriminant = ((part*part) - (part3)*((part2*part2) - (r * r)));
if (discriminant > 0)
{
float t_add = ((d) * (part2)+sqrt(discriminant)) / (part3);
float t_sub = ((d) * (part2)-sqrt(discriminant)) / (part3);
float t = fmin(t_add, t_sub);
if (t > 0)
{
result.m_iNumberOfSolutions = 2;
result.m_bRayHit = true;
result.m_fDist = t;
}
}
else if (discriminant == 0)
{
float t_add = ((d)* (part2)+sqrt(discriminant)) / (part3);
float t_sub = ((d)* (part2)-sqrt(discriminant)) / (part3);
float t = fmin(t_add, t_sub);
if (t > 0)
{
result.m_iNumberOfSolutions = 1;
result.m_bRayHit = true;
result.m_fDist = t;
}
}
return result;
}
编辑:
我很高兴地报告我发现了我的问题。
与我的姐姐坐下来看看这个我注意到在我的射线球命中检测中我有这个:
float t_add = ((d) * (part2)+sqrt(discriminant)) / (part3);
哪个不对。 d 应该是否定的。它应该是:
float t_add = ((neg_d * (e_min_c)) + sqrt(discriminant)) / (part2);
(我重命名了几个变量)以前我有一个零矢量,所以我可以表达 - d 为(zero_vector - d )我已经删除了因为我实现了一个成员函数来否定任何给定的向量;但是我忘了回去在 d 上打电话。修好并将我的球体移动到负z平面后,我的Lambertian和Blinn-Phong着色实现正常工作。