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:

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:

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?