我正在编写一个基本的Ray-tracer,以便更好地理解整个事物。我遇到了一个问题,这个问题一直让我退缩一段时间,一个球体的漫反射阴影。我使用了以下来源的公式来计算球体交点和漫反射着色。
http://www.ccs.neu.edu/home/fell/CSU540/programs/RayTracingFormulas.htm
我的代码计算着色(在链接上尝试复制源代码)如前所示。在大多数情况下,计算在某些球体上看起来是正确的,但有时,取决于灯光的位置取决于球体阴影的正确/破坏程度。
TVector intersect (ray.getRayOrigin().getVectX() + t * (ray.getRayDirection().getVectX() - ray.getRayOrigin().getVectX()),
ray.getRayOrigin().getVectY() + t * (ray.getRayDirection().getVectY() - ray.getRayOrigin().getVectY()),
ray.getRayOrigin().getVectZ() + t * (ray.getRayDirection().getVectZ() - ray.getRayOrigin().getVectZ()));
//Calculate the normal at the intersect point
TVector NormalIntersect (intersect.getVectX() - (position.getVectX()/r),
intersect.getVectY() - (position.getVectY()/r),
intersect.getVectZ() - (position.getVectZ()/r));
NormalIntersect = NormalIntersect.normalize();
//Find unit vector from intersect(x,y,z) to the light(x,y,z)
TVector L1 (light.GetPosition().getVectX() - intersect.getVectX(),
light.GetPosition().getVectY() - intersect.getVectY(),
light.GetPosition().getVectZ() - intersect.getVectZ());
L1 = L1.normalize();
double Magnitude = L1.magnitude();
TVector UnitVector(L1.getVectX() / Magnitude,
L1.getVectY() / Magnitude,
L1.getVectZ() / Magnitude);
//Normalized or not, the result is the same
UnitVector = UnitVector.normalize();
float Factor = (NormalIntersect.dotProduct(UnitVector));
float kd = 0.9; //diffuse-coefficient
float ka = 0.1; //Ambient-coefficient
Color pixelFinalColor(kd * Factor * (color.getcolorRed()) + (ka * color.getcolorRed()) ,
kd * Factor * (color.getcolorGreen()) + (ka * color.getcolorGreen()) ,
kd * Factor * (color.getcolorBlue()) + (ka * color.getcolorBlue()) ,1);
从图中可以看出,某些球体看起来是正确的阴影,而其他球体则完全被打破。起初我认为问题可能在于UnitVector计算,但是当我查看它时,我无法找到问题。任何人都可以看到问题的原因吗?
注意:我正在使用OpenGl来渲染我的场景。
更新: 我仍然遇到一些问题,但是我认为它们大部分已经得到了解决,感谢你们的帮助,以及对我如何计算单位向量的一些改动。更新如下所示。非常感谢所有给出答案的人。
TVector UnitVector (light.GetPosition().getVectX() - intersect.getVectX(),
light.GetPosition().getVectY() - intersect.getVectY(),
light.GetPosition().getVectZ() - intersect.getVectZ());
UnitVector = UnitVector.normalize();
float Factor = NormalIntersect.dotProduct(UnitVector);
//Set Pixel Final Color
Color pixelFinalColor(min(1,kd * Factor * color.getcolorRed()) + (ka * color.getcolorRed()) ,
min(1,kd * Factor * color.getcolorGreen()) + (ka * color.getcolorGreen()) ,
min(1,kd * Factor * color.getcolorBlue()) + (ka * color.getcolorBlue()) ,1);
答案 0 :(得分:1)
你在正常计算中错位了大括号,应该是
TVector NormalIntersect ((intersect.getVectX() - position.getVectX())/r,
(intersect.getVectY() - position.getVectY())/r,
(intersect.getVectZ() - position.getVectZ())/r);
答案 1 :(得分:1)
首先,如果你的getRayDirection()
正在按照它所说的那样做,它会产生一个方向,而不是一个凝视点,所以你不应该从它减去射线原点,这是一个点。
(虽然方向和点都用矢量表示,但没有意义
向一个方向添加一个点)
此外,您正在对L1
进行规范化,然后计算其幅度并将其每个组件除以该幅度,以生成UnitVector
,然后再次在上调用normalize
。这是不必要的:第一次归一化后L1
的幅度为1,您将相同的矢量标准化3次,只需使用L1
。
最后一个问题是夹紧问题。您调用Factor
的变量是值cos(th)
,其中th
是光线方向与法线之间的角度。但cos(th)
的范围为[1,-1]
,您只需要[0,1]
的范围,因此您必须将Factor
限制在该范围内:
Factor = max(0, min( NormalIntersect.dotProduct(UnitVector), 1));
(并删除制作min
)中的color
来电。
这种夹紧对于法线背向光线的面是必要的,这些面将具有负cos(th)
值。它们的法线和光的方向之间的角度大于pi/2
。直观地说,它们应该对于所讨论的光线看起来尽可能暗,所以我们将它们钳制到0)。
这是我的代码的最终版本,应该工作。我将假设您在scale(float)
课程中定义了operator +(const TVector &)
和TVector
等,因为它们会让您的生活更轻松。另外,为简洁起见,我打算致电NormalIntersect
,只需normal
:
TVector
intersect = ray.getRayOrigin() + ray.getRayDirection().scale(t),
normal = (intersect - position).normalize(),
L1 = (light.GetPosition() - intersect).normalize();
float
diffuse = max(0, min(normal.dotProduct(L1), 1)),
kd = 0.1,
ka = 0.9;
Color pixelFinalColor = color.scale(kd * diffuse + ka);