16 Commits

Author SHA1 Message Date
Godzil
f226664fe3 And cones ! 2020-02-23 02:31:30 +00:00
Godzil
0650ac7b44 There were a small copy mistake in ch12 test file. Update the render output 2020-02-22 23:01:06 +00:00
Godzil
d87bbb184e And now we have cylinders! 2020-02-22 22:58:57 +00:00
Godzil
b9bacd3ac9 Don't really understand why this code is marked as not being tested where it should. 2020-02-22 18:51:03 +00:00
Godzil
1d685de8fd Trying to identify why they say these lines are not tested 2020-02-22 18:29:47 +00:00
Godzil
9c35cfc4f3 Trying to fix coverage. 2020-02-22 18:21:30 +00:00
Godzil
56095169eb Add a test for hw3render 2020-02-22 18:00:07 +00:00
Godzil
60db274214 Trye to talk a bit more in the readme XD 2020-02-22 17:45:45 +00:00
Godzil
566be9bcf6 Add missing image 2020-02-22 17:40:48 +00:00
Godzil
dac74007ea Add sample from Chapter 12 :) 2020-02-22 17:39:25 +00:00
Godzil
b251b632ac Add a parameter for shapes to not drop shadow. 2020-02-22 17:38:25 +00:00
Godzil
81e323fdf4 Added CUBES! 2020-02-22 17:30:15 +00:00
Godzil
c9021974f6 Add a world generator based on another raytracer file format I made in the past and add a crude tool to run it.
it does not render properly, there are some major differences between both engine especially in the material definition. Will need more work, but is not urgent.
2020-02-22 15:16:25 +00:00
Godzil
4d4c4a7453 Add preliminary support for material emissivity.
Not yet sure it work as I intended.
2020-02-22 15:14:09 +00:00
Godzil
935c8ebff7 Add support for multiple lights 2020-02-22 15:12:06 +00:00
Godzil
4cdf7a4264 Correct default canvas size for ch11_test 2020-02-22 01:29:42 +00:00
37 changed files with 2100 additions and 264 deletions

View File

@@ -14,34 +14,44 @@ It is writen in C++ with no STL and use [LodePNG](https://github.com/lvandeve/lo
Examples outputs
----------------
From chapter 05:
From chapter 05 - Sphere intersections:
![Chapter 5 rendering test](output/ch5_test.png)
From Chapter 06:
From Chapter 06 - Phong shading:
![Chapter 6 rendering test](output/ch6_test.png)
From Chapter 07:
From Chapter 07 - World / Camera / Scenes:
![Chapter 7 rendering test](output/ch7_test.png)
From Chapter 08:
From Chapter 08 - Shadows:
![Chapter 8 rendering test](output/ch8_test.png)
From Chapter 09:
From Chapter 09 - Planes:
![Chapter 9 rendering test](output/ch9_test.png)
From Chapter 10:
From Chapter 10 - Patterns:
![Chapter 10 rendering test](output/ch10_test.png)
From Chapter 11:
From Chapter 11 - Reflections, Transparency & Refractions
![Chapter 11 reflections rendering test](output/ch11_reflection.png)
![Chapter 11 refraction rendering test](output/ch11_refraction.png)
![Chapter 11 rendering test](output/ch11_test.png)
![Chapter 11 rendering test](output/ch11_test.png)
From Chapter 12 - Cubes:
![Chapter 12 rendering test](output/ch12_test.png)
From Chapter 13 - Cylinders:
![Chapter 13 rendering test](output/ch13_test.png)
Bonus:
![Chapter 13 cone test](output/ch13_cone.png)

BIN
output/ch12_test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

BIN
output/ch13_cone.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

BIN
output/ch13_test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

33
source/include/cone.h Normal file
View File

@@ -0,0 +1,33 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Cone header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_CONE_H
#define DORAYME_CONE_H
#include <shape.h>
#include <ray.h>
#include <intersect.h>
class Cone : public Shape {
protected:
Intersect localIntersect(Ray r);
Tuple localNormalAt(Tuple point);
bool checkCap(Ray r, double t, double y);
void intersectCaps(Ray r, Intersect &xs);
public:
bool isClosed;
double minCap;
double maxCap;
Cone() : minCap(-INFINITY), maxCap(INFINITY), isClosed(false), Shape(SHAPE_CONE) {};
};
#endif /* DORAYME_CONE_H */

28
source/include/cube.h Normal file
View File

@@ -0,0 +1,28 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Cube header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_CUBE_H
#define DORAYME_CUBE_H
#include <shape.h>
#include <ray.h>
#include <intersect.h>
class Cube : public Shape {
private:
void checkAxis(double axeOrigine, double axeDirection, double *axeMin, double *axeMax);
Intersect localIntersect(Ray r);
Tuple localNormalAt(Tuple point);
public:
Cube() : Shape(SHAPE_CUBE) {};
};
#endif /* DORAYME_CUBE_H */

33
source/include/cylinder.h Normal file
View File

@@ -0,0 +1,33 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Cylinder header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_CYLINDER_H
#define DORAYME_CYLINDER_H
#include <shape.h>
#include <ray.h>
#include <intersect.h>
class Cylinder : public Shape {
private:
Intersect localIntersect(Ray r);
Tuple localNormalAt(Tuple point);
bool checkCap(Ray r, double t);
void intersectCaps(Ray r, Intersect &xs);
public:
bool isClosed;
double minCap;
double maxCap;
Cylinder() : minCap(-INFINITY), maxCap(INFINITY), isClosed(false), Shape(SHAPE_CYLINDER) {};
};
#endif //DORAYME_CYLINDER_H

View File

@@ -26,13 +26,14 @@ public:
double shininess;
double reflective;
double transparency;
double emissive;
double refractiveIndex;
Pattern *pattern;
public:
Material() : colour(Colour(1, 1, 1)), ambient(0.1), diffuse(0.9), specular(0.9), shininess(200),
reflective(0.0), transparency(0.0), refractiveIndex(1.0), pattern(nullptr) {};
reflective(0.0), transparency(0.0), emissive(0), refractiveIndex(1.0), pattern(nullptr) {};
Colour lighting(Light light, Tuple point, Tuple eyeVector, Tuple normalVector, Shape *hitObject, bool inShadow = false);
@@ -40,7 +41,12 @@ public:
double_equal(this->diffuse, b.diffuse) &&
double_equal(this->specular, b.specular) &&
double_equal(this->shininess, b.shininess) &&
double_equal(this->reflective, b.reflective) &&
double_equal(this->transparency, b.transparency) &&
double_equal(this->emissive, b.emissive) &&
double_equal(this->refractiveIndex, b.refractiveIndex) &&
(this->colour == b.colour); };
bool operator!=(const Material &b) const { return !(*this == b); };
};

View File

@@ -18,4 +18,7 @@ bool double_equal(double a, double b);
double deg_to_rad(double deg);
double min3(double a, double b, double c);
double max3(double a, double b, double c);
#endif /* DORAYME_MATH_HELPER_H */

View File

@@ -22,6 +22,9 @@ enum ShapeType
SHAPE_NONE,
SHAPE_SPHERE,
SHAPE_PLANE,
SHAPE_CUBE,
SHAPE_CYLINDER,
SHAPE_CONE,
};
/* Base class for all object that can be presented in the world */
@@ -38,6 +41,7 @@ public:
Matrix transformMatrix;
Matrix inverseTransform;
Material material;
bool dropShadow;
public:
Shape(ShapeType = SHAPE_NONE);

View File

@@ -26,6 +26,7 @@ public:
double_equal(this->y, b.y) &&
double_equal(this->z, b.z) &&
double_equal(this->w, b.w); };
bool operator!=(const Tuple &b) const { return !(*this == b); };
Tuple operator+(const Tuple &b) const { return Tuple(this->x + b.x, this->y + b.y,
this->z + b.z, this->w + b.w); };

View File

@@ -44,7 +44,7 @@ public:
Tuple shadeHit(Computation comps, uint32_t depthCount = 4);
Tuple colourAt(Ray r, uint32_t depthCount = 4);
bool isShadowed(Tuple point);
bool isShadowed(Tuple point, uint32_t light = 0);
Colour reflectColour(Computation comps, uint32_t depthCount = 4);
Colour refractedColour(Computation comps, uint32_t depthCount = 4);

View File

@@ -10,6 +10,7 @@
#define DORAYME_WORLDBUILDER_H
#include <world.h>
#include <camera.h>
/* Let's keep a single header for now, will see later */
@@ -22,7 +23,30 @@ public:
/* Not implemented yet */
class Hw3File : public World
{
private:
Matrix transformStack[50];
uint32_t transStackCount;
public:
double currentAmbient;
double currentShininess;
double currentSpecular;
double currentDiffuse;
double currentEmission;
double currentReflective;
double currentTransparency;
double currentRefIndex;
Colour currentColour;
Matrix cam;
double camFoV;
public:
Matrix getTransformMatrix();
void popTransformMatrix();
void pushTransformMatrix();
void applyTransformMatrix(Matrix t);
Hw3File(const char *filename);
};

View File

@@ -31,4 +31,32 @@ bool double_equal(double a, double b)
double deg_to_rad(double deg)
{
return deg * M_PI / 180.;
}
double min3(double a, double b, double c)
{
if (a <= b)
{
if (c < a) return c;
return a;
}
if (b <= a)
{
if (c < b) return c;
}
return b;
}
double max3(double a, double b, double c)
{
if (a >= b)
{
if (c > a) return c;
return a;
}
if (b >= a)
{
if (c > b) return c;
}
return b;
}

124
source/shapes/cone.cpp Normal file
View File

