CPU Ray Tracer finds intersection for only a certain setup

20 Views Asked by At

As an assignment, I am trying to make a basic ray tracer in C++. I have separate classes for each object type (sphere, triangle, and mesh) and a camera. I am given an input file from which I parse the properties of each of these objects. The input files are in the following format:

#BackgroundColor 
r g b 
 
#MaxRecursionDepth 
d 
 
#ShadowRayEpsilon 
e 
 
#Camera 
x_cam y_cam z_cam 
x_gaze y_gaze z_gaze 
x_up y_up z_up 
left right bottom top 
distance 
width height 
 
#Material 
id1 
r_amb g_amb b_amb 
r_dif g_dif b_dif 
r_spec g_spec b_spec 
spec_exp 
r_mir g_mir b_mir 
 
#Material 
id2 
. . . 
. . . 
 
#AmbientLight 
r g b 
 
#PointLight 
id1 
x y z 
intensity_r intensity_g intensity_b 
 
#PointLight 
id2 
. . . 
. . . 
 
#VertexList 
x1 y1 z1 
x2 y2 z2 
. . . 
 
#Sphere 
id1 
material_id 
center_ind 
r 
 
#Sphere 
id2 
. . .  
. . .  
 
#Triangle 
id1 
material_id 
ind1 ind2 ind3 
 
#Triangle 
id2 
. . . 
. . . 
 
#Mesh 
id1 
material_id 
ind11 ind12 ind13 
ind21 ind22 ind23 
. . .  
 
#Mesh 
id2 
. . .  
. . .  

I tested my parsing functions and everything gets properly populated. I have been given three separate input files to test my code and for the first input file I get the expected output. Here is what that file looks like:

#BackgroundColor
0 0 0

#MaxRecursionDepth
3

#ShadowRayEpsilon
1e-3

#Camera
0 0 0
0 0 -1
0 1 0
-1 1 -1 1
1
800 800

#Material
1
1 1 1
1 1 1
1 1 1
1
0 0 0

#AmbientLight
25 25 25

#PointLight
1
0 0 0
1000 1000 1000

#VertexList
-0.5 0.5 -2
-0.5 -0.5 -2
0.5 -0.5 -2
0.5 0.5 -2
0.75 0.75 -2
1 0.75 -2
0.875 1 -2
-0.875 1 -2

#Sphere
1
1
8
0.3

#Triangle
1
1
5 6 7

#Mesh
1
1
3 1 2
1 3 4

Currently I am only trying to get my intersections correct so I don't account for lighting but this is the output I get: output.pbm

However, when I try with another input file, I get a completely empty image. This is the second input file

#BackgroundColor
0 0 0

#MaxRecursionDepth
3

#ShadowRayEpsilon
1e-3

#Camera
0 5 25
0 0 1
0 1 0
-1 1 -1 1
1
800 800

#Material
1
1 1 1
1 1 1
1 1 1
1
0 0 0

#Material
2
1 1 1
1 0 0
1 1 1
100
0 0 0

#AmbientLight
25 25 25

#PointLight
1
10 10 10
100000 100000 100000

#VertexList
        -100 0  100
         100 0  100
         100 0 -100
        -100 0 -100
          0 5 0

#Sphere
1
2
5
5

#Mesh
1
1
3 1 2
1 3 4

With this input what I expect to see is the following: expectedOutput

But the image I get is completely black and there seems to be no intersection at all.

Here are my codes:

mesh.cpp

#include "../include/mesh.hpp"
#include "../include/utility.hpp"
#include <cmath>
#include <limits>

bool Mesh::intersect(Ray *r){
    bool hit = false;
    float tClosest = std::numeric_limits<float>::max();
    for(Triangle *t : meshTriangles){
        if(t->intersect(r)){
            if(r->t < tClosest){
                tClosest = r->t;
                hit = true;
            }
        }
    }

    r->t = tClosest;
    return hit;
}

ray.cpp

#include "../include/ray.hpp"
#include <iostream>
#include <limits>
Ray::Ray(){
    this->t = std::numeric_limits<float>::max();
}

VECTOR3_FLOAT Ray::getIntersectionPoint(){
    return add(origin, multiplyWithScalar(direction, t));
}

void Ray::printRayEquation(){
    std::cout << "{"<< this->origin[X] <<"," << this->origin[Y] <<","<< this->origin[Z]<<"} + ";
    std::cout << "{"<< this->direction[X] <<"," << this->direction[Y] <<","<< this->direction[Z]<<"} * t";
    std::cout << std::endl;
}

