光线追踪三角形的问题(方向和着色)

Issues with Raytracing triangles (orientation and coloring)

编辑:我发现由于屏幕坐标和世界坐标之间的差异,所有像素都颠倒了,所以这不再是问题。
编辑: 在遵循@TheVee 的建议(使用绝对值)后,我的图像变得更好了,但我仍然看到颜色问题。

我在光线追踪三角形方面遇到了一些麻烦。这是我关于同一主题的 的跟进。这个问题的答案让我意识到我需要采取不同的方法。我采用的新方法效果更好,但我现在发现我的光线追踪器存在几个问题:

  1. 有一个三角形从不呈现颜色(它始终是黑色,即使它的颜色应该是黄色)。

这是我期待看到的:

但这就是我实际上看到的:

  1. 解决调试第一个问题,即使我删除了所有其他对象(包括蓝色三角​​形),黄色三角形始终呈现黑色,所以我不认为我的阴影光线有问题正在发送。我怀疑这与 triangle/plane 相对于相机的角度有关。

这是我的光线追踪三角形过程,它基于 this website 中的过程。

  1. 判断光线是否与平面相交。
  2. 如果是,则确定光线是否在三角形内部相交(使用参数坐标)。

这里是判断光线是否击中平面的代码:

private Vector getPlaneIntersectionVector(Ray ray)
{
    double epsilon = 0.00000001;
    Vector w0 = ray.getOrigin().subtract(getB());
    double numerator = -(getPlaneNormal().dotProduct(w0));
    double denominator = getPlaneNormal().dotProduct(ray.getDirection());
    //ray is parallel to triangle plane
    if (Math.abs(denominator) < epsilon)
    {
        //ray lies in triangle plane
        if (numerator == 0)
        {
            return null;
        }
        //ray is disjoint from plane
        else
        {
            return null;
        }
    }
    double intersectionDistance = numerator / denominator;

    //intersectionDistance < 0 means the "intersection" is behind the ray (pointing away from plane), so not a real intersection
    return (intersectionDistance >= 0) ? ray.getLocationWithMagnitude(intersectionDistance) : null;
}

一旦我确定光线与平面相交,这里是确定光线是否在三角形内的代码:

private boolean isIntersectionVectorInsideTriangle(Vector planeIntersectionVector)
{
    //Get edges of triangle
    Vector u = getU(); 
    Vector v = getV();

    //Pre-compute unique five dot-products
    double uu = u.dotProduct(u);
    double uv = u.dotProduct(v);
    double vv = v.dotProduct(v);
    Vector w = planeIntersectionVector.subtract(getB());
    double wu = w.dotProduct(u);
    double wv = w.dotProduct(v);
    double denominator = (uv * uv) - (uu * vv);

    //get and test parametric coordinates
    double s = ((uv * wv) - (vv * wu)) / denominator;
    if (s < 0 || s > 1)
    {
        return false;
    }
    double t = ((uv * wu) - (uu * wv)) / denominator;
    if (t < 0 || (s + t) > 1)
    {
        return false;
    }

    return true;
}

我认为我的着色有问题。我认为这与各种三角形的法线有关。这是我在为球体和三角形构建光照模型时考虑的方程式:

现在,这是执行此操作的代码:

public Color calculateIlluminationModel(Vector normal, boolean isInShadow, Scene scene, Ray ray, Vector intersectionPoint)
{
    //c = cr * ca + cr * cl * max(0, n \dot l)) + cl * cp * max(0, e \dot r)^p
    Vector lightSourceColor = getColorVector(scene.getLightColor()); //cl
    Vector diffuseReflectanceColor = getColorVector(getMaterialColor()); //cr
    Vector ambientColor = getColorVector(scene.getAmbientLightColor()); //ca
    Vector specularHighlightColor = getColorVector(getSpecularHighlight()); //cp
    Vector directionToLight = scene.getDirectionToLight().normalize(); //l
    double angleBetweenLightAndNormal = directionToLight.dotProduct(normal);
    Vector reflectionVector = normal.multiply(2).multiply(angleBetweenLightAndNormal).subtract(directionToLight).normalize(); //r

    double visibilityTerm = isInShadow ? 0 : 1;
    Vector ambientTerm = diffuseReflectanceColor.multiply(ambientColor);

    double lambertianComponent = Math.max(0, angleBetweenLightAndNormal);
    Vector diffuseTerm = diffuseReflectanceColor.multiply(lightSourceColor).multiply(lambertianComponent).multiply(visibilityTerm);

    double angleBetweenEyeAndReflection = scene.getLookFrom().dotProduct(reflectionVector);
    angleBetweenEyeAndReflection = Math.max(0, angleBetweenEyeAndReflection);
    double phongComponent = Math.pow(angleBetweenEyeAndReflection, getPhongConstant());
    Vector phongTerm = lightSourceColor.multiply(specularHighlightColor).multiply(phongComponent).multiply(visibilityTerm);

    return getVectorColor(ambientTerm.add(diffuseTerm).add(phongTerm));
}

我看到法线和光源之间的点积对于黄色三角形是 -1,对于蓝色三角形大约是 -.707,所以我不确定法线是否是错误的方式是问题所在。无论如何,当我添加确保光线和法线之间的角度为正(Math.abs(directionToLight.dotProduct(normal));)时,它导致了相反的问题:

我怀疑它会很小 typo/bug,但我需要另一双眼睛才能发现我看不到的东西。

注意:我的三角形有顶点(a,b,c),边(u,v)分别使用a-bc-b计算(另外,那些用于计算 plane/triangle 法线)。 Vector(x,y,z)点组成,Ray由原点Vector和归一化方向Vector.[=31=组成]

以下是我计算所有三角形法线的方法:

private Vector getPlaneNormal()
{
    Vector v1 = getU();
    Vector v2 = getV();
    return v1.crossProduct(v2).normalize();
}

如果我遗漏了您认为对解决这些问题很重要的任何内容,请告诉我。

编辑: 在@TheVee 的帮助下,这就是我最后得到的:

z 缓冲和带有三角形的 phong 高光仍然存在问题,但我在这里试图解决的问题已解决。

在包括平面物体在内的场景的光线追踪中,我们从错误的一侧击中它们是一个常见的问题。包含点积的公式是在一个固有假设下提出的,即光从外向法线指向的方向入射到物体上。 这可能只适用于你的三角形的一半可能方向,而且你运气不好,无法将其法向背离光线。

从技术上讲,在物理世界中,三角形的体积不会为零。它由一层薄薄的 material 组成。在任一侧,它都有指向外部的适当法线。分配单个法线是一种合理的简化,因为两者仅符号不同。

但是,如果我们进行了简化,则需要考虑到这一点。在我们的公式中,从技术上讲是向内法线会产生负点积,这种情况下它们不是为这种情况而设计的。这就像光线来自物体内部,或者光线照射到不可能挡住它的表面。这就是他们给出错误结果的原因。负值会减去来自其他光源的光,并且根据大小和实施可能会导致变暗、全黑或数值下溢。

但是因为我们知道正确的法线要么是我们正在使用的法线,要么是负法线,我们可以通过采取预防性绝对值来立即修复这些情况,其中隐式假定正点积(在您的代码中,那是 angleBetweenLightAndNormal)。一些像 OpenGL 这样的库会为你做这件事,并且最重要的是使用附加信息(符号)在 two different materials (front and back) you may provide if desired. Alternatively, they can be set to not draw the back faces for solid object at all because they will be overdrawn by front faces in solid objects anyway (known as face culling 之间进行选择),节省大约一半的数值工作。