4

因此,我在网上阅读了有关光线追踪的信息,并开始在业余时间从头开始编写光线追踪器。我正在使用 C++,我已经学习了大约一个月了。我已经阅读了网络上的光线追踪理论,到目前为止它工作得很好。它只是一个基本的光线追踪器,既不使用模型也不使用纹理。

起初它制作了一个 Raycaster,并且对结果非常满意。 在此处输入图像描述

所以我尝试了多个对象,它也起作用了。我只是在这个实现中使用了漫反射着色,并将光的颜色添加到对象颜色的未着色点。 在此处输入图像描述 不幸的是,此代码不适用于多个光源。于是我开始重写我的代码,让它支持多灯。我还阅读了 Phong 照明并开始工作: 在此处输入图像描述 它甚至可以与多个灯一起使用: 在此处输入图像描述

到目前为止,我很高兴,但现在我有点卡住了。我已经尝试修复这个问题很长一段时间了,但我什么也没想到。当我添加第二个球体甚至第三个球体时,只有最后一个球体被照亮。最后,我的意思是我的数组中存储所有对象的对象。请参见下面的代码。 在此处输入图像描述

显然,紫色球体应该有类似的照明,因为它们的中心位于同一平面上。令我惊讶的是,球体只有环境照明 --> 阴影,但情况并非如此。

所以现在我的跟踪功能:

Colour raytrace(const Ray &r, const int &depth)
{
    //first find the nearest intersection of a ray with an object
    //go through all objects an find the one with the lowest parameter

    double t, t_min = INFINITY;
    int index_nearObj = -1;//-1 to know if obj found
    for(int i = 0; i < objSize; i++)
    {
        //skip light src
        if(!dynamic_cast<Light *>(objects[i]))
        {
            t = objects[i]->findParam(r);
            if(t > 0 && t < t_min) //if there is an intersection and
            //its distance is lower than the current min --> new min
            {
                t_min = t;
                index_nearObj = i;
            }
        }
    }
    //now that we have the nearest intersection, calc the intersection point
    //and the normal at that point
    //r.position + t * r.direction

    if(t_min < 0 || t_min == INFINITY) //no intersection --> return background Colour
        return White;
    Vector intersect = r.getOrigin() + r.getDirection()*t;
    Vector normal = objects[index_nearObj]->NormalAtIntersect(intersect);

    //then calculate light ,shading and colour


    Ray shadowRay;
    Ray rRefl;//reflected ray
    bool shadowed;
    double t_light = -1;
    Colour finalColour = White;
    Colour objectColor = objects[index_nearObj]->getColour();
    Colour localColour;
    Vector tmpv;

    //get material properties
    double ka = 0.1; //ambient coefficient
    double kd; //diffuse coefficient
    double ks; //specular coefficient
    Colour ambient = ka * objectColor; //ambient component
    //the minimum Colour the obj has, even if object is not hit by light
    Colour diffuse, specular;
    double brightness;
    int index = -1;
    localColour = ambient;
    //look if the object is in shadow or light
    //do this by casting a ray from the obj and
    // check if there is an intersection with another obj
    for(int i = 0; i < objSize; i++)
    {
        if(dynamic_cast<Light *>(objects[i])) //if object is a light
        {//for each light
            shadowed = false;
            //create Ray to light
            //its origin is the intersection point
            //its direction is the position of the light - intersection
            tmpv = objects[i]->getPosition() - intersect;
            shadowRay = Ray(intersect  + (!tmpv) * BIAS, tmpv);
            //the ray construcor automatically normalizes its direction
            t_light = objects[i]->findParam(shadowRay);



            if(t_light < 0) //no imtersect, which is quite impossible
                continue;

            //then we check if that Ray intersects one object that is not a light
            for(int j = 0; j < objSize; j++)
            {
                if(!dynamic_cast<Light *>(objects[j]))//if obj is not a light
                {
                    //we compute the distance to the object and compare it
                    //to the light distance, for each light seperately
                    //if it is smaller we know the light is behind the object
                    //--> shadowed by this light

                    t = objects[j]->findParam(shadowRay);
                    if(t < 0) // no intersection
                        continue;
                    if(t < t_light) // intersection that creates shadow
                        shadowed = true;
                    else
                    {
                        shadowed = false;
                        index = j;//not using the index now,maybe later
                        break;
                    }
                }
            }

            //we know if intersection is shadowed or not
            if(!shadowed)// if obj is not shadowed
            {
                rRefl = objects[index_nearObj]->calcReflectingRay(shadowRay, intersect); //reflected ray from ligh src, for ks
                kd = maximum(0.0, (normal|shadowRay.getDirection()));
                ks = pow(maximum(0.0, (r.getDirection()|rRefl.getDirection())), objects[index_nearObj]->getMaterial().shininess);
                diffuse = kd * objectColor;// * objects[i]->getColour();
                specular = ks * objects[i]->getColour();//not sure if obj needs specular colour
                brightness = 1 /(1 + t_light * DISTANCE_DEPENDENCY_LIGHT);
                localColour += brightness * (diffuse + specular);
            }
        }
    }
    //handle reflection

    //handle transmission

    //combine colours
    //localcolour+reflectedcolour*refl_coeff + transmittedcolor*transmission coeff

    finalColour = localColour; //+reflcol+ transmcol
    return finalColour;
}