void Ray::printIntersectionPoint(){
    std::cout << "{"<< this->origin[X] + this->direction[X]*t << "," 
    << this->origin[Y] + this->direction[Y]*t << "," 
    << this->origin[Z] + this->direction[Z]*t << "}" << std::endl;
}

sphere.cpp

#include "../include/sphere.hpp"
#include "../include/utility.hpp"
#include <cmath>

bool Sphere::intersect(Ray *ray){
    float t0=0, t1=0;
    VECTOR3_FLOAT originCenter;
    originCenter[X] = ray->origin[X] - this->centerCoordinate[X];
    originCenter[Y] = ray->origin[Y] - this->centerCoordinate[Y];
    originCenter[Z] = ray->origin[Z] - this->centerCoordinate[Z];

    float a = calculateDotProduct(ray->direction, ray->direction);
    float b = 2 * calculateDotProduct(ray->direction, originCenter);
    float c = calculateDotProduct(originCenter, originCenter) - powf(this->r, 2.0f);
    
    if(!solveQuadraticEquation(a,b,c,t0,t1)){
        return false;
    }
    
    if(t0 > t1) std::swap(t0, t1);
    if(t0 < 0){
        t0 = t1;
        if(t0 < 0){
            return false;
        }
    }

    ray->t = t0;
    return true;
}

triangle.cpp

#include "../include/triangle.hpp"
#include <iostream>
#include <cmath>

void Triangle::calculateTriangleNormal(){
    VECTOR3_FLOAT normal = calculateCrossProduct(
        VECTOR3_FLOAT {
            this->v1[X] - this->v0[X], //C-A
            this->v1[Y] - this->v0[Y], //C-A
            this->v1[Z] - this->v0[Z]  //C-A
        },
        VECTOR3_FLOAT{
            this->v2[X] - this->v0[X],//B-A
            this->v2[Y] - this->v0[Y],//B-A
            this->v2[Z] - this->v0[Z] //B-A
        }
    );

    this->normal[X] = -normal[X];
    this->normal[Y] = -normal[Y];
    this->normal[Z] = -normal[Z];

}

bool Triangle::intersect(Ray *r){
    VECTOR3_FLOAT edge1 = subtract(v1, v0);
    VECTOR3_FLOAT edge2 = subtract(v2, v0);
    VECTOR3_FLOAT h = calculateCrossProduct(r->direction, edge2);

    float a = calculateDotProduct(edge1, h);

    if(a > -1e-6 && a<1e-6){
        return false; // ray is parallel to trig
    }

    float f = 1.0f / a;
    VECTOR3_FLOAT s = subtract(r->origin, v0);
    float u = f * calculateDotProduct(s, h);
    if(u < 0.0f || u > 1.0f){
        return false;
    }

    VECTOR3_FLOAT q = calculateCrossProduct(s, edge1);
    float v = f * calculateDotProduct(r->direction, q);
    if (v < 0.0f || u+v > 1.0f){
        return false;
    }
    
    r->t = f * calculateDotProduct(edge2, q);
    return r->t > 1e-6;
    
}

utility.cpp

#include "../include/utility.hpp"
#include <cmath>

bool solveQuadraticEquation(float a, float b, float c, float &x1, float &x2){
    float determinant = powf(b, 2) - (4.0f*a*c);
    
    if(determinant < 0.0f){
        return false;
    }
    else if(determinant == 0.0f){
        x1 = x2 = (-b)/(2.0f * a);
    }
    else{
        x1 = (-b + sqrtf(determinant))/(2.0f*a);
        x2 = (-b - sqrtf(determinant))/(2.0f*a);
    }

    return true;
}

vector3_float.cpp

#include "../include/vector3_float.hpp"
#include <cmath>
#define X 0
#define Y 1
#define Z 2
VECTOR3_FLOAT add(VECTOR3_FLOAT vector1, VECTOR3_FLOAT vector2){
    return VECTOR3_FLOAT{
        vector1[X]+vector2[X], 
        vector1[Y]+vector2[Y], 
        vector1[Z]+vector2[Z]
    };
}

