Raytracing shadows

So, I read about raytracing on the net and started writing Raytracer from scratch in my spare time. I am using C ++ which I have been studying for about a month now. I've read about the theory of raytracing on the web and it has worked quite well so far. Its just a basic Raytracer that uses no models or textures.

He first made a Raycaster and was very pleased with the result. enter image description here

So, I tried several objects and it worked too. I just used diffuse shading in this implementation and added light to the color of the object at the points that it is not shaded. enter image description here Unfortunately this code didn't work for multiple lights. So I started rewriting my code to support multiple lights. I also read about Phong lighting and got to work: enter image description here It even works with multiple light sources: enter image description here

So far I'm happy, but now I'm kind of stuck. I've tried fixing this for quite a while, but I haven't come up with anything. When I add a second Orb, or even a third, only the last Orb will light up. By the last, I mean the object in my array where I store all the objects. See below code. enter image description here

It is clear that the violet Sphere should have similar illumination, since their center lies in the same plane. To my surprise, the sphere only has ambient lighting → shaded, which shouldn't be.

So now my trace function:

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;
}

      

The rendering function is shown below:

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
    }
}

      

So here are my intersection functions:

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;
}

      

Here is the abstract class Object. Every ball, light, etc. It is an 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;
    }
};

      

I am using a global array of object pointers to store all lights, spheres, etc. the world:

Object *objects[objSize];

      

I know my code is a mess, but if anyone has any idea what is going on, I would really appreciate it.

EDIT 1 I've added photos.

EDIT 2 Updated code, minor bug fixed. There is still no solution.

Update: Added render code that creates rays.

+3


source to share


1 answer


Problem detected

I was able to debug your ray tracer using Linux and gcc.
As for the problem, ok ... as soon as I found it, I felt the desire again

to mind my keyboard. :) Your algorithm is correct except for a little sneaky detail:
Vector intersect = r.getOrigin() + r.getDirection()*t;

      

When you calculate the intersection point, you use t

instead t_min

.
The fix is ​​to change the specified line:

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

      

The correct conclusion is as follows:

enter image description here



Other offers

I think the problem is with your shadow ray loop:

        //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;
                }
            }
        }

      

Basically, when an intersection is detected, you set the flag shadowed

to true

BUT you keep looping: this is inefficient and wrong. When an intersection is found, there is no need to look for another. I am assuming your flag shadow

is set to false again because you are not stopping the loop. Also, when t >= t_light

you break the loop, which is also wrong (it's just how t < 0

). I would change the code to the following:

            //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;
                    }
                }
            }

      

Some other suggestions:

  • Build your rendering code by adding a function that gives a ray that finds the closest / first intersection with the scene. This avoids duplication of code.

  • Don't overload operators for dot product and normalization: use special functions instead.

  • Whenever possible, try to keep your variables as small as possible: this improves the readability of your code.

  • Keep exploring raytracing because it's awesome: D

+6


source







All Articles