接下来是渲染函数:

for(uint32_t y = 0; y < h; y++)
{
    for(uint32_t x = 0; x < w; x++)
    {
        //pixel coordinates for the scene, depends on implementation...here camera on z axis
        pixel.X() = ((x+0.5)/w-0.5)*aspectRatio *angle;
        pixel.Y() = (0.5 - (y+0.5)/w)*angle;
        pixel.Z() = look_at.getZ();//-1, cam at 0,0,0

        rTmp = Ray(cam.getOrigin(), pixel - cam.getOrigin());
        cTmp = raytrace(rTmp, depth);//depth == 0
        pic.setPixel(y, x, cTmp);//writes colour of pixel in picture
    }
}

所以这是我的相交函数:

double Sphere::findParam(const Ray &r) const
{
    Vector rorig = r.getOrigin();
    Vector rdir = r.getDirection();
    Vector center = getPosition();
    double det, t0 , t1; // det is the determinant of the quadratic equation: B² - 4AC;

    double a = (rdir|rdir); 
    //one could optimize a away cause rdir is a unit vector
    double b = ((rorig - center)|rdir)*2;
    double c = ((rorig - center)|(rorig - center)) - radius*radius;
    det = b*b - 4*c*a;
    if(det < 0)
        return -1;

    t0 = (-b - sqrt(det))/(2*a);
    if(det == 0)//one ontersection, no need to compute the second param!, is same
        return t0;

    t1 = (-b + sqrt(det))/(2*a);
    //two intersections, if t0 or t1 is neg, the intersection is behind the origin!

    if(t0 < 0 && t1 < 0) return -1;
    else if(t0 > 0 && t1 < 0) return t0;
    else if(t0 < 0 && t1 > 0) return t1;
    if(t0 < t1)
        return t0;
    return t1;
}

Ray Sphere::calcReflectingRay(const Ray &r, const Vector &intersection)const
{
    Vector rdir = r.getDirection();
    Vector normal = NormalAtIntersect(intersection);
    Vector dir = rdir - 2 * (rdir|normal) * normal;
    return Ray(intersection, !dir);
}

//Light intersection(point src)
double Light::findParam(const Ray &r) const
{
    double t;
    Vector rorig = r.getOrigin();
    Vector rdir = r.getDirection();
    t = (rorig - getPosition())|~rdir; //~inverts a Vector
    //if I dont do this, then both spheres are not illuminated-->ambient
    if(t > 0)
        return t;
    return -1;
}

这是抽象的 Object 类。每个球体、光等都是一个对象。