VECTOR3_FLOAT subtract(VECTOR3_FLOAT vector1, VECTOR3_FLOAT vector2){
    return VECTOR3_FLOAT{
        vector1[X]-vector2[X], 
        vector1[Y]-vector2[Y], 
        vector1[Z]-vector2[Z]
    };
}
VECTOR3_FLOAT multiplyWithScalar(VECTOR3_FLOAT vector, float scalar){
    return VECTOR3_FLOAT{
        vector[X]*scalar,
        vector[Y]*scalar,
        vector[Z]*scalar
    };
}
VECTOR3_FLOAT calculateCrossProduct(VECTOR3_FLOAT vector1, VECTOR3_FLOAT vector2){
    VECTOR3_FLOAT vector3;
    vector3[X] = vector1[Y]*vector2[Z] - vector1[Z]*vector2[Y];
    vector3[Y] = -(vector1[X]*vector2[Z] - vector1[Z]*vector2[X]);
    vector3[Z] = vector1[X]*vector2[Y] - vector1[Y]*vector2[X];
    return vector3;
}

VECTOR3_FLOAT normalizeVector(VECTOR3_FLOAT vector){
    float vectorMagnitude = calculateMagnitude(vector);
    return VECTOR3_FLOAT{
        vector[X]/vectorMagnitude,
        vector[Y]/vectorMagnitude,
        vector[Z]/vectorMagnitude
    };
}

float calculateDotProduct(VECTOR3_FLOAT vector1, VECTOR3_FLOAT vector2){
    return vector1[X]*vector2[X] + vector1[Y]*vector2[Y] + vector1[Z]*vector2[Z];
}

float calculateMagnitude(VECTOR3_FLOAT vector){
    return sqrtf(powf(vector[X], 2.0f) + powf(vector[Y], 2.0f) + powf(vector[Z], 2.0f));
}

main.cpp

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <cstring>
#include <array>
#include <cmath>
#include "include/parse.hpp"

std::vector<Ray *> computeViewingRays(Camera *camera){
    //W is gaze here not -W
    
    VECTOR3_FLOAT cameraU = calculateCrossProduct(
        VECTOR3_FLOAT {
            camera->V[X],
            camera->V[Y],
            camera->V[Z]
        },
        VECTOR3_FLOAT {
            -camera->W[X], //makes sense because gaze = -w
            -camera->W[Y],
            -camera->W[Z]
        }
    );
    
    camera->U[X] = cameraU[X];
    camera->U[Y] = cameraU[Y];
    camera->U[Z] = cameraU[Z];

    VECTOR3_FLOAT centerPixel;
    centerPixel[X] = camera->position[X] + camera->W[X]*camera->distance;
    centerPixel[Y] = camera->position[Y] + camera->W[Y]*camera->distance;
    centerPixel[Z] = camera->position[Z] + camera->W[Z]*camera->distance;

    VECTOR3_FLOAT cornerPixel;
    cornerPixel[X] = centerPixel[X] + camera->left*camera->U[X] + camera->top*camera->V[X];
    cornerPixel[Y] = centerPixel[Y] + camera->left*camera->U[Y] + camera->top*camera->V[Y];
    cornerPixel[Z] = centerPixel[Z] + camera->left*camera->U[Z] + camera->top*camera->V[Z];


    std::vector<float> allSu;
    std::vector<float> allSv;
    float cameraRLDiff = camera->right - camera->left;
    float cameraTBDiff = camera->top - camera->bottom;

    for(int i=0; i<camera->width; i++){
        float currentSu;
        currentSu = (cameraRLDiff * ((float)i+0.5f))/(float)camera->width;
        allSu.push_back(currentSu);
    }

    for(int i=0; i<camera->height; i++){
        float currentSv;
        currentSv = (cameraTBDiff * ((float)i+0.5f))/(float)camera->height;
        allSv.push_back(currentSv);
    }

    std::vector<Ray *> rays;
    for(int i=0; i<camera->height; i++){
        for(int j=0; j<camera->width; j++){
            Ray *currentRay = new Ray();
            currentRay->origin[X] = camera->position[X];
            currentRay->origin[Y] = camera->position[Y];
            currentRay->origin[Z] = camera->position[Z];

            currentRay->direction = add(cornerPixel, subtract(multiplyWithScalar(camera->U, allSu[j]), multiplyWithScalar(camera->V, allSv[i])));

            rays.push_back(currentRay);
        }
    }
    std::cout << "Calculating Ray Equations Done!" << std::endl;
    return rays;
}