@@ -0,0 +1,124 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Cone implementation
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <tuple.h>
#include <ray.h>
#include <shape.h>
#include <cone.h>
#include <math_helper.h>
bool Cone::checkCap(Ray r, double t, double y)
{
/* Helping function to reduce duplication.
* Checks to see if the intersection ot t is within a radius
* of 1 (the radius of our Cone from the y axis
*/
double x = r.origin.x + t * r.direction.x;
double z = r.origin.z + t * r.direction.z;
return (x * x + z * z) <= fabs(y);
}
void Cone::intersectCaps(Ray r, Intersect &xs)
{
/* Caps only mattter is the Cone is closed, and might possibly be
* intersected by the ray
*/
if ((this->isClosed) && (fabs(r.direction.y) > getEpsilon()))
{
double t;
/* Check for an intersection with the lower end cap by intersecting
* the ray with the plan at y = this->minCap
*/
t = (this->minCap - r.origin.y) / r.direction.y;
if (this->checkCap(r, t, this->minCap))
{
xs.add(Intersection(t, this));
}
/* Check for an intersection with the upper end cap by intersecting
* the ray with the plan at y = this->maxCap
*/
t = (this->maxCap - r.origin.y) / r.direction.y;
if (this->checkCap(r, t, this->maxCap))
{
xs.add(Intersection(t, this));
}
}
}
Intersect Cone::localIntersect(Ray r)
{
Intersect ret;
double A = pow(r.direction.x, 2) -
pow(r.direction.y, 2) +
pow(r.direction.z, 2);
double B = (2 * r.origin.x * r.direction.x) -
(2 * r.origin.y * r.direction.y) +
(2 * r.origin.z * r.direction.z);
double C = pow(r.origin.x, 2) -
pow(r.origin.y, 2) +
pow(r.origin.z, 2);
if ((fabs(A) <= getEpsilon()) && (fabs(B) >= getEpsilon()))
{
double t = -C / (2*B);
ret.add(Intersection(t, this));
}
else if (fabs(A) >= getEpsilon())
{
double disc = pow(B, 2) - 4 * A * C;
if (disc >= 0)
{
double t0 = (-B - sqrt(disc)) / (2 * A);
double t1 = (-B + sqrt(disc)) / (2 * A);
double y0 = r.origin.y + t0 * r.direction.y;
if ((this->minCap < y0) && (y0 < this->maxCap))
{
ret.add(Intersection(t0, this));
}
double y1 = r.origin.y + t1 * r.direction.y;
if ((this->minCap < y1) && (y1 < this->maxCap))
{
ret.add(Intersection(t1, this));
}
}
}
this->intersectCaps(r, ret);
return ret;
}
Tuple Cone::localNormalAt(Tuple point)
{
/* Compute the square of the distance from the Y axis */
double dist = point.x * point.x + point.z * point.z;
if ((dist < 1) && (point.y >= (this->maxCap - getEpsilon())))
{
return Vector(0, 1, 0);
}
if ((dist < 1) && (point.y <= this->minCap + getEpsilon()))
{
return Vector(0, -1, 0);
}
double y = sqrt(point.x * point.x + point.z * point.z);
if (point.y > 0)
{
y = -y;
}
return Vector(point.x, y, point.z);
}

76
source/shapes/cube.cpp Normal file
View File

@@ -0,0 +1,76 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Cube implementation
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <tuple.h>
#include <ray.h>
#include <shape.h>
#include <cube.h>
#include <math_helper.h>
void Cube::checkAxis(double axeOrigin, double axeDirection, double *axeMin, double *axeMax)
{
double tMinNumerator = (-1 - axeOrigin);
double tMaxNumerator = (1 - axeOrigin);
if (fabs(axeDirection) >= getEpsilon())
{
*axeMin = tMinNumerator / axeDirection;
*axeMax = tMaxNumerator / axeDirection;
}
else
{
*axeMin = tMinNumerator * INFINITY;
*axeMax = tMaxNumerator * INFINITY;
}
if (*axeMin > *axeMax)
{
double swap = *axeMax;
*axeMax = *axeMin;
*axeMin = swap;
}
}
Intersect Cube::localIntersect(Ray r)
{
Intersect ret;
double xtMin, xtMax, ytMin, ytMax, ztMin, ztMax;
double tMin, tMax;
this->checkAxis(r.origin.x, r.direction.x, &xtMin, &xtMax);
this->checkAxis(r.origin.y, r.direction.y, &ytMin, &ytMax);
this->checkAxis(r.origin.z, r.direction.z, &ztMin, &ztMax);
tMin = max3(xtMin, ytMin, ztMin);
tMax = min3(xtMax, ytMax, ztMax);
if (tMin <= tMax)
{
ret.add(Intersection(tMin, this));
ret.add(Intersection(tMax, this));
}
return ret;
}
Tuple Cube::localNormalAt(Tuple point)
{
double maxC = max3(fabs(point.x), fabs(point.y), fabs(point.z));
if (maxC == fabs(point.x))
{
return Vector(point.x, 0, 0);
}
else if (maxC == fabs(point.y))
{
return Vector(0, point.y, 0);
}
return Vector(0, 0, point.z);
}

109
source/shapes/cylinder.cpp Normal file
View File

@@ -0,0 +1,109 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Cylinder implementation
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <tuple.h>
#include <ray.h>
#include <shape.h>
#include <cylinder.h>
#include <math_helper.h>
bool Cylinder::checkCap(Ray r, double t)
{
/* Helping function to reduce duplication.
* Checks to see if the intersection ot t is within a radius
* of 1 (the radius of our cylinder from the y axis
*/
double x = r.origin.x + t * r.direction.x;
double z = r.origin.z + t * r.direction.z;
return (x * x + z * z) <= 1;
}
void Cylinder::intersectCaps(Ray r, Intersect &xs)
{
/* Caps only mattter is the cylinder is closed, and might possibly be
* intersected by the ray
*/
if ((this->isClosed) && (fabs(r.direction.y) > getEpsilon()))
{
double t;
/* Check for an intersection with the lower end cap by intersecting
* the ray with the plan at y = this->minCap
*/
t = (this->minCap - r.origin.y) / r.direction.y;
if (this->checkCap(r, t))
{
xs.add(Intersection(t, this));
}
/* Check for an intersection with the upper end cap by intersecting
* the ray with the plan at y = this->maxCap
*/
t = (this->maxCap - r.origin.y) / r.direction.y;
if (this->checkCap(r, t))
{
xs.add(Intersection(t, this));
}
}
}
Intersect Cylinder::localIntersect(Ray r)
{
Intersect ret;
double A = r.direction.x * r.direction.x + r.direction.z * r.direction.z;
/* Ray is parallel to the Y axis */
if (A >= getEpsilon())
{
double B = 2 * r.origin.x * r.direction.x +
2 * r.origin.z * r.direction.z;
double C = r.origin.x * r.origin.x + r.origin.z * r.origin.z - 1;
double disc = B * B - 4 * A * C;
if (disc >= 0)
{
double t0 = (-B - sqrt(disc)) / (2 * A);
double t1 = (-B + sqrt(disc)) / (2 * A);
double y0 = r.origin.y + t0 * r.direction.y;
if ((this->minCap < y0) && (y0 < this->maxCap))
{
ret.add(Intersection(t0, this));
}
double y1 = r.origin.y + t1 * r.direction.y;
if ((this->minCap < y1) && (y1 < this->maxCap))
{
ret.add(Intersection(t1, this));
}
}
}
this->intersectCaps(r, ret);
return ret;
}
Tuple Cylinder::localNormalAt(Tuple point)
{
/* Compute the square of the distance from the Y axis */
double dist = point.x * point.x + point.z * point.z;
if ((dist < 1) && (point.y >= (this->maxCap - getEpsilon())))
{
return Vector(0, 1, 0);
}
if ((dist < 1) && (point.y <= this->minCap + getEpsilon()))
{
return Vector(0, -1, 0);
}
return Vector(point.x, 0, point.z);
}

View File

@@ -25,6 +25,7 @@ Colour Material::lighting(Light light, Tuple point, Tuple eyeVector, Tuple norma
Tuple effectiveColour = pointColor * light.intensity;
Tuple ambientColour = Colour(0, 0, 0);
Tuple emissiveColour = Colour(0, 0, 0);
Tuple diffuseColour = Colour(0, 0, 0);
Tuple specularColour = Colour(0, 0, 0);
Tuple finalColour = Colour(0, 0, 0);
@@ -33,6 +34,8 @@ Colour Material::lighting(Light light, Tuple point, Tuple eyeVector, Tuple norma
ambientColour = effectiveColour * this->ambient;
emissiveColour = pointColor * this->emissive;
if (!inShadow)
{
lightDotNormal = lightVector.dot(normalVector);
@@ -60,7 +63,7 @@ Colour Material::lighting(Light light, Tuple point, Tuple eyeVector, Tuple norma
}
}
}
finalColour = ambientColour + diffuseColour + specularColour;
finalColour = emissiveColour + ambientColour + diffuseColour + specularColour;
return Colour(finalColour.x, finalColour.y, finalColour.z);
}

View File

@@ -15,6 +15,7 @@
Shape::Shape(ShapeType type)
{
this->dropShadow = true;
this->type = type;
this->transformMatrix = Matrix4().identity();
this->inverseTransform = this->transformMatrix.inverse();

View File

@@ -19,6 +19,11 @@ double Tuple::magnitude()
Tuple Tuple::normalise()
{
double mag = this->magnitude();
if (mag == 0)
{
return Tuple(0, 0, 0, 0);
}
return Tuple(this->x / mag, this->y / mag, this->z / mag, this->w / mag);
}

View File

@@ -94,13 +94,17 @@ Intersect World::intersect(Ray r)
Tuple World::shadeHit(Computation comps, uint32_t depthCount)
{
/* TODO: Add support for more than one light */
uint32_t lightIndex;
bool isThereAnObstacle = this->isShadowed(comps.overHitPoint);
Tuple surface = Colour(0, 0, 0);
Tuple surface = comps.object->material.lighting(*this->lightList[0], comps.overHitPoint, comps.eyeVector,
comps.normalVector, comps.object, isThereAnObstacle);
for(lightIndex = 0; lightIndex < this->lightCount; lightIndex++)
{
bool isThereAnObstacle = this->isShadowed(comps.overHitPoint, lightIndex);
surface = surface + comps.object->material.lighting(*this->lightList[lightIndex], comps.overHitPoint, comps.eyeVector,
comps.normalVector, comps.object, isThereAnObstacle);
}
Tuple reflected = this->reflectColour(comps, depthCount);
Tuple refracted = this->refractedColour(comps, depthCount);
@@ -109,7 +113,6 @@ Tuple World::shadeHit(Computation comps, uint32_t depthCount)
double reflectance = comps.schlick();
return surface + reflected * reflectance + refracted * (1 - reflectance);
}
return surface + reflected + refracted;
@@ -130,22 +133,27 @@ Tuple World::colourAt(Ray r, uint32_t depthCount)
}
}
bool World::isShadowed(Tuple point)
bool World::isShadowed(Tuple point, uint32_t light)
{
/* TODO: Add support for more than one light */
Tuple v = this->lightList[0]->position - point;
Tuple v = this->lightList[light]->position - point;
double distance = v.magnitude();
Tuple direction = v.normalise();
Ray r = Ray(point, direction);
Intersection h = this->intersect(r).hit();
Intersect xs = this->intersect(r);
if (!h.nothing() && (h.t < distance))
int i;
for(i = 0; i < xs.count(); i++)
{
return true;
}
Intersection h = xs[i];
if (h.t < 0) continue;
if ((h.object->dropShadow == true) && (h.t < distance))
{
return true;
}
}
return false;
}

