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

4
编辑: 我发现所有像素颜色都是上下颠倒的,因为屏幕坐标系和世界坐标系的差异,这个问题已经解决了。
编辑: 在遵循@TheVee建议(使用绝对值)之后,我的图片好多了,但我仍然看到颜色方面存在问题。
我在光线追踪三角形方面遇到一些小问题。这是我关于同一主题的之前的问题的后续。那个问题的答案让我意识到需要采取不同的方法。我采用的新方法效果要好得多,但现在我在我的光线追踪器中看到了一些问题:
  1. 有一个三角形永远不会以彩色呈现(它总是黑色的,即使它本来应该是黄色的).
这里是我期望看到的:
Expected 但这里是我实际看到的:
Actual
  1. 解决第一个问题的调试,即使我删除所有其他对象(包括蓝色三角形),黄色三角形始终呈黑色渲染,因此我不认为这是我发送的阴影射线的问题。我怀疑与相机相对于三角形/平面的角度有关。
下面是我光线追踪三角形的过程,它基于这个网站中的过程。
  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;
}

我认为我的着色出现了一些问题,这可能与各个三角形的法线有关。在为球体和三角形构建照明模型时,我考虑了以下方程式:
Lighting model 现在,这里是实现此操作的代码:
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,蓝色三角形约为-0.707,所以我不确定法线朝向错误是否是问题所在。无论如何,当我确保光线和法线之间的角度为正数(Math.abs(directionToLight.dotProduct(normal));)时,会导致相反的问题:
Absolute value of dot products 我怀疑这将是一个小的拼写错误/错误,但我需要另一双眼睛来发现我没有发现的问题。
注意:我的三角形有顶点(a,b,c),边缘(u,v)分别使用a-bc-b计算(同样,用于计算平面/三角形法线)。一个Vector由一个(x,y,z)点组成,一个Ray由一个起点Vector和一个归一化方向Vector组成。
以下是我如何计算所有三角形的法线:
private Vector getPlaneNormal()
{
    Vector v1 = getU();
    Vector v2 = getV();
    return v1.crossProduct(v2).normalize();
}

请告诉我是否遗漏了任何你认为解决这些问题的重要内容。
编辑:在@TheVee的帮助下,这是我最终得到的结果: “工作”图像 深度缓冲仍存在问题,并且三角形的光照高亮度也存在问题,但我尝试解决的问题已经解决了。

1
尝试在 if (denominator < epsilon) 中取绝对值。症状看起来像是前后面多边形的典型问题。请记住,点积很容易为负数,而负数肯定比您的 epsilon 更小,即使它“很大”。 - The Vee
1
嗯,你的颜色选错了,但是你在这里公开的代码中没有提到颜色计算。尝试在某个地方获取黄色的负阴影将会是相同的。 - The Vee
1
也许修复后,轻组件只是增加了总和过高的问题。(请注意,在此之前,您一直得到一个完全没有阴影的平面蓝色三角形,这可能只是漫反射项,但您要尝试获取的场景在其上具有明显的Phong光照。)尝试仅使用环境光/漫反射光/镜面光重新渲染,您可能会找到罪魁祸首。 - The Vee
1
我已经在写一个了。 - The Vee
@TheVee 我已经编辑好了并放在结尾处。虽然z缓冲和三角形的phong着色仍有一些问题,但我试图解决的问题已经解决了。谢谢! - Cache Staheli
显示剩余6条评论
1个回答

2
在包含平面物体的场景中进行光线追踪时,常见问题是我们从错误的方向击中它们。包含点积的公式假设光线沿着朝外法向量的方向进入物体。这仅对三角形的可能方向的一半成立,如果您不幸将其定向为法向量背离光源,则无效。
严格来说,在物理世界中,三角形并不具有零体积。它由一层薄材料组成,在两侧都有一个正确的指向外部的法向量。分配单个法向量是一种简化,因为两者只有符号不同。
然而,如果我们进行了简化,我们需要加以考虑。在公式中使用技术上内向法向量会产生负的点积,这是公式没有预测到的情况。这就好像光线从对象内部进入或者它撞上了显然不可能阻挡它的表面。这就是为什么它们会给出错误结果。负值将减少来自其他光源的光的数量,具体取决于大小和实现方式,可能导致变暗、完全黑色或数值下溢。
但是因为我们知道正确的法线向量要么是我们正在使用的向量,要么是它的负向量,所以我们可以通过在假定点积为正的情况下进行预防性绝对值运算(在您的代码中,这是指angleBetweenLightAndNormal),一次性解决这些问题。一些库如OpenGL会自动执行这个操作,并利用额外的信息(符号)来选择您可能提供的两种不同的材料(前面和后面)。或者,它们可以被设置为根本不绘制实心物体的背面,因为它们会被实心物体的正面覆盖掉(称为面剔除),从而节省大约一半的计算工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接