int main(int argc, char *argv[]){
    std::string filename = argv[1];

    Color *backgroundColor = parseBackgroundColor(filename);
    int maxRecursionDepth = parseMaxRecursionDepth(filename);
    float shadowRayEpsilon = parseShadowRayEpsilon(filename);
    
    Camera *camera = parseCamera(filename);
    
    std::vector<Material *> materials = parseMaterials(filename);
    AmbientLight *ambientLight = parseAmbientLight(filename);
    std::vector<PointLight *> pointLights = parsePointLights(filename);
    
    VertexList *vertexList = parseVertexList(filename);
    
    std::vector<Sphere *> spheres = parseSpheres(filename);
    std::vector<Triangle *> triangles = parseTriangles(filename);
    std::vector<Mesh *> meshes = parseMeshes(filename);

    for(Sphere *s : spheres){
        s->centerCoordinate[X] = vertexList->vertices[s->centerInd][X];
        s->centerCoordinate[Y] = vertexList->vertices[s->centerInd][Y];
        s->centerCoordinate[Z] = vertexList->vertices[s->centerInd][Z];
    }

    for(Triangle *t : triangles){
        t->v0[X] = vertexList->vertices[t->v0Ind][X];
        t->v0[Y] = vertexList->vertices[t->v0Ind][Y];
        t->v0[Z] = vertexList->vertices[t->v0Ind][Z];

        t->v1[X] = vertexList->vertices[t->v1Ind][X];
        t->v1[Y] = vertexList->vertices[t->v1Ind][Y];
        t->v1[Z] = vertexList->vertices[t->v1Ind][Z];

        t->v2[X] = vertexList->vertices[t->v2Ind][X];
        t->v2[Y] = vertexList->vertices[t->v2Ind][Y];
        t->v2[Z] = vertexList->vertices[t->v2Ind][Z];
        t->calculateTriangleNormal();

    }

    for(Mesh *m : meshes){
        for(std::array<int, 3> vertexIndex : m->vertexIndices){
            Triangle *t = new Triangle();
                t->v0[X] = vertexList->vertices[vertexIndex[0]][X];
                t->v0[Y] = vertexList->vertices[vertexIndex[0]][Y];
                t->v0[Z] = vertexList->vertices[vertexIndex[0]][Z];

                t->v1[X] = vertexList->vertices[vertexIndex[1]][X];
                t->v1[Y] = vertexList->vertices[vertexIndex[1]][Y];
                t->v1[Z] = vertexList->vertices[vertexIndex[1]][Z];

                t->v2[X] = vertexList->vertices[vertexIndex[2]][X];
                t->v2[Y] = vertexList->vertices[vertexIndex[2]][Y];
                t->v2[Z] = vertexList->vertices[vertexIndex[2]][Z];
                t->calculateTriangleNormal();
                m->meshTriangles.push_back(t);
        }
    }
    
    std::vector<Ray *> viewingRays = computeViewingRays(camera);
    
    int intersectingRayCount = 0;
    int notIntersectingRayCount = 0;

    std::ofstream MyFile("output.pbm");
    MyFile << "P1\n";
    MyFile << "# Example\n";
    MyFile << camera->width << " ";
    MyFile << camera->height << "\n";

    for (int i = 0; i < camera->width; ++i) {
        for (int j = 0; j < camera->height; ++j) {
            Ray *r = viewingRays[i * camera->height + j];
            bool intersected = false;

            for(Sphere *s : spheres){
                if(s->intersect(r)){
                    intersected = true;
                    break;
                }
            }
            if(!intersected){
                for(Triangle *t : triangles){
                    if(t->intersect(r)){
                        intersected = true;
                        break;
                    }
                }
            }
            if(!intersected){
                for (Mesh *m : meshes) {
                    if(m->intersect(r)){
                        intersected = true;
                        break;
                    }
                }
            }
            if (intersected) {
                MyFile << "0";
                intersectingRayCount++;
            } else {
                MyFile << "1";
                notIntersectingRayCount++;
            }

            MyFile << " ";
        }
        MyFile << "\n";
    }
    MyFile.close();
    

    std::cout << "Total rays: " << intersectingRayCount + notIntersectingRayCount <<std::endl;
    std::cout << "Not intersecting ray count: " << notIntersectingRayCount << std::endl;
    std::cout << "Intersecting ray count: " << intersectingRayCount << std::endl;
    
    delete backgroundColor;
    delete camera;
    for(Material *m : materials){
        delete m;
    }
    delete ambientLight;
    for(PointLight *p : pointLights){
        delete p;
    }
    delete vertexList;
    for(Sphere *s : spheres){
        delete s;
    }
    for(Triangle *t : triangles){
        delete t;
    }
    for(Mesh *m : meshes){
        delete m;
    }

    return 0;
}

What might be the reason that between input file #1 and input file #2, I get drastically different results?

0

There are 0 best solutions below