View File

@@ -7,18 +7,20 @@
*
*/
/* Don't build for now */
#if 0
/* This file is parsing a text format from another raytracer I made in the past. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <ctype.h>
#include <math.h>
#include <worldbuilder.h>
#include <sphere.h>
#include <matrix.h>
#include <transformation.h>
#define IS_CMD(_cmd) (strncmp(buffer, _cmd, sizeof(_cmd)) == 0)
typedef void (*cmdFunc)(scene *sc, uint32_t curLine, char *first, float argv[15]);
typedef void (*cmdFunc)(Hw3File *w, uint32_t curLine, double argv[15]);
typedef struct cmd_def
{
@@ -27,111 +29,127 @@ typedef struct cmd_def
cmdFunc f;
} cmd_def;
void cmdCamera (scene *sc, uint32_t curLine, char *first, float argv[15])
static void cmdCamera (Hw3File *w, uint32_t curLine, double argv[15])
{
sc->cam.seteye(point(argv[0], argv[1], argv[2]));
sc->cam.setat(point(argv[3], argv[4], argv[5]));
sc->cam.setup(vector(argv[6], argv[7], argv[8]));
sc->cam.setfovy(argv[9]);
w->cam = viewTransform(Point(argv[0], argv[1], argv[2]),
Point(argv[3], argv[4], argv[5]),
Vector(argv[6], argv[7], argv[8]));
w->camFoV = deg_to_rad(argv[9]);
}
void cmdSphere (scene *sc, uint32_t curLine, char *first, float argv[15])
/* 0: X, 1: Y, 2: Z
* 3: Radius
*/
static void cmdSphere (Hw3File *w, uint32_t curLine, double argv[15])
{
printf("Adding a sphere...\n");
/* Instanciate a sphere */
sphere *sp = new sphere(point(argv[0], argv[1], argv[2]), argv[3], sc->getMatrix());
sp->ambient = sc->ambient;
sp->diffuse = sc->diffuse;
sp->specular = sc->specular;
sp->emission = sc->emission;
sp->shininess = sc->shininess;
sp->sourceLine = curLine;
sc->o[sc->o_count] = sp;
sc->o_count++;
Sphere *shape = new Sphere();
Matrix pos = translation(argv[0], argv[1], argv[2]) * scaling(argv[3], argv[3], argv[3]);
shape->setTransform(w->getTransformMatrix() * pos);
shape->material.ambient = w->currentAmbient;
shape->material.reflective = w->currentReflective;
shape->material.shininess = w->currentShininess;
shape->material.specular = w->currentSpecular;
shape->material.diffuse = w->currentDiffuse;
shape->material.colour = w->currentColour;
shape->material.transparency = w->currentTransparency;
shape->material.refractiveIndex = w->currentRefIndex;
w->addObject(shape);
}
void cmdCube (scene *sc, uint32_t curLine, char *first, float argv[15])
static void cmdCube (Hw3File *w, uint32_t curLine, double argv[15])
{
}
static void cmdTrans (Hw3File *w, uint32_t curLine, double argv[15])
{
Matrix m = translation(argv[0], argv[1], argv[2]);
w->applyTransformMatrix(m);
}
static void cmdRotate (Hw3File *w, uint32_t curLine, double argv[15])
{
Matrix m = Matrix4().identity();
if (argv[2] != 0)
{
m = m * rotationZ(argv[3]);
}
if (argv[1] != 0)
{
m = m * rotationY(argv[3]);
}
if (argv[0] != 0)
{
m = m * rotationX(argv[3]);
}
w->applyTransformMatrix(m);
}
static void cmdScale (Hw3File *w, uint32_t curLine, double argv[15])
{
Matrix m = scaling(argv[0], argv[1], argv[2]);
w->applyTransformMatrix(m);
}
static void cmdPushT (Hw3File *w, uint32_t curLine, double argv[15])
{
w->pushTransformMatrix();
}
static void cmdPopT (Hw3File *w, uint32_t curLine, double argv[15])
{
w->popTransformMatrix();
}
static void cmdLPoint (Hw3File *w, uint32_t curLine, double argv[15])
{
/* create point light */
Light *l = new Light(POINT_LIGHT, Point(argv[0], argv[1], argv[2]), Colour(argv[3], argv[4], argv[5]));
w->addLight(l);
}
static void cmdAmbient (Hw3File *w, uint32_t curLine, double argv[15])
{
//w->currentAmbient = (argv[0] + argv[1] + argv[2]) / 3;
w->currentColour = Colour(argv[0], argv[1], argv[2]);
}
static void cmdColour (Hw3File *w, uint32_t curLine, double argv[15])
{
w->currentColour = Colour(argv[0], argv[1], argv[2]);
}
static void cmdDiffuse (Hw3File *w, uint32_t curLine, double argv[15])
{
w->currentDiffuse = (argv[0] + argv[1] + argv[2]) / 3;
}
static void cmdSpecular (Hw3File *w, uint32_t curLine, double argv[15])
{
w->currentSpecular = (argv[0] + argv[1] + argv[2]) / 3;
}
static void cmdShine (Hw3File *w, uint32_t curLine, double argv[15])
{
w->currentReflective = argv[0];
}
static void cmdEmission (Hw3File *w, uint32_t curLine, double argv[15])
{
w->currentEmission = (argv[0] + argv[1] + argv[2]) / 3;
}
#if 0
void cmdMaxVerts (scene *sc, uint32_t curLine, char *first, float argv[15])
{
sc->vertexCount = (uint32_t) argv[0];
sc->vertex = new point[sc->vertexCount];
sc->curVertex = 0;
}
void cmdMaxVertN (scene *sc, uint32_t curLine, char *first, float argv[15])
{
/* ignore for now */
}
void cmdVertex (scene *sc, uint32_t curLine, char *first, float argv[15])
{
sc->vertex[sc->curVertex].set(argv[0], argv[1], argv[2]);
sc->curVertex++;
}
void cmdVertexN (scene *sc, uint32_t curLine, char *first, float argv[15])
{
/* ignore for now */
}
void cmdTri (scene *sc, uint32_t curLine, char *first, float argv[15])
{
/* arg are vertex numbers */
triangle *tr = new triangle(sc->vertex[(int)argv[0]], sc->vertex[(int)argv[1]], sc->vertex[(int)argv[2]], sc->getMatrix());
tr->ambient = sc->ambient;
tr->diffuse = sc->diffuse;
tr->specular = sc->specular;
tr->emission = sc->emission;
tr->shininess = sc->shininess;
tr->sourceLine = curLine;
sc->o[sc->o_count] = tr;
sc->o_count++;
}
void cmdTriN (scene *sc, uint32_t curLine, char *first, float argv[15])
{
/* ignore for noz */
}
#endif
void cmdTrans (scene *sc, uint32_t curLine, char *first, float argv[15])
{
matrix m;
m.translation(argv[0], argv[1], argv[2]);
sc->applyTransform(m);
}
void cmdRotate (scene *sc, uint32_t curLine, char *first, float argv[15])
{
matrix m;
vector axis;
axis.set(argv[0], argv[1], argv[2]);
m.rotation(axis, argv[3]);
sc->applyTransform(m);
}
void cmdScale (scene *sc, uint32_t curLine, char *first, float argv[15])
{
matrix m;
m.scale(argv[0], argv[1], argv[2]);
sc->applyTransform(m);
}
void cmdPushT (scene *sc, uint32_t curLine, char *first, float argv[15])
{
sc->pushMatrix();
}
void cmdPopT (scene *sc, uint32_t curLine, char *first, float argv[15])
{
sc->popMatrix();
}
#if 0
void cmdLDire (scene *sc, uint32_t curLine, char *first, float argv[15])
static void cmdLDire (Hw3File *w, uint32_t curLine, double argv[15])
{
/* create directional light */
light *cur = new light();
@@ -145,163 +163,184 @@ void cmdLDire (scene *sc, uint32_t curLine, char *first, float argv[15])
sc->l[sc->l_count] = cur;
sc->l_count++;
}
#endif
void cmdLPoint (scene *sc, uint32_t curLine, char *first, float argv[15])
static void cmdAtten (Hw3File *w, uint32_t curLine, double argv[15])
{
/* create point light */
light *cur = new light();
cur->type = LIGHT_POINT;
cur->attenuation = sc->attenuation;
cur->position.set(argv[0], argv[1], argv[2]);
cur->lightcolor.set(argv[3], argv[4], argv[5]);
sc->l[sc->l_count] = cur;
sc->l_count++;
sc->attenuation = Vector(argv[0], argv[1], argv[2]);
}
#if 0
void cmdAtten (scene *sc, uint32_t curLine, char *first, float argv[15])
static void cmdMaxVerts (Hw3File *w, uint32_t curLine, double argv[15])
{
sc->attenuation = vector(argv[0], argv[1], argv[2]);
sc->vertexCount = (uint32_t) argv[0];
sc->vertex = new point[sc->vertexCount];
sc->curVertex = 0;
}
static void cmdMaxVertN (Hw3File *w, uint32_t curLine, double argv[15])
{
/* ignore for now */
}
static void cmdVertex (Hw3File *w, uint32_t curLine, double argv[15])
{
sc->vertex[sc->curVertex].set(argv[0], argv[1], argv[2]);
sc->curVertex++;
}
static void cmdVertexN (Hw3File *w, uint32_t curLine, double argv[15])
{
/* ignore for now */
}
static void cmdTri (Hw3File *w, uint32_t curLine, double argv[15])
{
/* arg are vertex numbers */
triangle *tr = new triangle(sc->vertex[(int)argv[0]], sc->vertex[(int)argv[1]], sc->vertex[(int)argv[2]], sc->getMatrix());
tr->ambient = sc->ambient;
tr->diffuse = sc->diffuse;
tr->specular = sc->specular;
tr->emission = sc->emission;
tr->shininess = sc->shininess;
tr->sourceLine = curLine;
sc->o[sc->o_count] = tr;
sc->o_count++;
}
#endif
void cmdAmbient (scene *sc, uint32_t curLine, char *first, float argv[15])
static cmd_def commandList[] =
{
sc->ambient = color(argv[0], argv[1], argv[2]);
}
void cmdDiffuse (scene *sc, uint32_t curLine, char *first, float argv[15])
{
sc->diffuse = color(argv[0], argv[1], argv[2]);
}
void cmdSpecular (scene *sc, uint32_t curLine, char *first, float argv[15])
{
sc->specular = color(argv[0], argv[1], argv[2]);
}
void cmdShine (scene *sc, uint32_t curLine, char *first, float argv[15])
{
sc->shininess = argv[0];
}
void cmdEmission (scene *sc, uint32_t curLine, char *first, float argv[15])
{
sc->emission = color(argv[0], argv[1], argv[2]);
}
cmd_def commandList[] =
{
/* Camera */
{ "camera", 10, cmdCamera },
/* Geometry */
{ "sphere", 4, cmdSphere },
//{ "maxverts", 1, cmdMaxVerts },
//{ "maxvertnorms", 1, cmdMaxVertN },
//{ "vertex", 3, cmdVertex },
//{ "vertexnormal", 6, cmdVertexN },
//{ "tri", 3, cmdTri },
//{ "trinormal", 3, cmdTriN },
//{ "cube", 1, cmdCube },
/* transformation */
{ "translate", 3, cmdTrans },
{ "rotate", 4, cmdRotate },
{ "scale", 3, cmdScale },
{ "pushTransform", 0, cmdPushT },
{ "popTransform", 0, cmdPopT },
/* Lights */
//{ "directional", 6, cmdLDire },
{ "point", 6, cmdLPoint },
//{ "attenuation", 3, cmdAtten },
/* Materials */
{ "ambient", 3, cmdAmbient },
{ "diffuse", 3, cmdDiffuse },
{ "specular", 3, cmdSpecular },
{ "shininess", 1, cmdShine },
{ "emission", 3, cmdEmission },
};
/* Camera */
{"camera", 10, cmdCamera},
/* Geometry */
{"sphere", 4, cmdSphere},
//{ "maxverts", 1, cmdMaxVerts },
//{ "maxvertnorms", 1, cmdMaxVertN },
//{ "vertex", 3, cmdVertex },
//{ "vertexnormal", 6, cmdVertexN },
//{ "tri", 3, cmdTri },
//{ "trinormal", 3, cmdTriN },
//{ "cube", 1, cmdCube },
/* transformation */
{"translate", 3, cmdTrans},
{"rotate", 4, cmdRotate},
{"scale", 3, cmdScale},
{"pushTransform", 0, cmdPushT},
{"popTransform", 0, cmdPopT},
/* Lights */
//{ "directional", 6, cmdLDire },
{"point", 6, cmdLPoint},
//{ "attenuation", 3, cmdAtten },
/* Materials */
{"color", 3, cmdColour},
{"ambient", 3, cmdAmbient},
{"diffuse", 3, cmdDiffuse},
{"specular", 3, cmdSpecular},
{"shininess", 1, cmdShine},
{"emission", 3, cmdEmission},
};
#define CMD_COUNT (sizeof(commandList) / sizeof(cmd_def))
scene *readfile::read(char *filename)
Hw3File::Hw3File(const char *filename) : transStackCount(0), currentColour(Colour(1, 1, 1)),
currentEmission(0), currentShininess(200), currentAmbient(0.1),
currentDiffuse(0.9), currentSpecular(0.9), camFoV(0)
{
FILE *fp;
scene *sc = new scene;
if (sc != NULL)
this->popTransformMatrix();
fp = fopen(filename, "rt");
if (fp != NULL)
{
fp = fopen(filename, "rt");
if (fp == NULL)
char buffer[512];
int line = 0;
while(!feof(fp))
{
delete sc;
sc = NULL;
}
else
{
char buffer[512];
int line = 0;
while(!feof(fp))
uint32_t i;
line++;
memset(buffer, 0, 512);
fgets(buffer, 512, fp);
if ((buffer[0] == '#') || (strlen(buffer) < 2))
{
uint32_t i;
line++;
memset(buffer, 0, 512);
fgets(buffer, 512, fp);
if ((buffer[0] == '#') || (strlen(buffer) < 2))
continue; /* Ingore empty line or commented lines */
//printf("::%d:> %s", strlen(buffer), buffer);
for (i = 0; i < CMD_COUNT; i++)
continue; /* Ingore empty line or commented lines */
}
for (i = 0; i < CMD_COUNT; i++)
{
if (strncmp(buffer, commandList[i].name, strlen(commandList[i].name)) == 0)
{
if (strncmp(buffer, commandList[i].name, strlen(commandList[i].name)) == 0)
char first[512];
double value[15];
if (commandList[i].numparam != 0)
{
char first[512];
float value[15];
if (commandList[i].numparam != 0)
size_t j;
int k = 0, l = 0;
char buff[512];
for(j = strlen(commandList[i].name); j < strlen(buffer); j++)
{
size_t j;
int k = 0, l = 0;
char buff[512];
for(j = strlen(commandList[i].name); j < strlen(buffer); j++)
if (!isspace(buffer[j]))
{
if (!isspace(buffer[j]))
{
buff[l++] = buffer[j];
}
else
{
buff[l] = 0;
l = 0;
if (k == 0)
{
strcpy(first, buff);
}
if (strlen(buff) > 0)
{
value[k++] = atof(buff);
}
}
buff[l++] = buffer[j];
}
if (k != abs(commandList[i].numparam))
else
{
printf("line %d malformed: given %d parameter, expected %d\n%s", line, k, abs(commandList[i].numparam), buffer);
sc = NULL;
goto exit;
buff[l] = 0;
l = 0;
if (k == 0)
{
strcpy(first, buff);
}
if (strlen(buff) > 0)
{
value[k++] = atof(buff);
}
}
}
commandList[i].f(sc, line, first, value);
break;
if (k != abs(commandList[i].numparam))
{
printf("line %d malformed: given %d parameter, expected %d\n%s", line, k, abs(commandList[i].numparam), buffer);
goto exit;
}
}
commandList[i].f(this, line, value);
break;
}
}
#ifdef USE_OCTREE
sc->createOctree();
#endif
}
}
exit:
return sc;
return;
}
#endif
Matrix Hw3File::getTransformMatrix()
{
return this->transformStack[this->transStackCount];
}
void Hw3File::popTransformMatrix()
{
if (this->transStackCount == 0)
{
this->transformStack[0] = Matrix4().identity();
}
else
{
this->transformStack[this->transStackCount] = Matrix4().identity();
this->transStackCount --;
}
}
void Hw3File::pushTransformMatrix()
{
this->transStackCount ++;
this->transformStack[this->transStackCount] = this->transformStack[this->transStackCount - 1];
}
void Hw3File::applyTransformMatrix(Matrix t)
{
this->transformStack[this->transStackCount] = this->transformStack[this->transStackCount] * t;
}

