在光线追踪器中计算阴影时出现意外结果

3

我正在使用C++开发光线追踪器,到目前为止,我已经能够基于漫反射、镜面反射和环境光计算照明模型。但当我尝试在场景中添加阴影时,场景变得非常混乱:

enter image description here

我的代码分为以下几部分:

  • 我有一个基类“SceneObject”,其中包含一个虚拟方法intersect(),该方法接受一个光线(由原点和方向定义)并输出一个布尔值,以及计算得出的t值、撞击点和对象法线的返回参数。
  • “Material”类包含镜面反射和漫反射颜色(向量),以及Phong指数(整数)的值。
  • 我从上述SceneObject派生了3个类:平面、三角形和球体类,每个类都有自己在基类中定义的版本的交点函数intersect()。
  • 我有一个函数,它使用对象法线、撞击点、光源和对象材料来计算给定像素的输出颜色。
  • 我要渲染的所有对象都存储在一个向量中。

这是我的一个派生类Triangle:

class Triangle: public SceneObject{
public:
    Triangle (vec3 a, vec3 b, vec3 c, Material mat){
        name = "Triangle";
        p0 = a;
        p1 = b;
        p2 = c;
        objectMaterial = mat;
        normal = normalize(cross(p0-p1, p0-p2));
    }

    //Möller-Trumbore algorithm
    bool intersect(Ray aRay, float &t, vec3 &hitPoint, vec3 &n){//we will use ray-plane intersection and barycentric coords:

        bool returnValue = false;
        //first we need to get a t of intersection between the passed ray and the triangle
        vec3 v0v1 = p1-p0;
        vec3 v0v2 = p2-p0;
        vec3 pvec = cross(aRay.getDirection(), v0v2);

        float det = dot(v0v1, pvec);

        if ( det >= 1e-6 ){ // Only draw if not backfacing

            float invDet = 1/det;

            float u = dot(-p0, pvec) * invDet;
            // No intersection if u < 0 or u > 1
            if (u >=0 && u <= 1) {
                vec3 qvec = cross(-p0, v0v1);
                float v = dot(aRay.getDirection(), qvec) * invDet;

                // No intersection if v < 0 or u + v > 1
                if (v >=0 && (u + v) <= 1){
                    t = dot(v0v2, qvec) * invDet;
                    returnValue = true;

                    hitPoint = aRay.getOrigin() + (t*aRay.getDirection());
                    n = normal;
                    //calculated_Out_Colour = calculateOutputColour(normal, aRay, lightSource, objectMaterial, t, hitPoint);

                }
            }
        }

        return returnValue; 
    }

private:
    vec3 p0;
    vec3 p1;
    vec3 p2;
    vec3 normal;
};

这是我的主循环,我在其中为窗口的每个像素生成所有光线,并确定颜色以及当前位置是否在阴影中:
for(int i=0;i<imageBuffer.Height();i++){
for(int j=0;j<imageBuffer.Width();j++){
    float currentX = ((float)i-256);
            float currentY = ((float)j-256);
            //cout << currentX << ", " << currentY << ", " << currentZ << endl;
            //make a ray for this pixel (i,j)
            glm::vec3 rayDirection = glm::normalize(glm::vec3(currentX, currentY, -d));
    //make a ray for this pixel (i,j)
    Ray currentRay(vec3(0,0,0), rayDirection);

    vec3 hitPoint;
    vec3 normalAtHit;
    float tnear = 999; // closest intersection, set to INFINITY to start with
    SceneObject* object = NULL;
    for (int k = 0; k < objects.size(); k++) {
        float t; // intersection to the current object if any
        if (objects[k]->intersect(currentRay, t, hitPoint, normalAtHit) && t < tnear) {
            object = objects[k].get();
            tnear = t;

            vec3 shadowRayDirection = normalize(light1.getLightOrigin()-hitPoint);
            Ray shadowRay(hitPoint+vec3(0.03, 0.03,0.03), shadowRayDirection);
            float shadowT;
            vec3 shadowHitPoint;
            vec3 shadowN;
            for (int m = 0; m < objects.size(); ++m) {
                if (objects[m]->intersect(shadowRay, shadowT, shadowHitPoint, shadowN)) {
                    imageBuffer.SetPixel(i, j, ambientColour*ambientIntensity); 
                    break;
                } else {
                    imageBuffer.SetPixel(i, j, calculateOutputColour(normalAtHit, currentRay, light1, objects[k]->getMaterial(), hitPoint));
                }
            }
            }
        }
    }
}

我真的很困惑,不知道为什么会发生这种情况。我尝试使用这里描述的算法,但它产生了与图像中显示的相同的结果。供参考,如果我循环并检查阴影,我的场景会看起来像这样:

enter image description here

我很感激在调试这个问题时得到的任何帮助。谢谢。


1
对于你的阴影光线,你应该使用hitPoint+0.03*shadowRayDirection或者hitPoint+0.03*normalAtHit作为光线的起点(两种方式都可以;可以根据实际情况进行选择)。 - Cornstalks
修改后的代码如下:float shadowOffset = 0.05; Ray shadowRay(hitPoint+shadowOffset*shadowRayDirection, shadowRayDirection);但是它产生了相同的结果 :-( - jjcastil
你计算t的方式可能是错误的(但也有可能是正确的,我不确定)。在我的光线追踪器中,我用了不同的方法:vec3 n = cross(v0v1, v0v2); float num = dot(n, p0 - aRay.getOrigin()); float den = dot(n, aRay.getDirection()); t = num / den; (但在除法之前请检查den != 0)。你可以尝试一下这个方法。为了测试你的t值是否正确,你可以输出一个深度图像,这样你就可以看到你计算的交点是否与你期望的相匹配。 - Cornstalks
1个回答

0

Cornstalks 是对的,但有一件重要的事情,如果你使用 hitPoint+0.03*normalAtHit,你需要确保法线与入射光线匹配,从你的代码中我们可以看到你只是取了原始图形的法线,但每个面都有两个法线(一个与另一个相反)。

根据这个,你可能会有一个从错误侧开始的光线,从而导致无效的阴影。

为了解决这个问题,你可以检查入射光线与法线的点积,根据结果(大于 0 或小于 0),你将知道是否需要翻转你的法线。


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