So I was tasked to create a model of the Cornell Box. I've managed to do everything up until casting shadows, in which case some shadows are cast when there should not be any. Here are pictures of what it looks like now and a larger picture of what it should look like:

enter image description here

I've found that for some reason, the box walls are also casting shadows.

A quick run-down of how it works: we have a pinhole camera which casts rays out and finds the nearest collision point, which is everything you're seeing. It saves these points as well as the distance from some start pt (camera in this case) to the collision point in a pre-defined data structure called an Intersection. It then uses this data to calculate the distance away from the light to create the intensities in lighting. The same method that is used to find the nearest collision point is used to find the nearest object for shadows as well using the original collision point as the start and the light source as the direction of the ray vector.

I've narrowed down the problem to raytracing; when checking to see if there are any objects to cast shadows from from say the upper-right region of the back gray wall, the cyan ceiling is picked up at a point from the same side of the light. E.g. say the light source is in the middle and the original point is on our right side of the light. The method somehow picks up a point that is on the same side of the light when the vector should be going straight through the light source and end somewhere on our left side.

Here is my source code for the functions in question:

bool ClosestIntersection( vec3 start, vec3 dir, const vector<Triangle>& triangles, Intersection& it ) {

    vec3 least;
    vec3 e1, e2, b, v0;
    mat3 A;

    least[0] = m;

    int index = triangles.size()-1;

    for(int i = 0; i < int(triangles.size()); i++) {

        v0 = triangles[i].v0;
        e1 = triangles[i].v1 - v0;
        e2 = triangles[i].v2 - v0;
        b = start - v0;

        A = mat3( -dir, e1, e2);

        if(!getInverse(A,b)) {continue;}

        vec3 x = A * b;

        if(x[0] <= least[0] && x[1] + x[2] <= 1.f && x[1] >= 0.f && x[2] >= 0.f && x[0] >= 0.00001f) {
            least = x;
            index = i;

    if (least[0] == m) {
        return false;
    else {
        it.position = least[0] * dir + start;
        const vec3 t = it.position-start;
        it.distance = sqrt(t[0]*t[0] + t[1]*t[1] + t[2]*t[2]);
        it.triangleIndex = index;
        return true;

vec3 DirectLight( const Intersection& i ){
    const Triangle T = triangles[i.triangleIndex];
    const vec3 r = lightPos - i.position;
    const float dist = sqrt(r[0]*r[0] + r[1]*r[1] + r[2]*r[2]);

    Intersection t;
    t.distance = dist;
    t.position = i.position;
    t.triangleIndex = i.triangleIndex; 

    //ClosestIntersection( i.position, lightPos, triangles, t);

    if(!ClosestIntersection( lightPos, i.position, triangles, t)) {return vec3(1,0,0);}
    else { return vec3(0,0,0); } */

    if(ClosestIntersection( i.position, lightPos, triangles, t) && (dist-t.distance > 1.f)) {

        return vec3(0,0,0);
    } else {
        const vec3 B = 14.f * T.color * (float(max(dot(r,T.normal),float(0)) / float(4*3.14 * dist* dist)));

        return B; 


I have spent over twelve hours trying to find this problem. Even my lecturer can't figure out the problem. Any help would be much appreciated!


are you sure you are not re-intersecting the same surface when checking for light occluder ? this is a classical precision issue. There are many way to tackle it:

  • First, have a good conditioning of the intersection point, by reintersecting a second time: consider your first hit distance d as approximate, create the point E' = Eye + (d-eps)*ray, recompute the intersection from here. (variant: for the first intersection, just use the bounding box).

  • Second, for grazing angle, depending on your shape representation, storage and computation precision and scales, tessellation, you might have dangerous imprecision here too, and must still ensure you won't reintersect the same surface. If flat or convex or tesselated, you can tag it an non-intersectable for the shadow-ray test. A simpler trick (more approximate) is to displace the start of the shadow ray by epsilon in the normal direction.


There is a trick that many in fact let you sidestep the issue altogether*. The scene and the set of things to be raytraced does not have to be the same. Most ray tracers allow for objects to not participate in shadow casting, reflections, etc.

In your case you could just take the outside walls of from shadow casting and therefore they would not self shadow allowing you to put lights outside the box with no problems.

Anyway you could add a slight bias to the sampling and consider the objects slightly further than your calculation result gives, for shadow casting this avoids all kinds of problems.

* Yes you have a problem you should fix that. But sometimes quick and done is all thats needed


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.