View File

@@ -3,9 +3,9 @@ project(DoRayTested)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
set(TESTS_SRC tuple_test.cpp colour_test.cpp canvas_test.cpp matrix_test.cpp transformation_test.cpp ray_test.cpp
intersect_test.cpp sphere_test.cpp light_test.cpp material_test.cpp world_test.cpp camera_test.cpp
shape_test.cpp plane_test.cpp pattern_test.cpp)
set(TESTS_SRC math_test.cpp tuple_test.cpp colour_test.cpp canvas_test.cpp matrix_test.cpp transformation_test.cpp
ray_test.cpp intersect_test.cpp sphere_test.cpp light_test.cpp material_test.cpp world_test.cpp camera_test.cpp
shape_test.cpp plane_test.cpp pattern_test.cpp cube_test.cpp cylinder_test.cpp cone_test.cpp)
add_executable(testMyRays)
target_include_directories(testMyRays PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
@@ -19,6 +19,11 @@ gtest_discover_tests(testMyRays
)
add_executable(hw3render)
target_include_directories(hw3render PUBLIC ../source/include)
target_sources(hw3render PRIVATE hw3render.cpp)
target_link_libraries(hw3render rayonnement)
add_executable(ch5_test)
target_include_directories(ch5_test PUBLIC ../source/include)
target_sources(ch5_test PRIVATE ch5_test.cpp)
@@ -59,6 +64,21 @@ target_include_directories(ch11_test PUBLIC ../source/include)
target_sources(ch11_test PRIVATE ch11_test.cpp)
target_link_libraries(ch11_test rayonnement)
add_executable(ch12_test)
target_include_directories(ch12_test PUBLIC ../source/include)
target_sources(ch12_test PRIVATE ch12_test.cpp)
target_link_libraries(ch12_test rayonnement)
add_executable(ch13_test)
target_include_directories(ch13_test PUBLIC ../source/include)
target_sources(ch13_test PRIVATE ch13_test.cpp)
target_link_libraries(ch13_test rayonnement)
add_executable(ch13_cone)
target_include_directories(ch13_cone PUBLIC ../source/include)
target_sources(ch13_cone PRIVATE ch13_cone.cpp)
target_link_libraries(ch13_cone rayonnement)
add_test(NAME Chapter05_Test COMMAND $<TARGET_FILE:ch5_test>)
add_test(NAME Chapter06_Test COMMAND $<TARGET_FILE:ch6_test>)
add_test(NAME Chapter07_Test COMMAND $<TARGET_FILE:ch7_test>)
@@ -67,3 +87,7 @@ add_test(NAME Chapter10_Test COMMAND $<TARGET_FILE:ch10_test>)
add_test(NAME Chapter11_Reflection COMMAND $<TARGET_FILE:ch11_reflection>)
add_test(NAME Chapter11_Refraction COMMAND $<TARGET_FILE:ch11_refraction>)
add_test(NAME Chapter11_Test COMMAND $<TARGET_FILE:ch11_test>)
add_test(NAME Chapter12_Test COMMAND $<TARGET_FILE:ch12_test>)
add_test(NAME Chapter13_Test COMMAND $<TARGET_FILE:ch13_test>)
add_test(NAME Chapter13_ConeBonus COMMAND $<TARGET_FILE:ch13_cone>)
add_test(NAME Hw3Render COMMAND $<TARGET_FILE:hw3render> ${CMAKE_CURRENT_SOURCE_DIR}/test.hw3scene)

View File

@@ -79,7 +79,7 @@ int main()
w.addLight(&light);
/* Set the camera */
Camera camera = Camera(1920, 1080, M_PI / 3);
Camera camera = Camera(100, 50, M_PI / 3);
camera.setTransform(viewTransform(Point(0, 1.5, -5),
Point(0, 1, 0),
Vector(0, 1, 0)));

View File

@@ -145,7 +145,7 @@ int main()
w.addLight(&light);
/* Set the camera */
Camera camera = Camera(400, 100, 1.152);
Camera camera = Camera(400, 200, 1.152);
camera.setTransform(viewTransform(Point(-2.6, 1.5, -3.9),
Point(-0.6, 1, -0.8),
Vector(0, 1, 0)));

213
tests/ch12_test.cpp Normal file
View File

@@ -0,0 +1,213 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Render test for reflection in chapter 12.
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <world.h>
#include <light.h>
#include <sphere.h>
#include <plane.h>
#include <cube.h>
#include <material.h>
#include <colour.h>
#include <canvas.h>
#include <camera.h>
#include <pattern.h>
#include <strippattern.h>
#include <gradientpattern.h>
#include <checkerspattern.h>
#include <ringpattern.h>
#include <transformation.h>
int main()
{
World w = World();
/* Add light */
Light light = Light(POINT_LIGHT, Point(0, 6.9, -5), Colour(1, 1, 0.9));
w.addLight(&light);
/* ----------------------------- */
/* The floor / ceiling */
Cube floor = Cube();
floor.setTransform( scaling(20, 7, 20) * translation(0, 1, 0));
floor.material.pattern = new CheckersPattern(Colour(0, 0, 0), Colour(0.25, 0.25, 0.25));
floor.material.pattern->setTransform(scaling(0.07, 0.07, 0.07));
floor.material.ambient = 0.25;
floor.material.diffuse = 0.7;
floor.material.specular = 0.9;
floor.material.shininess = 300;
floor.material.reflective = 0.1;
w.addObject(&floor);
/* Walls */
Cube walls = Cube();
walls.setTransform(scaling(10, 10, 10));
walls.material.pattern = new CheckersPattern(Colour( 0.4863, 0.3765, 0.2941), Colour(0.3725, 0.2902, 0.2275 ));
walls.material.pattern->setTransform(scaling(0.05, 20, 0.05));
walls.material.ambient = 0.1;
walls.material.diffuse = 0.7;
walls.material.specular = 0.9;
walls.material.shininess = 300;
walls.material.reflective = 0.1;
w.addObject(&walls);
/* Table top */
Cube tableTop = Cube();
tableTop.setTransform(translation(0, 3.1, 0) * scaling(3, 0.1, 2));
tableTop.material.pattern = new StripPattern(Colour(0.5529, 0.4235, 0.3255), Colour(0.6588, 0.5098, 0.4000 ));
tableTop.material.pattern->setTransform(scaling(0.05, 0.05, 0.05) * rotationY(0.1));
tableTop.material.ambient = 0.1;
tableTop.material.diffuse = 0.7;
tableTop.material.specular = 0.9;
tableTop.material.shininess = 300;
tableTop.material.reflective = 0.2;
w.addObject(&tableTop);
/* Leg 1 */
Cube leg1 = Cube();
leg1.setTransform(translation(2.7, 1.5, -1.7) * scaling(0.1, 1.5, 0.1));
leg1.material.colour = Colour(0.5529, 0.4235, 0.3255);
leg1.material.ambient = 0.2;
leg1.material.diffuse = 0.7;
w.addObject(&leg1);
/* Leg 2 */
Cube leg2 = Cube();
leg2.setTransform(translation(2.7, 1.5, 1.7) * scaling(0.1, 1.5, 0.1));
leg2.material.colour = Colour(0.5529, 0.4235, 0.3255);
leg2.material.ambient = 0.2;
leg2.material.diffuse = 0.7;
w.addObject(&leg2);
/* Leg 3 */
Cube leg3 = Cube();
leg3.setTransform(translation(-2.7, 1.5, -1.7) * scaling(0.1, 1.5, 0.1));
leg3.material.colour = Colour(0.5529, 0.4235, 0.3255);
leg3.material.ambient = 0.2;
leg3.material.diffuse = 0.7;
w.addObject(&leg3);
/* Leg 4 */
Cube leg4 = Cube();
leg4.setTransform(translation(-2.7, 1.5, 1.7) * scaling(0.1, 1.5, 0.1));
leg4.material.colour = Colour(0.5529, 0.4235, 0.3255);
leg4.material.ambient = 0.2;
leg4.material.diffuse = 0.7;
w.addObject(&leg4);
/* ----------------------------- */
/* Glass cube */
Cube glassCube = Cube();
glassCube.setTransform(translation(0, 3.45001, 0) * rotationY(0.2) * scaling(0.25, 0.25, 0.25));
glassCube.dropShadow = false;
glassCube.material.colour = Colour(1, 1, 0.8);
glassCube.material.ambient = 0;
glassCube.material.diffuse = 0.3;
glassCube.material.specular = 0.9;
glassCube.material.shininess = 300;
glassCube.material.reflective = 0.7;
glassCube.material.transparency = 0.7;
glassCube.material.refractiveIndex = 1.5;
w.addObject(&glassCube);
/* Little cube 1 */
Cube lilCube1 = Cube();
lilCube1.setTransform(translation(1, 3.35, -0.9) *
rotationY(-0.4) *
scaling(0.15, 0.15, 0.15));
lilCube1.material.colour = Colour(1, 0.5, 0.5);
lilCube1.material.reflective = 0.6;
lilCube1.material.diffuse = 0.4;
w.addObject(&lilCube1);
/* Little cube 2 */
Cube lilCube2 = Cube();
lilCube2.setTransform(translation(-1.5, 3.27, 0.3) *
rotationY(0.4) *
scaling(0.15, 0.07, 0.15));
lilCube2.material.colour = Colour(1, 1, 0.5);
w.addObject(&lilCube2);
/* Little cube 3 */
Cube lilCube3 = Cube();
lilCube3.setTransform(translation(0, 3.25, 1) *
rotationY(0.4) *
scaling(0.2, 0.05, 0.05));
lilCube3.material.colour = Colour(0.5, 1, 0.5);
w.addObject(&lilCube3);
/* Little cube 4 */
Cube lilCube4 = Cube();
lilCube4.setTransform(translation(-0.6, 3.4, -1) *
rotationY(0.8) *
scaling(0.05, 0.2, 0.05));
lilCube4.material.colour = Colour(0.5, 0.5, 1);
w.addObject(&lilCube4);
/* Little cube 5 */
Cube lilCube5 = Cube();
lilCube5.setTransform(translation(2, 3.4, 1) *
rotationY(0.8) *
scaling(0.05, 0.2, 0.05));
lilCube5.material.colour = Colour(0.5, 1, 1);
w.addObject(&lilCube5);
/* ----------------------------- */
/* Frame 1 */
Cube frame1 = Cube();
frame1.setTransform(translation(-10, 4, 1) * scaling(0.05, 1, 1));
frame1.material.colour = Colour(0.7098, 0.2471, 0.2196);
frame1.material.diffuse = 0.6;
w.addObject(&frame1);
/* Frame 2 */
Cube frame2 = Cube();
frame2.setTransform(translation(-10, 3.4, 2.7) * scaling(0.05, 0.4, 0.4));
frame2.material.colour = Colour(0.2667, 0.2706, 0.6902);
frame2.material.diffuse = 0.6;
w.addObject(&frame2);
/* Frame 3 */
Cube frame3 = Cube();
frame3.setTransform(translation(-10, 4.6, 2.7) * scaling(0.05, 0.4, 0.4));
frame3.material.colour = Colour(0.3098, 0.5961, 0.3098);
frame3.material.diffuse = 0.6;
w.addObject(&frame3);
/* ----------------------------- */
/* Mirror */
Cube mirror = Cube();
mirror.setTransform(translation(-2, 3.5, 9.95) * scaling(4.8, 1.4, 0.06));
mirror.material.colour = Colour(0, 0, 0);
mirror.material.diffuse = 0;
mirror.material.ambient = 0;
mirror.material.specular = 1;
mirror.material.shininess = 300;
mirror.material.reflective = 1;
w.addObject(&mirror);
/* ----------------------------- */
/* Set the camera */
Camera camera = Camera(400, 200, 0.785);
camera.setTransform(viewTransform(Point(8, 6, -8),
Point(0, 3, 0),
Vector(0, 1, 0)));
/* Now render it */
Canvas image = camera.render(w, 4);
image.SaveAsPNG("ch12_test.png");
return 0;
}

204
tests/ch13_cone.cpp Normal file
View File

@@ -0,0 +1,204 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Render test for reflection in chapter 13.
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <world.h>
#include <light.h>
#include <sphere.h>
#include <plane.h>
#include <cube.h>
#include <cylinder.h>
#include <cone.h>
#include <material.h>
#include <colour.h>
#include <canvas.h>
#include <camera.h>
#include <pattern.h>
#include <strippattern.h>
#include <gradientpattern.h>
#include <checkerspattern.h>
#include <ringpattern.h>
#include <transformation.h>
int main()
{
World w = World();
/* Add light */
Light light = Light(POINT_LIGHT, Point(1, 6.9, -4.9), Colour(1, 1, 1));
w.addLight(&light);
/* ----------------------------- */
/* The floor */
Plane floor = Plane();
floor.material.pattern = new CheckersPattern(Colour(0.5, 0.5, 0.5), Colour(0.75, 0.75, 0.75));
floor.material.pattern->setTransform(rotationY(0.3) * scaling(0.25, 0.25, 0.25));
w.addObject(&floor);
Plane ceiling = Plane();
ceiling.material.pattern = new CheckersPattern(Colour(0.5, 0.2, 0.5), Colour(0.75, 0.4, 0.75));
ceiling.material.pattern->setTransform(rotationY(0.3));
ceiling.setTransform(translation(0, 8, 0));
w.addObject(&ceiling);
/* ----------------------------- */
Cone cyl1 = Cone();
cyl1.minCap = -1;
cyl1.maxCap = 0;
cyl1.isClosed = true;
cyl1.setTransform(translation(-1, 0, 1) * scaling(0.5, 1, 0.5) * translation(0, 1, 0));
cyl1.material.colour = Colour(0, 1, 0.6);
cyl1.material.diffuse = 0.1;
cyl1.material.specular = 0.9;
cyl1.material.shininess = 300;
cyl1.material.reflective = 0.3;
w.addObject(&cyl1);
/* ----------------------------- */
/* Concentrics */
Cylinder cons1 = Cylinder();
cons1.minCap = 0;
cons1.maxCap = 0.2;
cons1.isClosed = false;
cons1.setTransform(translation(1, 0, 0) * scaling(0.8, 1, 0.8));
cons1.material.colour = Colour(1, 1, 0.3);
cons1.material.ambient = 0.1;
cons1.material.diffuse = 0.8;
cons1.material.specular = 0.9;
cons1.material.shininess = 300;
cons1.material.reflective = 0.2;
w.addObject(&cons1);
Cylinder cons2 = Cylinder();
cons2.minCap = 0;
cons2.maxCap = 0.3;
cons2.isClosed = false;
cons2.setTransform(translation(1, 0, 0) * scaling(0.6, 1, 0.6));
cons2.material.colour = Colour(1, 0.9, 0.4);
cons2.material.ambient = 0.1;
cons2.material.diffuse = 0.8;
cons2.material.specular = 0.9;
cons2.material.shininess = 300;
cons2.material.reflective = 0.2;
w.addObject(&cons2);
Cylinder cons3 = Cylinder();
cons3.minCap = 0;
cons3.maxCap = 0.4;
cons3.isClosed = false;
cons3.setTransform(translation(1, 0, 0) * scaling(0.4, 1, 0.4));
cons3.material.colour = Colour(1, 0.8, 0.5);
cons3.material.ambient = 0.1;
cons3.material.diffuse = 0.8;
cons3.material.specular = 0.9;
cons3.material.shininess = 300;
cons3.material.reflective = 0.2;
w.addObject(&cons3);
Cylinder cons4 = Cylinder();
cons4.minCap = 0;
cons4.maxCap = 0.5;
cons4.isClosed = true;
cons4.setTransform(translation(1, 0, 0) * scaling(0.2, 1, 0.2));
cons4.material.colour = Colour(1, 0.7, 0.6);
cons4.material.ambient = 0.1;
cons4.material.diffuse = 0.8;
cons4.material.specular = 0.9;
cons4.material.shininess = 300;
cons4.material.reflective = 0.2;
w.addObject(&cons4);
/* ----------------------------- */
/* decoratives cylinders */
Cylinder deco1 = Cylinder();
deco1.minCap = 0;
deco1.maxCap = 0.3;
deco1.isClosed = true;
deco1.setTransform(translation(0, 0, -0.75) * scaling(0.05, 1, 0.05));
deco1.material.colour = Colour(1, 0, 0);
deco1.material.ambient = 0.1;
deco1.material.diffuse = 0.9;
deco1.material.specular = 0.9;
deco1.material.shininess = 300;
deco1.material.reflective = 0.5;
w.addObject(&deco1);
Cylinder deco2 = Cylinder();
deco2.minCap = 0;
deco2.maxCap = 0.3;
deco2.isClosed = true;
deco2.setTransform(translation(0, 0, -2.25) * rotationY(-0.15) * translation(0, 0, 1.5) * scaling(0.05, 1, 0.05));
deco2.material.colour = Colour(1, 1, 0);
deco2.material.ambient = 0.1;
deco2.material.diffuse = 0.9;
deco2.material.specular = 0.9;
deco2.material.shininess = 300;
deco2.material.reflective = 0.5;
w.addObject(&deco2);
Cylinder deco3 = Cylinder();
deco3.minCap = 0;
deco3.maxCap = 0.3;
deco3.isClosed = true;
deco3.setTransform(translation(0, 0, -2.25) * rotationY(-0.3) * translation(0, 0, 1.5) * scaling(0.05, 1, 0.05));
deco3.material.colour = Colour(0, 1, 0);
deco3.material.ambient = 0.1;
deco3.material.diffuse = 0.9;
deco3.material.specular = 0.9;
deco3.material.shininess = 300;
deco3.material.reflective = 0.5;
w.addObject(&deco3);
Cylinder deco4 = Cylinder();
deco4.minCap = 0;
deco4.maxCap = 0.3;
deco4.isClosed = true;
deco4.setTransform(translation(0, 0, -2.25) * rotationY(-0.45) * translation(0, 0, 1.5) * scaling(0.05, 1, 0.05));
deco4.material.colour = Colour(0, 1, 1);
deco4.material.ambient = 0.1;
deco4.material.diffuse = 0.9;
deco4.material.specular = 0.9;
deco4.material.shininess = 300;
deco4.material.reflective = 0.5;
w.addObject(&deco4);
/* ----------------------------- */
Cone glassCylinder = Cone();
glassCylinder.minCap = 0.001;
glassCylinder.maxCap = 1;
glassCylinder.isClosed = true;
glassCylinder.dropShadow = false;
glassCylinder.setTransform(translation(0, 0, -1.5) * scaling(0.33, 1, 0.33));
glassCylinder.material.colour = Colour(0.2, 0.63, 0.24);
glassCylinder.material.diffuse = 0.1;
glassCylinder.material.specular = 0.9;
glassCylinder.material.shininess = 300;
glassCylinder.material.reflective = 0.3;
w.addObject(&glassCylinder);
/* ----------------------------- */
/* Set the camera */
Camera camera = Camera(400, 200, 0.314);
camera.setTransform(viewTransform(Point(8, 3.5, -9),
Point(0, 0.3, 0),
Vector(0, 1, 0)));
/* Now render it */
Canvas image = camera.render(w, 20);
image.SaveAsPNG("ch13_cone.png");
return 0;
}

191
tests/ch13_test.cpp Normal file
View File

@@ -0,0 +1,191 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Render test for reflection in chapter 13.
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <world.h>
#include <light.h>
#include <sphere.h>
#include <plane.h>
#include <cube.h>
#include <cylinder.h>
#include <material.h>
#include <colour.h>
#include <canvas.h>
#include <camera.h>
#include <pattern.h>
#include <strippattern.h>
#include <gradientpattern.h>
#include <checkerspattern.h>
#include <ringpattern.h>
#include <transformation.h>
int main()
{
World w = World();
/* Add light */
Light light = Light(POINT_LIGHT, Point(1, 6.9, -4.9), Colour(1, 1, 1));
w.addLight(&light);
/* ----------------------------- */
/* The floor / ceiling */
Plane floor = Plane();
floor.material.pattern = new CheckersPattern(Colour(0.5, 0.5, 0.5), Colour(0.75, 0.75, 0.75));
floor.material.pattern->setTransform(rotationY(0.3) * scaling(0.25, 0.25, 0.25));
w.addObject(&floor);
/* ----------------------------- */
Cylinder cyl1 = Cylinder();
cyl1.minCap = 0;
cyl1.maxCap = 0.75;
cyl1.isClosed = true;
cyl1.setTransform(translation(-1, 0, 1) * scaling(0.5, 1, 0.5));
cyl1.material.colour = Colour(0, 0, 0.6);
cyl1.material.diffuse = 0.1;
cyl1.material.specular = 0.9;
cyl1.material.shininess = 300;
cyl1.material.reflective = 0.9;
w.addObject(&cyl1);
/* ----------------------------- */
/* Concentrics */
Cylinder cons1 = Cylinder();
cons1.minCap = 0;
cons1.maxCap = 0.2;
cons1.isClosed = false;
cons1.setTransform(translation(1, 0, 0) * scaling(0.8, 1, 0.8));
cons1.material.colour = Colour(1, 1, 0.3);
cons1.material.ambient = 0.1;
cons1.material.diffuse = 0.8;
cons1.material.specular = 0.9;
cons1.material.shininess = 300;
w.addObject(&cons1);
Cylinder cons2 = Cylinder();
cons2.minCap = 0;
cons2.maxCap = 0.3;
cons2.isClosed = false;
cons2.setTransform(translation(1, 0, 0) * scaling(0.6, 1, 0.6));
cons2.material.colour = Colour(1, 0.9, 0.4);
cons2.material.ambient = 0.1;
cons2.material.diffuse = 0.8;
cons2.material.specular = 0.9;
cons2.material.shininess = 300;
w.addObject(&cons2);
Cylinder cons3 = Cylinder();
cons3.minCap = 0;
cons3.maxCap = 0.4;
cons3.isClosed = false;
cons3.setTransform(translation(1, 0, 0) * scaling(0.4, 1, 0.4));
cons3.material.colour = Colour(1, 0.8, 0.5);
cons3.material.ambient = 0.1;
cons3.material.diffuse = 0.8;
cons3.material.specular = 0.9;
cons3.material.shininess = 300;
w.addObject(&cons3);
Cylinder cons4 = Cylinder();
cons4.minCap = 0;
cons4.maxCap = 0.5;
cons4.isClosed = true;
cons4.setTransform(translation(1, 0, 0) * scaling(0.2, 1, 0.2));
cons4.material.colour = Colour(1, 0.7, 0.6);
cons4.material.ambient = 0.1;
cons4.material.diffuse = 0.8;
cons4.material.specular = 0.9;
cons4.material.shininess = 300;
w.addObject(&cons4);
/* ----------------------------- */
/* decoratives cylinders */
Cylinder deco1 = Cylinder();
deco1.minCap = 0;
deco1.maxCap = 0.3;
deco1.isClosed = true;
deco1.setTransform(translation(0, 0, -0.75) * scaling(0.05, 1, 0.05));
deco1.material.colour = Colour(1, 0, 0);
deco1.material.ambient = 0.1;
deco1.material.diffuse = 0.9;
deco1.material.specular = 0.9;
deco1.material.shininess = 300;
w.addObject(&deco1);
Cylinder deco2 = Cylinder();
deco2.minCap = 0;
deco2.maxCap = 0.3;
deco2.isClosed = true;
deco2.setTransform(translation(0, 0, -2.25) * rotationY(-0.15) * translation(0, 0, 1.5) * scaling(0.05, 1, 0.05));
deco2.material.colour = Colour(1, 1, 0);
deco2.material.ambient = 0.1;
deco2.material.diffuse = 0.9;
deco2.material.specular = 0.9;
deco2.material.shininess = 300;
w.addObject(&deco2);
Cylinder deco3 = Cylinder();
deco3.minCap = 0;
deco3.maxCap = 0.3;
deco3.isClosed = true;
deco3.setTransform(translation(0, 0, -2.25) * rotationY(-0.3) * translation(0, 0, 1.5) * scaling(0.05, 1, 0.05));
deco3.material.colour = Colour(0, 1, 0);
deco3.material.ambient = 0.1;
deco3.material.diffuse = 0.9;
deco3.material.specular = 0.9;
deco3.material.shininess = 300;
w.addObject(&deco3);
Cylinder deco4 = Cylinder();
deco4.minCap = 0;
deco4.maxCap = 0.3;
deco4.isClosed = true;
deco4.setTransform(translation(0, 0, -2.25) * rotationY(-0.45) * translation(0, 0, 1.5) * scaling(0.05, 1, 0.05));
deco4.material.colour = Colour(0, 1, 1);
deco4.material.ambient = 0.1;
deco4.material.diffuse = 0.9;
deco4.material.specular = 0.9;
deco4.material.shininess = 300;
w.addObject(&deco4);
/* ----------------------------- */
/* glass cylinder */
Cylinder glassCylinder = Cylinder();
glassCylinder.minCap = 0.0001;
glassCylinder.maxCap = 0.5;
glassCylinder.isClosed = true;
glassCylinder.setTransform(translation(0, 0, -1.5) * scaling(0.33, 1, 0.33));
glassCylinder.material.colour = Colour(0.25, 0, 0);
glassCylinder.material.diffuse = 0.1;
glassCylinder.material.specular = 0.9;
glassCylinder.material.shininess = 300;
glassCylinder.material.reflective = 0.9;
glassCylinder.material.transparency = 0.9;
glassCylinder.material.refractiveIndex = 1.5;
w.addObject(&glassCylinder);
/* ----------------------------- */
/* Set the camera */
Camera camera = Camera(400, 200, 0.314);
camera.setTransform(viewTransform(Point(8, 3.5, -9),
Point(0, 0.3, 0),
Vector(0, 1, 0)));
/* Now render it */
Canvas image = camera.render(w, 20);
image.SaveAsPNG("ch13_test.png");
return 0;
}

132
tests/cone_test.cpp Normal file
View File

@@ -0,0 +1,132 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Cone unit tests
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <intersect.h>
#include <intersection.h>
#include <cone.h>
#include <transformation.h>
#include <gtest/gtest.h>
class ConeTest : public Cone
{
public:
Tuple doLocalNormalAt(Tuple point)
{
return localNormalAt(point);
}
};
TEST(ConeTest, Intersecting_a_cone_with_a_ray)
{
Cone cone = Cone();
Point Origins[] = {
Point(0, 0, -5),
Point(0, 0, -5),
Point(1, 1, -5),
};
Vector Directions[] = {
Vector(0, 0, 1),
Vector(1, 1, 1),
Vector(-0.5, -1, 1),
};
double t0s[] = { 5, 8.66025, 4.55006 };
double t1s[] = { 5, 8.66025, 49.44994 };
int i;
for(i = 0; i < 3; i++)
{
Tuple direction = Directions[i].normalise();
Ray r = Ray(Origins[i], direction);
Intersect xs = cone.intersect(r);
/* Temporary lower the precision */
set_equal_precision(0.00001);
ASSERT_EQ(xs.count(), 2);
EXPECT_TRUE(double_equal(xs[0].t, t0s[i]));
EXPECT_TRUE(double_equal(xs[1].t, t1s[i]));
set_equal_precision(FLT_EPSILON);
}
}
TEST(ConeTest, Intersecting_a_cone_with_a_ray_parall_to_one_of_its_halves)
{
Cone cone = Cone();
Tuple direction = Vector(0, 1, 1).normalise();
Ray r = Ray(Point(0, 0, -1), direction);
Intersect xs = cone.intersect(r);
ASSERT_EQ(xs.count(), 1);
/* Temporary lower the precision */
set_equal_precision(0.00001);
ASSERT_TRUE(double_equal(xs[0].t, 0.35355));
set_equal_precision(FLT_EPSILON);
}
TEST(ConeTest, Intersecting_a_cone_end_cap)
{
Point Origins[] = {
Point(0, 0, -5),
Point(0, 0, -0.25),
Point(0, 0, -0.25),
};
Vector Directions[] = {
Vector(0, 1, 0),
Vector(0, 1, 1),
Vector(0, 1, 0),
};
uint32_t Counts[] = { 0, 2, 4 };
Cone cone = Cone();
cone.minCap = -0.5;
cone.maxCap = 0.5;
cone.isClosed = true;
int i;
for(i = 0; i < 3; i++)
{
Tuple direction = Directions[i].normalise();
Ray r = Ray(Origins[i], direction);
Intersect xs = cone.intersect(r);
ASSERT_EQ(xs.count(), Counts[i]);
}
}
TEST(ConeTest, Computing_the_normal_vector_on_a_cone)
{
ConeTest cone = ConeTest();
Point HitPointss[] = {
Point(0, 0, 0),
Point(1, 1, 1),
Point(-1, -1, 0),
};
Vector Normals[] = {
Vector(0, 0, 0),
Vector(1, -sqrt(2), 1),
Vector(-1, 1, 0),
};
int i;
for(i = 0; i < 3; i++)
{
ASSERT_EQ(cone.doLocalNormalAt(HitPointss[i]), Normals[i]);
}
}

117
tests/cube_test.cpp Normal file
View File

@@ -0,0 +1,117 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Cube unit tests
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <intersect.h>
#include <intersection.h>
#include <cube.h>
#include <transformation.h>
#include <gtest/gtest.h>
TEST(CubeTest, A_ray_intersects_a_cube)
{
Cube c = Cube();
Point Origins[] = {
Point(5, 0.5, 0),
Point(-5, 0.5, 0),
Point(0.5, 5, 0),
Point(0.5, -5, 0),
Point(0.5, 0, 5),
Point(0.5, 0, -5),
Point(0, 0.5, 0),
};
Vector Directions[] = {
Vector(-1, 0, 0),
Vector(1, 0, 0),
Vector(0, -1, 0),
Vector(0, 1, 0),
Vector(0, 0, -1),
Vector(0, 0, 1),
Vector(0, 0, 1),
};
double t1[] = { 4, 4, 4, 4, 4, 4, -1 };
double t2[] = { 6, 6, 6, 6, 6, 6, 1 };
int i;
for(i = 0; i < 7; i++)
{
Ray r = Ray(Origins[i], Directions[i]);
Intersect xs = c.intersect(r);
ASSERT_EQ(xs.count(), 2);
EXPECT_EQ(xs[0].t, t1[i]);
EXPECT_EQ(xs[1].t, t2[i]);
}
}
TEST(CubeTest, A_ray_miss_a_cube)
{
Cube c = Cube();
Point Origins[] = {
Point(-2, 0, 0),
Point(0, -2, 0),
Point(0, 0, -2),
Point(2, 0, 2),
Point(0, 2, 2),
Point(2, 2, 0),
};
Vector Directions[] = {
Vector(0.2673, 0.5345, 0.8018),
Vector(0.8018, 0.2673, 0.5345),
Vector(0.5345, 0.8018, 0.2673),
Vector(0, 0, -1),
Vector(0, -1, 0),
Vector(-1, 0, 0),
};
int i;
for(i = 0; i < 6; i++)
{
Ray r = Ray(Origins[i], Directions[i]);
Intersect xs = c.intersect(r);
ASSERT_EQ(xs.count(), 0);
}
}
TEST(CubeTest, The_normal_on_the_surface_of_a_cube)
{
Cube c = Cube();
Point HitPoints[] = {
Point(1, 0.5, -0.8),
Point(-1, -0.2, 0.9),
Point(-0.4, 1, -0.1),
Point(0.3, -1, -0.7),
Point(-0.6, 0.3, 1),
Point(0.4, 0.4, -1),
Point(1, 1, 1),
Point(-1, -1, -1),
};
Vector ExpectedNormals[] = {
Vector(1, 0, 0),
Vector(-1, 0, 0),
Vector(0, 1, 0),
Vector(0, -1, 0),
Vector(0, 0, 1),
Vector(0, 0, -1),
Vector(1, 0, 0),
Vector(-1, 0, 0),
};
int i;
for(i = 0; i < 8; i++)
{
ASSERT_EQ(c.normalAt(HitPoints[i]), ExpectedNormals[i]);
}
}

223
tests/cylinder_test.cpp Normal file
View File

@@ -0,0 +1,223 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Cylinder unit tests
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <intersect.h>
#include <intersection.h>
#include <cylinder.h>
#include <transformation.h>
#include <gtest/gtest.h>
TEST(CylinderTest, A_ray_miss_a_cylinder)
{
Cylinder cyl = Cylinder();
Point Origins[] = {
Point(1, 0, 0),
Point(0, 0, 0),
Point(0, 0, -5),
};
Vector Directions[] = {
Vector(0, 1, 0),
Vector(0, 1, 0),
Vector(1, 1, 1),
};
int i;
for(i = 0; i < 3; i++)
{
Tuple direction = Directions[i].normalise();
Ray r = Ray(Origins[i], direction);
Intersect xs = cyl.intersect(r);
ASSERT_EQ(xs.count(), 0);
}
}
TEST(CylinderTest, A_ray_hit_a_cylinder)
{
Cylinder cyl = Cylinder();
Point Origins[] = {
Point(1, 0, -5),
Point(0, 0, -5),
Point(0.5, 0, -5),
};
Vector Directions[] = {
Vector(0, 0, 1),
Vector(0, 0, 1),
Vector(0.1, 1, 1),
};
double t0s[] = { 5, 4, 6.80798 };
double t1s[] = { 5, 6, 7.08872 };
int i, j;
for(i = 0; i < 3; i++)
{
Tuple direction = Directions[i].normalise();
Ray r = Ray(Origins[i], direction);
Intersect xs = cyl.intersect(r);
/* Temporary lower the precision */
set_equal_precision(0.00001);
ASSERT_EQ(xs.count(), 2);
EXPECT_TRUE(double_equal(xs[0].t, t0s[i]));
EXPECT_TRUE(double_equal(xs[1].t, t1s[i]));
set_equal_precision(FLT_EPSILON);
}
}
TEST(CylinderTest, Normal_vector_on_a_cylinder)
{
Cylinder cyl = Cylinder();
Point HitPointss[] = {
Point(1, 0, 0),
Point(0, 5, -1),
Point(0, -2, 1),
Point(-1, 1, 0),
};
Vector Normals[] = {
Vector(1, 0, 0),
Vector(0, 0, -1),
Vector(0, 0, 1),
Vector(-1, 0, 0),
};
int i;
for(i = 0; i < 4; i++)
{
ASSERT_EQ(cyl.normalAt(HitPointss[i]), Normals[i]);
}
}
TEST(CylinderTest, The_default_minimum_and_maximum_for_a_cylinder)
{
Cylinder cyl = Cylinder();
ASSERT_EQ(cyl.minCap, -INFINITY);
ASSERT_EQ(cyl.maxCap, INFINITY);
}
TEST(CylinderTest, Intersecting_a_constrained_cylinder)
{
Point Origins[] = {
Point(0, 1.5, 0),
Point(0, 3, -5),
Point(0, 0, -5),
Point(0, 2, -5),
Point(0, 1, -5),
Point(0, 1.5, -2),
};
Vector Directions[] = {
Vector(0.1, 1, 0),
Vector(0, 0, 1),
Vector(0, 0, 1),
Vector(0, 0, 1),
Vector(0, 0, 1),
Vector(0., 0, 1),
};
uint32_t Counts[] = { 0, 0, 0, 0, 0, 2 };
Cylinder cyl = Cylinder();
cyl.minCap = 1;
cyl.maxCap = 2;
int i;
for(i = 0; i < 6; i++)
{
Tuple direction = Directions[i].normalise();
Ray r = Ray(Origins[i], direction);
Intersect xs = cyl.intersect(r);
ASSERT_EQ(xs.count(), Counts[i]);
}
}
TEST(CylinderTest, The_default_closed_value_for_a_cylinder)
{
Cylinder cyl = Cylinder();
ASSERT_EQ(cyl.isClosed, false);
}
TEST(CylinderTest, Intersecting_the_caps_of_a_close_cylinder)
{
Point Origins[] = {
Point(0, 3, 0),
Point(0, 3, -2),
Point(0, 4, -2), /* Edge case */
Point(0, 0, -5),
Point(0, -1, -2), /* Edge case */
};
Vector Directions[] = {
Vector(0, -1, 0),
Vector(0, -1, 2),
Vector(0, -1, 1),
Vector(0, 1, 2),
Vector(0, 1, 1),
};
uint32_t Counts[] = { 2, 2, 2, 2, 2 };
Cylinder cyl = Cylinder();
cyl.minCap = 1;
cyl.maxCap = 2;
cyl.isClosed = true;
int i;
for(i = 0; i < 5; i++)
{
Tuple direction = Directions[i].normalise();
Ray r = Ray(Origins[i], direction);
Intersect xs = cyl.intersect(r);
ASSERT_EQ(xs.count(), Counts[i]);
}
}
TEST(CylinderTest, The_normal_on_a_cylinder_end_cap)
{
Cylinder cyl = Cylinder();
Point HitPointss[] = {
Point(0, 1, 0),
Point(0.5, 1, 0),
Point(0, 1, 0.5),
Point(0, 2, 0),
Point(0.5, 2, 0),
Point(0, 2, 0.5),
};
Vector Normals[] = {
Vector(0, -1, 0),
Vector(0, -1, 0),
Vector(0, -1, 0),
Vector(0, 1, 0),
Vector(0, 1, 0),
Vector(0, 1, 0),
};
cyl.minCap = 1;
cyl.maxCap = 2;
cyl.isClosed = true;
int idx;
for(idx = 0; idx < 6; idx++)
{
ASSERT_EQ(cyl.normalAt(HitPointss[idx]), Normals[idx]);
}
}

42
tests/hw3render.cpp Normal file
View File

@@ -0,0 +1,42 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Renderer using hw3 files as world builder.
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <stdio.h>
#include <world.h>
#include <worldbuilder.h>
#include <light.h>
#include <sphere.h>
#include <material.h>
#include <colour.h>
#include <canvas.h>
#include <camera.h>
#include <transformation.h>
int main(int argc, char *argv[])
{
if(argc != 2)
{
printf("usage: %s file.hw3\n", argv[0]);
return -1;
}
Hw3File world = Hw3File(argv[1]);
/* Set the camera resolution */
Camera cam = Camera(640, 480, world.camFoV);
cam.setTransform(world.cam);
/* Now render it */
Canvas image = cam.render(world);
image.SaveAsPNG("hw3render.png");
return 0;
}

View File

@@ -121,4 +121,47 @@ TEST(MaterialTest, Transparency_and_refractive_index_for_the_default_material)
ASSERT_EQ(m.transparency, 0.0);
ASSERT_EQ(m.refractiveIndex, 1.0);
}
TEST(MaterialTest, Equality_tests)
{
Material m = Material();
Material m2 = Material();
ASSERT_EQ(m, m2);
m.ambient = 42;
ASSERT_NE(m, m2);
m.ambient = m2.ambient;
m.diffuse = 42;
ASSERT_NE(m, m2);
m.diffuse = m2.diffuse;
m.specular = 42;
ASSERT_NE(m, m2);
m.specular = m2.specular;
m.shininess = 42;
ASSERT_NE(m, m2);
m.shininess = m2.shininess;
m.reflective = 42;
ASSERT_NE(m, m2);
m.reflective = m2.reflective;
m.transparency = 42;
ASSERT_NE(m, m2);
m.transparency = m2.transparency;
m.emissive = 42;
ASSERT_NE(m, m2);
m.emissive = m2.emissive;
m.refractiveIndex = 42;
ASSERT_NE(m, m2);
m.refractiveIndex = m2.refractiveIndex;
m.colour = Colour(32, 32, 32);
ASSERT_NE(m, m2);
}

