GrandDuc
GrandDuc

Reputation: 13

Problems with a simple raytracer in c++

What it does is basically checking for collisions against an array of triangles and drawing an image based on the color of the triangle it hits. I think my problem lies in the collision detection. Code here:

for (int i = 0; i < triangles.size(); i++){
    vec3 v0 = triangles[i].v0;
    vec3 v1 = triangles[i].v1;
    vec3 v2 = triangles[i].v2;
    vec3 e1 = v1 - v0;
    vec3 e2 = v2 - v0;
    vec3 b = start - v0;
    mat3 A(-dir, e1, e2);
    vec3 x = glm::inverse(A) * b;

    if (x.x > 0 && x.y > 0 && (x.x + x.y < 1) && (x.z - start.z>= 0)){
        return true;
    }
}

...where "dir" is the direction of the ray coming from the camera, calculated as "x - SCREEN_WIDTH / 2, y - SCREEN_HEIGHT / 2, focalLength". SCREEN_WIDTH, SCREEN_HEIGHT and focalLength are constants. Start is the position of the camera, set to 0,0,0.

What I'm not sure about is what x really is and what I should check for before returning true. "x.x > 0 && x.y > 0 && (x.x + x.y < 1)" is supposed to check if the ray hits not only on the same plane but actually inside the triangle, and the last part ("x.z - start.z>= 0", the one I'm least sure about), if the collision happened in front of the camera.

I get images, but no matter how much I try it's never right. It's supposed to be a classic TestModel of a room with different colored walls and two shapes in it. The closest I think I've been is getting four of five walls right, with the far one missing and a part of one of the shapes on the other side of it.

Upvotes: 1

Views: 171

Answers (1)

Alnitak
Alnitak

Reputation: 339776

I'm not familiar with the matrix formulation for triangle intersection - it sounds quite expensive.

My own code is below, where my e1 and e2 are equivalent to yours - i.e. they represent the edge vectors from v0 to v1 and v2 respectively:

// NB: triangles are assumed to be in world space
vector3 pvec = vector3::cross(ray.direction(), e2);
double det = e1.dot(pvec);
if (::fabs(det) < math::epsilon) return 0;

double invDet = 1.0 / det;
vector3 tvec(p0, ray.origin());

double u = tvec.dot(pvec) * invDet;
if (u < 0 || u > 1) return 0;

vector3 qvec = vector3::cross(tvec, e1);
double v = ray.direction().dot(qvec) * invDet;
if (v < 0 || u + v > 1) return 0;

double t = e2.dot(qvec) * invDet;
if (t > math::epsilon) {        // avoid self intersection
    // hit found at distance "t"
}

I suspect the problem is in your calculation of the ray vector, which should be normalised.

Upvotes: 1

Related Questions