class Object
{
    Colour color;
    Vector pos;
    //Colour specular;not using right now
    Material_t mat;
public:
    Object();
    Object(const Colour &);
    Object(const Colour &, const Vector &);
    Object(const Colour &, const Vector &, const Material_t &);
    virtual ~Object()=0;

    virtual double findParam(const Ray &) const =0;
    virtual Ray calcReflectingRay(const Ray &, const Vector &)const=0;
    virtual Ray calcRefractingRay(const Ray &, const Vector &)const=0;
    virtual Vector NormalAtIntersect(const Vector &)const=0;

    Colour getColour()const {return color;}
    Colour & colour() {return color;}
    Vector getPosition()const {return pos;}
    Vector & Position() {return pos;}
    Material_t getMaterial()const {return mat;}
    Material_t & material() {return mat;}

    friend bool operator!=(const Object &obj1, const Object &obj2)
    {//compares only references!
        if(&obj1 != &obj2)
            return true;
        return false;
    }
};

我使用一个全局对象指针数组来存储世界的所有灯光、球体等:

Object *objects[objSize];

我知道我的代码一团糟,但如果有人知道发生了什么,我将不胜感激。

编辑 1 我添加了图片。

编辑 2 更新了代码,修复了一个小错误。仍然没有解决办法。

更新:添加了渲染代码,用于创建光线。

4

1 回答 1

6

发现问题

我设法使用 Linux 和 gcc 调试了你的光线追踪器。
关于这个问题,嗯……一旦我发现它,我就有一种想反复用头敲击键盘的冲动。:) 你的算法是正确的,除了一些偷偷摸摸的细节:

Vector intersect = r.getOrigin() + r.getDirection()*t;

计算交点时,使用t代替t_min
修复包括将上述行更改为:

Vector intersect = r.getOrigin() + r.getDirection()*t_min;

正确的输出如下:

在此处输入图像描述

其他建议

我认为问题在于您的阴影射线循环:

        //then we check if that Ray intersects one object that is not a light
        for(int j = 0; j < objSize; j++)
        {
            if(!dynamic_cast<Light *>(objects[j]))//if obj is not a light
            {
                //we compute the distance to the object and compare it
                //to the light distance, for each light seperately
                //if it is smaller we know the light is behind the object
                //--> shadowed by this light

                t = objects[j]->findParam(shadowRay);
                if(t < 0) // no intersection
                    continue;
                if(t < t_light) // intersection that creates shadow
                    shadowed = true;
                else
                {
                    shadowed = false;
                    index = j;//not using the index now,maybe later
                    break;
                }
            }
        }

基本上,当找到一个交叉点时,您将shadowed标志设置为true,但是您继续循环:这既低效又不正确。当找到一个交叉点时,无需再寻找另一个交叉点。我的猜测是您的shadow标志再次设置为 false 因为您没有停止循环。此外,当t >= t_light您中断循环时,这也是不正确的(就像t < 0)。我会将代码更改为以下内容:

            //then we check if that Ray intersects one object that is not a light
            for (int j = 0; j < objSize; j++)
            {
                // Exclude lights AND the closest object found
                if(j != index_nearObj && !dynamic_cast<Light *>(objects[j]))
                {
                    //we compute the distance to the object and compare it
                    //to the light distance, for each light seperately
                    //if it is smaller we know the light is behind the object
                    //--> shadowed by this light

                    t = objects[j]->findParam(shadowRay);

                    // If the intersection point lies between the object and the light source,
                    // then the object is in shadow!
                    if (t >= 0 && t < t_light)
                    {
                          // Set the flag and stop the cycle
                          shadowed = true;
                          break;
                    }
                }
            }

其他一些建议:

  • 通过添加一个函数来重构您的渲染代码,该函数给定光线找到与场景的最近/第一个交点。这避免了代码重复。

  • 不要为点积和规范化重载运算符:改用专用函数。

  • 尽量减少变量的范围:这提高了代码的可读性。

  • 继续探索光线追踪的东西,因为它很棒:D

于 2014-09-24T21:10:10.943 回答