97
tests/math_test.cpp Normal file
View File

@@ -0,0 +1,97 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Math helper unit tests
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <math_helper.h>
#include <gtest/gtest.h>
TEST(MathTest, Default_epsilon)
{
ASSERT_EQ(getEpsilon(), FLT_EPSILON);
}
TEST(MathTest, Check_setting_epsilon)
{
set_equal_precision(0.00001);
ASSERT_EQ(getEpsilon(), 0.00001);
set_equal_precision(FLT_EPSILON);
}
TEST(MathTest, Check_double_equal_is_working)
{
ASSERT_TRUE(double_equal(0 - DBL_EPSILON, 0));
ASSERT_FALSE(double_equal(0 - FLT_EPSILON, 0));
ASSERT_TRUE(double_equal(0 - FLT_EPSILON/2.0, 0));
ASSERT_FALSE(double_equal(0 - 0.001, 0));
ASSERT_FALSE(double_equal(0 - 0.00001, 0));
ASSERT_FALSE(double_equal(0 - 0.000001, 0));
set_equal_precision(0.00001);
ASSERT_TRUE(double_equal(0 - FLT_EPSILON*2, 0));
ASSERT_FALSE(double_equal(0 - 0.001, 0));
ASSERT_FALSE(double_equal(0 - 0.00001, 0));
ASSERT_TRUE(double_equal(0 - 0.000001, 0));
set_equal_precision(FLT_EPSILON);
}
TEST(MathTest, Check_that_deg_to_rad_is_working)
{
double angle180 = deg_to_rad(180);
double angle90 = deg_to_rad(90);
double angle270 = deg_to_rad(270);
ASSERT_EQ(angle180, M_PI);
ASSERT_EQ(angle90, M_PI / 2.);
ASSERT_EQ(angle270, M_PI * 1.5);
}
TEST(MathTest, Min_is_working)
{
ASSERT_EQ(min3(1, 2, 3), 1);
ASSERT_EQ(min3(1, 3, 2), 1);
ASSERT_EQ(min3(2, 1, 3), 1);
ASSERT_EQ(min3(2, 3, 1), 1);
ASSERT_EQ(min3(3, 1, 2), 1);
ASSERT_EQ(min3(3, 2, 1), 1);
ASSERT_EQ(min3(1, 2, 2), 1);
ASSERT_EQ(min3(2, 1, 2), 1);
ASSERT_EQ(min3(2, 2, 1), 1);
ASSERT_EQ(min3(3, 2, 2), 2);
ASSERT_EQ(min3(2, 3, 2), 2);
ASSERT_EQ(min3(2, 2, 3), 2);
ASSERT_EQ(min3(2, 2, 2), 2);
}
TEST(MathTest, Max_is_working)
{
ASSERT_EQ(max3(1, 2, 3), 3);
ASSERT_EQ(max3(1, 3, 2), 3);
ASSERT_EQ(max3(2, 1, 3), 3);
ASSERT_EQ(max3(2, 3, 1), 3);
ASSERT_EQ(max3(3, 1, 2), 3);
ASSERT_EQ(max3(3, 2, 1), 3);
ASSERT_EQ(max3(1, 2, 2), 2);
ASSERT_EQ(max3(2, 1, 2), 2);
ASSERT_EQ(max3(2, 2, 1), 2);
ASSERT_EQ(max3(3, 2, 2), 3);
ASSERT_EQ(max3(2, 3, 2), 3);
ASSERT_EQ(max3(2, 2, 3), 3);
ASSERT_EQ(max3(2, 2, 2), 2);
}

9
tests/test.hw3scene Normal file
View File

@@ -0,0 +1,9 @@
# A ball lit by 3 lights
camera 0 0 -1 0 0 0 0 1 0 45
point 0 4 0 .7 .2 .2
#point 4 0 4 .5 .5 .5
point 4 -4 0 .2 .7 .2
point -4 -4 0 .2 .2 .7
sphere 0 0 0 .3

View File

@@ -245,14 +245,4 @@ TEST(TransformationTest, An_arbitrary_view_transformation)
set_equal_precision(FLT_EPSILON);
}
TEST(TransformationTest, Check_that_deg_to_rad_is_working)
{
double angle180 = deg_to_rad(180);
double angle90 = deg_to_rad(90);
double angle270 = deg_to_rad(270);
ASSERT_EQ(angle180, M_PI);
ASSERT_EQ(angle90, M_PI / 2.);
ASSERT_EQ(angle270, M_PI * 1.5);
}

View File

@@ -22,6 +22,22 @@ TEST(TupleTest, Tuple_With_w_equal_1_and_is_point)
ASSERT_FALSE(a.isVector());
}
TEST(TupleTest, Two_tuples_are_equal)
{
Tuple a = Tuple(1, 2, 3, 4);
Tuple b = Tuple(1, 2, 3, 4);
Tuple c = Tuple(4, 3, 2, 1);
Tuple d = Tuple(1, 2, 3, 5);
Tuple e = Tuple(1, 2, 5, 5);
Tuple f = Tuple(1, 5, 5, 5);
ASSERT_EQ(a, b);
ASSERT_NE(a, c);
ASSERT_NE(a, d);
ASSERT_NE(a, e);
ASSERT_NE(a, f);
}
TEST(TupleTest, Tuple_With_w_equal_0_and_is_vector)
{
Tuple a = Tuple(4.3, -4.2, 3.1, 0.0);