19 Commits

Author SHA1 Message Date
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
Godzil
51a6bbebb9 Refraction is fully there, with magic fresnel! 2020-02-22 01:27:48 +00:00
Godzil
e45dbad59e Added some proper test scenes for chapter 11. 2020-02-22 00:50:55 +00:00
Godzil
3db0aaaeac Refraction seems to work. Still need to do a nice scene. 2020-02-21 22:50:12 +00:00
Godzil
df52cb36db Working on refraction & transparency.
Lots of work left to do!
2020-02-21 18:59:14 +00:00
Godzil
89dd74fa7c Finally! We have reflections! 2020-02-21 17:39:45 +00:00
Godzil
7337ae4837 Finally! We have reflections! 2020-02-21 17:21:06 +00:00
Godzil
9fffb68026 Remove nanogui dependencies for now. If the need of a gui come, will add back but for now it just add unnecessary checkout time 2020-02-21 12:08:54 +00:00
Godzil
7687581e83 Finishing touch for patterns! 2020-02-21 12:05:30 +00:00
Godzil
75cf59cc1a Adding support for pattern.
Still a bit more work to be done there.
2020-02-21 09:36:34 +00:00
Godzil
9d0db6a635 Added planes! 2020-02-21 00:26:48 +00:00
Godzil
66c1582a5f Shape is now an abstract class and can't be instanciated.
Change derived shape to only deal with local calculation they don't need anymore to deal with how they've been transformed.
2020-02-21 00:02:30 +00:00
Godzil
2a8fe61388 Working on adding test for the shape object. 2020-02-20 18:06:29 +00:00
61 changed files with 2955 additions and 311 deletions

3
.gitmodules vendored
View File

@@ -1,9 +1,6 @@
[submodule "external/googletest"]
path = external/googletest
url = https://github.com/google/googletest.git
[submodule "external/nanogui"]
path = external/nanogui
url = https://github.com/Godzil/nanogui.git
[submodule "external/glfw"]
path = external/glfw
url = https://github.com/glfw/glfw.git

View File

@@ -28,4 +28,24 @@ From Chapter 07:
From Chapter 08:
![Chapter 8 rendering test](output/ch8_test.png)
![Chapter 8 rendering test](output/ch8_test.png)
From Chapter 09:
![Chapter 9 rendering test](output/ch9_test.png)
From Chapter 10:
![Chapter 10 rendering test](output/ch10_test.png)
From Chapter 11:
![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)
From Chapter 12:
![Chapter 12 rendering test](output/ch12_test.png)

1
external/nanogui vendored

Submodule external/nanogui deleted from 16bc6b1d3a

BIN
output/ch10_test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

BIN
output/ch11_reflection.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

BIN
output/ch11_refraction.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

BIN
output/ch11_test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

BIN
output/ch8_test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
output/ch9_test.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

View File

@@ -3,12 +3,12 @@
# First most is build as a library
add_library(rayonnement STATIC)
file(GLOB RAY_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
file(GLOB RAY_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h ${CMAKE_CURRENT_SOURCE_DIR}/pattern/*.h)
file(GLOB RAY_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/shapes/*.cpp
${CMAKE_CURRENT_SOURCE_DIR}/worldbuilder/*.cpp)
target_include_directories(rayonnement PUBLIC include)
target_include_directories(rayonnement PUBLIC include pattern)
target_sources(rayonnement PRIVATE ${RAY_HEADERS} ${RAY_SOURCES})
target_link_libraries(rayonnement LodePNG)

View File

@@ -55,7 +55,7 @@ Ray Camera::rayForPixel(uint32_t pixelX, uint32_t pixelY)
return Ray(origin, direction);
}
Canvas Camera::render(World world)
Canvas Camera::render(World world, uint32_t depth)
{
uint32_t x, y;
Canvas image = Canvas(this->horizontalSize, this->verticalSize);
@@ -65,7 +65,7 @@ Canvas Camera::render(World world)
for(x = 0; x < this->horizontalSize; x++)
{
Ray r = this->rayForPixel(x, y);
Tuple colour = world.colourAt(r);
Tuple colour = world.colourAt(r, depth);
image.putPixel(x, y, colour);
}
}

View File

@@ -32,7 +32,7 @@ public:
Camera(uint32_t hsize, uint32_t vsize, double fov);
void setTransform(Matrix transform);
Ray rayForPixel(uint32_t pixelX, uint32_t pixelY);
Canvas render(World w);
Canvas render(World w, uint32_t depth = 5);
};
#endif /* DORAYME_CAMERA_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 */

View File

@@ -13,18 +13,53 @@
#include <ray.h>
class Shape;
class Intersect;
struct Computation
{
Computation(Shape *object, double t, Tuple point, Tuple eyev, Tuple normalv, Tuple overHitP, bool inside) :
object(object), t(t), hitPoint(point), eyeVector(eyev), normalVector(normalv), inside(inside), overHitPoint(overHitP) { };
Computation(Shape *object, double t, Tuple point, Tuple eyev, Tuple normalv, Tuple overHitP,
bool inside, Tuple reflectV = Vector(0, 0, 0), double n1 = 1.0, double n2 = 1.0,
Tuple underHitP = Point(0, 0, 0)) :
object(object), t(t), hitPoint(point), eyeVector(eyev), normalVector(normalv), inside(inside),
overHitPoint(overHitP), underHitPoint(underHitP), reflectVector(reflectV), n1(n1), n2(n2) { };
double schlick()
{
/* Find the cos of the angle betzeen the eye and normal vector */
double cos = this->eyeVector.dot(this->normalVector);
double r0;
/* Total internal reflection can only occur when n1 > n2 */
if (this->n1 > this->n2)
{
double n, sin2_t;
n = this->n1 / this->n2;
sin2_t = (n * n) * (1.0 - (cos * cos));
if (sin2_t > 1.0)
{
return 1.0;
}
/* Compute the cos of theta */
cos = sqrt(1.0 - sin2_t);
}
r0 = ((this->n1 - this->n2) / (this->n1 + this->n2));
r0 = r0 * r0;
return r0 + (1 - r0) * ((1 - cos)*(1 - cos)*(1 - cos)*(1 - cos)*(1 - cos));
};
Shape *object;
double t;
Tuple hitPoint;
Tuple overHitPoint;
Tuple underHitPoint;
Tuple eyeVector;
Tuple normalVector;
Tuple reflectVector;
double n1;
double n2;
bool inside;
};
@@ -39,7 +74,7 @@ public:
Intersection(double t, Shape *object) : t(t), object(object) { };
bool nothing() { return (this->object == nullptr); };
Computation prepareComputation(Ray r);
Computation prepareComputation(Ray r, Intersect *xs = nullptr);
bool operator==(const Intersection &b) const { return ((this->t == b.t) && (this->object == b.object)); };
};

112
source/include/list.h Normal file
View File

@@ -0,0 +1,112 @@
/*
* DoRayMe - a quick and dirty Raytracer
* List header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_LIST_H
#define DORAYME_LIST_H
#include <shape.h>
struct ChainList
{
Shape *shape;
ChainList *next;
};
class List
{
private:
ChainList *head;
ChainList *tail;
uint32_t count;
public:
List() : head(nullptr), tail(nullptr), count(0) { };
~List()
{
ChainList *p = this->head;
if (p == nullptr) { return; }
/* clear up the list */
}
Shape *last()
{
ChainList *p = this->tail;
if (p == nullptr) { return nullptr; }
return p->shape;
}
void remove(Shape *s)
{
ChainList *p = this->head;
if (p == nullptr) { return; }
if ((p->next == nullptr) && (p->shape == s))
{
/* First element */
this->tail = nullptr;
free(this->head);
this->head = nullptr;
this->count = 0;
return;
}
while(p->next != nullptr)
{
if (p->next->shape == s)
{
ChainList *found = p->next;
p->next = p->next->next;
free(found);
if (p->next == NULL) { this->tail = p; }
this->count --;
return;
}
p = p->next;
}
}
void append(Shape *s)
{
ChainList *theNew = (ChainList *)calloc(1, sizeof(ChainList));
theNew->shape = s;
ChainList *p = this->tail;
this->tail = theNew;
if (p != nullptr) { p->next = theNew; }
else { this->head = theNew; } /* If the tail is empty, it mean the list IS empty. */
this->count ++;
}
bool isEmpty()
{
return (this->count == 0);
}
bool doesInclude(Shape *s)
{
ChainList *p = this->head;
while(p != nullptr)
{
if (p->shape == s) { return true; }
p = p->next;
}
return false;
}
};
#endif //DORAYME_LIST_H

View File

@@ -11,8 +11,11 @@
#include <tuple.h>
#include <colour.h>
#include <pattern.h>
#include <light.h>
class Shape;
class Material
{
public:
@@ -21,17 +24,29 @@ public:
double diffuse;
double specular;
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) {};
Material() : colour(Colour(1, 1, 1)), ambient(0.1), diffuse(0.9), specular(0.9), shininess(200),
reflective(0.0), transparency(0.0), emissive(0), refractiveIndex(1.0), pattern(nullptr) {};
Colour lighting(Light light, Tuple point, Tuple eyeVector, Tuple normalVector, bool inShadow = false);
Colour lighting(Light light, Tuple point, Tuple eyeVector, Tuple normalVector, Shape *hitObject, bool inShadow = false);
bool operator==(const Material &b) const { return double_equal(this->ambient, b.ambient) &&
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); };
};
#endif /* DORAYME_MATERIAL_H */

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 */

36
source/include/pattern.h Normal file
View File

@@ -0,0 +1,36 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Pattern header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_PATTERN_H
#define DORAYME_PATTERN_H
#include <colour.h>
#include <tuple.h>
#include <matrix.h>
class Shape;
class Pattern
{
public:
Colour a;
Colour b;
Matrix transformMatrix;
Matrix inverseTransform;
public:
Pattern(Colour a, Colour b);
virtual Colour patternAt(Tuple point) = 0;
void setTransform(Matrix transform);
Colour patternAtObject(Shape *object, Tuple point);
};
#endif /* DORAYME_PATTERN_H */

22
source/include/plane.h Normal file
View File

@@ -0,0 +1,22 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Plane header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_PLANE_H
#define DORAYME_PLANE_H
class Plane : public Shape
{
private:
Intersect localIntersect(Ray r);
Tuple localNormalAt(Tuple point);
public:
Plane() : Shape(SHAPE_PLANE) { };
};
#endif //DORAYME_PLANE_H

View File

@@ -21,6 +21,9 @@ enum ShapeType
{
SHAPE_NONE,
SHAPE_SPHERE,
SHAPE_PLANE,
SHAPE_CUBE,
SHAPE_CONE,
};
/* Base class for all object that can be presented in the world */
@@ -29,16 +32,21 @@ class Shape
private:
ShapeType type;
private:
virtual Intersect localIntersect(Ray r) = 0;
virtual Tuple localNormalAt(Tuple point) = 0;
public:
Matrix transformMatrix;
Matrix inverseTransform;
Material material;
bool dropShadow;
public:
Shape(ShapeType = SHAPE_NONE);
virtual Intersect intersect(Ray r);
virtual Tuple normalAt(Tuple point);
Intersect intersect(Ray r);
Tuple normalAt(Tuple point);
void setTransform(Matrix transform);
void setMaterial(Material material) { this->material = material; };

View File

@@ -15,11 +15,20 @@
class Sphere : public Shape
{
private:
Intersect localIntersect(Ray r);
Tuple localNormalAt(Tuple point);
public:
Sphere() : Shape(SHAPE_SPHERE) { };
/* All sphere are at (0, 0, 0) and radius 1 in the object space */
virtual Intersect intersect(Ray r);
virtual Tuple normalAt(Tuple point);
};
/* Mostly for test purposes */
class GlassSphere : public Sphere
{
public:
GlassSphere() : Sphere() { this->material.transparency = 1.0; this->material.refractiveIndex = 1.5; };
};
#endif /* DORAYME_SPHERE_H */

View File

@@ -0,0 +1,28 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Test shape header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_TESTSHAPE_H
#define DORAYME_TESTSHAPE_H
#include <shape.h>
#include <ray.h>
#include <tuple.h>
class TestShape : public Shape
{
private:
Intersect localIntersect(Ray r);
Tuple localNormalAt(Tuple point);
public:
Ray localRay;
TestShape();
};
#endif //DORAYME_TESTSHAPE_H

View File

@@ -40,10 +40,14 @@ public:
bool objectIsIn(Shape &s);
Shape *getObject(int i) { return this->objectList[i]; };
Light *getLight(int i) { return this->lightList[i]; };
Tuple shadeHit(Computation comps);;
Tuple colourAt(Ray r);
bool isShadowed(Tuple point);
Tuple shadeHit(Computation comps, uint32_t depthCount = 4);
Tuple colourAt(Ray r, uint32_t depthCount = 4);
bool isShadowed(Tuple point, uint32_t light = 0);
Colour reflectColour(Computation comps, uint32_t depthCount = 4);
Colour refractedColour(Computation comps, uint32_t depthCount = 4);
Intersect intersect(Ray r);

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

@@ -8,9 +8,13 @@
*/
#include <intersection.h>
#include <shape.h>
#include <list.h>
Computation Intersection::prepareComputation(Ray r)
Computation Intersection::prepareComputation(Ray r, Intersect *xs)
{
double n1 = 1.0;
double n2 = 1.0;
Tuple hitP = r.position(this->t);
Tuple normalV = this->object->normalAt(hitP);
Tuple eyeV = -r.direction;
@@ -23,6 +27,46 @@ Computation Intersection::prepareComputation(Ray r)
}
Tuple overHitP = hitP + normalV * getEpsilon();
Tuple underHitP = hitP - normalV * getEpsilon();
Tuple reflectV = r.direction.reflect(normalV);
if (xs != nullptr)
{
List containers;
int j, k;
for(j = 0; j < xs->count(); j++)
{
Intersection i = (*xs)[j];
if (*this == i)
{
if (!containers.isEmpty())
{
n1 = containers.last()->material.refractiveIndex;
}
}
if (containers.doesInclude(i.object))
{
containers.remove(i.object);
}
else
{
containers.append(i.object);
}
if (*this == i)
{
if (!containers.isEmpty())
{
n2 = containers.last()->material.refractiveIndex;
}
/* End the loop */
break;
}
}
}
return Computation(this->object,
this->t,
@@ -30,5 +74,9 @@ Computation Intersection::prepareComputation(Ray r)
eyeV,
normalV,
overHitP,
inside);
inside,
reflectV,
n1,
n2,
underHitP);
}

View File

@@ -31,4 +31,42 @@ 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;
}
if (c <= a)
{
if (b < c) return b;
}
return c;
}
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;
}
if (c >= a)
{
if (b > c) return b;
}
return c;
}

31
source/pattern.cpp Normal file
View File

@@ -0,0 +1,31 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Pattern implementation
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <pattern.h>
#include <shape.h>
Pattern::Pattern(Colour a, Colour b): a(a), b(b)
{
this->transformMatrix = Matrix4().identity();
this->inverseTransform = this->transformMatrix.inverse();
};
Colour Pattern::patternAtObject(Shape *object, Tuple worldPoint)
{
Tuple objectPoint = object->inverseTransform * worldPoint;
Tuple patternPoint = this->inverseTransform * objectPoint;
return this->patternAt(patternPoint);
}
void Pattern::setTransform(Matrix transform)
{
this->transformMatrix = transform;
this->inverseTransform = transform.inverse();
}

View File

@@ -0,0 +1,25 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Checkers Pattern header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_CHECKERSPATTERN_H
#define DORAYME_CHECKERSPATTERN_H
class CheckersPattern : public Pattern
{
public:
CheckersPattern(Colour a, Colour b) : Pattern(a, b) { };
Colour patternAt(Tuple point)
{
double value = floor(point.x) + floor(point.y) + floor(point.z);
return (fmod(value, 2) == 0)?this->a:this->b;
}
};
#endif /* DORAYME_CHECKERSPATTERN_H */

View File

@@ -0,0 +1,30 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Gradient Pattern header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_GRADIENTPATTERN_H
#define DORAYME_GRADIENTPATTERN_H
#include <pattern.h>
class GradientPattern : public Pattern
{
public:
GradientPattern(Colour a, Colour b) : Pattern(a, b) { };
Colour patternAt(Tuple point)
{
Tuple distance = this->b - this->a;
double fraction = point.x - floor(point.x);
Tuple ret = this->a + distance * fraction;
return Colour(ret.x, ret.y, ret.z);
}
};
#endif /* DORAYME_GRADIENTPATTERN_H */

View File

@@ -0,0 +1,30 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Ring Pattern header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_RINGSUPPORT_H
#define DORAYME_RINGSUPPORT_H
#include <pattern.h>
class RingPattern : public Pattern
{
public:
RingPattern(Colour a, Colour b) : Pattern(a, b) { };
Colour patternAt(Tuple point)
{
double squared = (point.x * point.x) + (point.z * point.z);
double value = floor(sqrt(squared));
return (fmod(value, 2) == 0)?this->a:this->b;
}
};
#endif /* DORAYME_RINGSUPPORT_H */

View File

@@ -0,0 +1,32 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Strip Pattern header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_STRIPPATTERN_H
#define DORAYME_STRIPPATTERN_H
#include <pattern.h>
#include <stdio.h>
class StripPattern : public Pattern
{
public:
StripPattern(Colour a, Colour b) : Pattern(a, b) { };
Colour patternAt(Tuple point)
{
if (fmod(floor(point.x), 2) == 0)
{
return this->a;
}
return this->b;
}
};
#endif /* DORAYME_STRIPPATTERN_H */

View File

@@ -0,0 +1,27 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Strip Pattern header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_TESTPATTERN_H
#define DORAYME_TESTPATTERN_H
#include <pattern.h>
#include <stdio.h>
class TestPattern : public Pattern
{
public:
TestPattern() : Pattern(Colour(0, 0, 0), Colour(1, 1, 1)) { };
Colour patternAt(Tuple point)
{
return Colour(point.x, point.y, point.z);
}
};
#endif /* DORAYME_TESTPATTERN_H */

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);
}

View File

@@ -9,14 +9,23 @@
#include <tuple.h>
#include <material.h>
#include <colour.h>
#include <shape.h>
Colour Material::lighting(Light light, Tuple point, Tuple eyeVector, Tuple normalVector, bool inShadow)
Colour Material::lighting(Light light, Tuple point, Tuple eyeVector, Tuple normalVector, Shape *hitObject, bool inShadow)
{
Colour pointColor = this->colour;
if (this->pattern != nullptr)
{
pointColor = this->pattern->patternAtObject(hitObject, point);
}
Tuple lightVector = (light.position - point).normalise();
Tuple reflectVector = Tuple(0, 0, 0, 0);
Tuple effectiveColour = this->colour * light.intensity;
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);
@@ -25,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);
@@ -52,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);
}

36
source/shapes/plane.cpp Normal file
View File

@@ -0,0 +1,36 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Plane implementation
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <tuple.h>
#include <ray.h>
#include <shape.h>
#include <plane.h>
#include <math_helper.h>
Intersect Plane::localIntersect(Ray r)
{
double t;
Intersect ret = Intersect();
if (fabs(r.direction.y) < getEpsilon())
{
/* With a direction == 0, the ray can't intersect the plane */
return ret;
}
t = -r.origin.y / r.direction.y;
ret.add(Intersection(t, this));
return ret;
}
Tuple Plane::localNormalAt(Tuple point)
{
return Vector(0, 1, 0);
}

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();
@@ -22,12 +23,21 @@ Shape::Shape(ShapeType type)
Intersect Shape::intersect(Ray r)
{
return Intersect();
return this->localIntersect(this->invTransform(r));
};
Tuple Shape::normalAt(Tuple point)
{
return Vector(0, 0, 0);
Tuple local_point = this->inverseTransform * point;
Tuple local_normal = this->localNormalAt(local_point);
Tuple world_normal = this->inverseTransform.transpose() * local_normal;
/* W may get wrong, so hack it. This is perfectly normal as we are using a 4x4 matrix instead of a 3x3 */
world_normal.w = 0;
return world_normal.normalise();
}
void Shape::setTransform(Matrix transform)

View File

@@ -13,17 +13,15 @@
#include <tuple.h>
#include <intersect.h>
Intersect Sphere::intersect(Ray r)
Intersect Sphere::localIntersect(Ray r)
{
Intersect ret;
double a, b, c, discriminant;
Ray transRay = this->invTransform(r);
Tuple sphere_to_ray = r.origin - Point(0, 0, 0);
Tuple sphere_to_ray = transRay.origin - Point(0, 0, 0);
a = transRay.direction.dot(transRay.direction);
b = 2 * transRay.direction.dot(sphere_to_ray);
a = r.direction.dot(r.direction);
b = 2 * r.direction.dot(sphere_to_ray);
c = sphere_to_ray.dot(sphere_to_ray) - 1;
discriminant = b * b - 4 * a * c;
@@ -37,14 +35,7 @@ Intersect Sphere::intersect(Ray r)
return ret;
}
Tuple Sphere::normalAt(Tuple point)
Tuple Sphere::localNormalAt(Tuple point)
{
Tuple object_point = this->inverseTransform * point;
Tuple object_normal = (object_point - Point(0, 0, 0)).normalise();
Tuple world_normal = this->inverseTransform.transpose() * object_normal;
/* W may get wrong, so hack it. This is perfectly normal as we are using a 4x4 matrix instead of a 3x3 */
world_normal.w = 0;
return world_normal.normalise();
return (point - Point(0, 0, 0)).normalise();
}

View File

@@ -0,0 +1,25 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Test shape implementation
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <shape.h>
#include <testshape.h>
TestShape::TestShape() : localRay(Point(0, 0, 0), Vector(0, 0, 0))
{
}
Intersect TestShape::localIntersect(Ray r)
{
this->localRay = r;
return Intersect();
}
Tuple TestShape::localNormalAt(Tuple point)
{
return Vector(point.x, point.y, point.z);
}

View File

@@ -92,19 +92,36 @@ Intersect World::intersect(Ray r)
return ret;
}
Tuple World::shadeHit(Computation comps)
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);
return comps.object->material.lighting(*this->lightList[0], comps.overHitPoint, comps.eyeVector,
comps.normalVector, 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);
if ((comps.object->material.reflective > 0) && (comps.object->material.transparency > 0))
{
double reflectance = comps.schlick();
return surface + reflected * reflectance + refracted * (1 - reflectance);
}
return surface + reflected + refracted;
}
Tuple World::colourAt(Ray r)
Tuple World::colourAt(Ray r, uint32_t depthCount)
{
Intersection hit = this->intersect(r).hit();
Intersect allHits = this->intersect(r);
Intersection hit = allHits.hit();
if (hit.nothing())
{
@@ -112,25 +129,67 @@ Tuple World::colourAt(Ray r)
}
else
{
return this->shadeHit(hit.prepareComputation(r));
return this->shadeHit(hit.prepareComputation(r, &allHits), 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;
}
Colour World::reflectColour(Computation comps, uint32_t depthCount)
{
if ((depthCount == 0) || (comps.object->material.reflective == 0))
{
return Colour(0, 0, 0);
}
/* So it is reflective, even just a bit. Let'sr reflect the ray! */
Ray reflectedRay = Ray(comps.overHitPoint, comps.reflectVector);
Tuple hitColour = this->colourAt(reflectedRay, depthCount - 1);
hitColour = hitColour * comps.object->material.reflective;
return Colour(hitColour.x, hitColour.y, hitColour.z);
}
Colour World::refractedColour(Computation comps, uint32_t depthCount)
{
double nRatio = comps.n1 / comps.n2;
double cos_i = comps.eyeVector.dot(comps.normalVector);
double sin2_t = (nRatio*nRatio) * (1 - cos_i * cos_i);
if ((sin2_t > 1 ) || (depthCount == 0) || (comps.object->material.transparency == 0))
{
return Colour(0, 0, 0);
}
double cos_t = sqrt(1.0 - sin2_t);
Tuple direction = comps.normalVector * (nRatio * cos_i - cos_t) - comps.eyeVector * nRatio;
Ray refractedRay = Ray(comps.underHitPoint, direction);
Tuple hitColour = this->colourAt(refractedRay, depthCount - 1) * comps.object->material.transparency;
return Colour(hitColour.x, hitColour.y, hitColour.z);
}

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,8 +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)
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)
add_executable(testMyRays)
target_include_directories(testMyRays PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
@@ -18,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)
@@ -29,10 +35,46 @@ target_sources(ch6_test PRIVATE ch6_test.cpp)
target_link_libraries(ch6_test rayonnement)
add_executable(ch7_test)
target_include_directories(ch6_test PUBLIC ../source/include)
target_include_directories(ch7_test PUBLIC ../source/include)
target_sources(ch7_test PRIVATE ch7_test.cpp)
target_link_libraries(ch7_test rayonnement)
add_executable(ch9_test)
target_include_directories(ch9_test PUBLIC ../source/include)
target_sources(ch9_test PRIVATE ch9_test.cpp)
target_link_libraries(ch9_test rayonnement)
add_executable(ch10_test)
target_include_directories(ch10_test PUBLIC ../source/include)
target_sources(ch10_test PRIVATE ch10_test.cpp)
target_link_libraries(ch10_test rayonnement)
add_executable(ch11_reflection)
target_include_directories(ch11_reflection PUBLIC ../source/include)
target_sources(ch11_reflection PRIVATE ch11_reflection.cpp)
target_link_libraries(ch11_reflection rayonnement)
add_executable(ch11_refraction)
target_include_directories(ch11_refraction PUBLIC ../source/include)
target_sources(ch11_refraction PRIVATE ch11_refraction.cpp)
target_link_libraries(ch11_refraction rayonnement)
add_executable(ch11_test)
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_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>)
add_test(NAME Chapter07_Test COMMAND $<TARGET_FILE:ch7_test>)
add_test(NAME Chapter09_Test COMMAND $<TARGET_FILE:ch9_test>)
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>)

94
tests/ch10_test.cpp Normal file
View File

@@ -0,0 +1,94 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Render test for chapter 10
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <world.h>
#include <light.h>
#include <sphere.h>
#include <plane.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()
{
/* First we need to construct the world */
Plane floor = Plane();
floor.material.specular = 0;
floor.material.pattern = new RingPattern(Colour(1, 0.9, 0.9), Colour(1, 0.2, 0.2));
Plane wall = Plane();
wall.material.specular = 0;
wall.material.pattern = new StripPattern(Colour(1, 0.9, 0.9), Colour(1, 0.2, 0.2));
wall.material.pattern->setTransform(translation(0, 0, 1) * rotationY(M_PI/4));
wall.setTransform(translation(0, 0, 5) * rotationX(M_PI/2));
Sphere middle = Sphere();
middle.setTransform(translation(-0.7, 1, 0.6));
middle.material.diffuse = 0.7;
middle.material.specular = 0.3;
middle.material.pattern = new StripPattern(Colour(0.1, 1, 0.5), Colour(0, 0.2, 0.2));
middle.material.pattern->setTransform((rotationZ(M_PI/4) * rotationY(M_PI/5) * scaling(0.2, 0.2, 0.2)));
Sphere right = Sphere();
right.setTransform(translation(1.5, 0.5, -0.5) * scaling(0.5, 0.5, 0.5));
right.material.diffuse = 0.7;
right.material.specular = 0.3;
right.material.pattern = new StripPattern(Colour(0.5, 1, 0.1), Colour(0, 0, 0));
right.material.pattern->setTransform((scaling(0.1, 0.1, 0.1)));
Sphere left = Sphere();
left.setTransform(translation(-1.5, 0.33, -0.75) * scaling(0.33, 0.33, 0.33));
left.material.diffuse = 0.7;
left.material.specular = 0.3;
left.material.pattern = new GradientPattern(Colour(1, 0.8, 0.1), Colour(0.1, 0.1, 1));
left.material.pattern->setTransform(translation(1.5, 0, 0) * scaling(2.1, 2, 2) * rotationY(-M_PI/4));
Sphere fourth = Sphere();
fourth.setTransform(translation(.5, 0.25, 0.4) * scaling(0.3, 0.3, 0.3));
fourth.material.diffuse = 0.7;
fourth.material.specular = 0.3;
fourth.material.pattern = new CheckersPattern(Colour(0.1, 0.8, 0.1), Colour(0.8, 1, 0.8));
fourth.material.pattern->setTransform( scaling(0.2, 0.2, 0.2));
World w = World();
w.addObject(&floor);
w.addObject(&wall);
w.addObject(&middle);
w.addObject(&left);
w.addObject(&right);
w.addObject(&fourth);
/* Add light */
Light light = Light(POINT_LIGHT, Point(-10, 10, -10), Colour(1, 1, 1));
w.addLight(&light);
/* Set the camera */
Camera camera = Camera(100, 50, M_PI / 3);
camera.setTransform(viewTransform(Point(0, 1.5, -5),
Point(0, 1, 0),
Vector(0, 1, 0)));
/* Now render it */
Canvas image = camera.render(w);
image.SaveAsPNG("ch10_test.png");
return 0;
}

117
tests/ch11_reflection.cpp Normal file
View File

@@ -0,0 +1,117 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Render test for reflection in chapter 11.
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <world.h>
#include <light.h>
#include <sphere.h>
#include <plane.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()
{
/* First we need to construct the world */
Plane floor = Plane();
floor.material.specular = 0;
floor.material.pattern = new RingPattern(Colour(1, 0.9, 0.9), Colour(1, 0.2, 0.2));
floor.material.reflective = 0.1;
Plane wall = Plane();
wall.material.specular = 0;
wall.material.pattern = new StripPattern(Colour(1, 0.9, 0.9), Colour(1, 0.2, 0.2));
wall.material.pattern->setTransform(translation(0, 0, 1) * rotationY(M_PI/4));
wall.setTransform(translation(0, 0, 5) * rotationX(M_PI/2));
Sphere middle = Sphere();
middle.setTransform(translation(-0.7, 1, 0.6));
middle.material.diffuse = 0.7;
middle.material.specular = 0.3;
middle.material.pattern = new StripPattern(Colour(0.1, 1, 0.5), Colour(0, 0.2, 0.2));
middle.material.pattern->setTransform((rotationZ(M_PI/4) * rotationY(M_PI/5) * scaling(0.2, 0.2, 0.2)));
Sphere right = Sphere();
right.setTransform(translation(1.5, 0.5, -0.5) * scaling(0.5, 0.5, 0.5));
right.material.diffuse = 0.7;
right.material.specular = 0.3;
right.material.pattern = new StripPattern(Colour(0.5, 1, 0.1), Colour(0, 0, 0));
right.material.pattern->setTransform((scaling(0.1, 0.1, 0.1)));
right.material.reflective = 0.1;
Sphere left = Sphere();
left.setTransform(translation(-1.5, 0.33, -0.75) * scaling(0.33, 0.33, 0.33));
left.material.diffuse = 0.7;
left.material.specular = 0.3;
left.material.pattern = new GradientPattern(Colour(1, 0.8, 0.1), Colour(0.1, 0.1, 1));
left.material.pattern->setTransform(translation(1.5, 0, 0) * scaling(2.1, 2, 2) * rotationY(-M_PI/4));
Sphere fourth = Sphere();
fourth.setTransform(translation(.5, 0.25, 0.4) * scaling(0.3, 0.3, 0.3));
fourth.material.diffuse = 0.7;
fourth.material.specular = 0.3;
fourth.material.pattern = new CheckersPattern(Colour(0.1, 0.8, 0.1), Colour(0.8, 1, 0.8));
fourth.material.pattern->setTransform( scaling(0.2, 0.2, 0.2));
fourth.material.reflective = 0.4;
World w = World();
w.addObject(&floor);
w.addObject(&wall);
w.addObject(&middle);
w.addObject(&left);
w.addObject(&right);
w.addObject(&fourth);
/* Add some more reflective spheres */
Sphere ref1 = Sphere();
ref1.setTransform(translation(1, 1, .4) * scaling(0.2, 0.2, 0.2));
ref1.material.reflective = 1;
ref1.material.colour = Colour(0.3, 0.7, 0.6);
w.addObject(&ref1);
Sphere ref2 = Sphere();
ref2.setTransform(translation(1.5, 2, -.8) * scaling(0.2, 0.2, 0.2));
ref2.material.reflective = 1;
ref2.material.specular = 0.5;
ref2.material.colour = Colour(0.3, 0.3, 0.3);
w.addObject(&ref2);
Sphere ref3 = Sphere();
ref3.setTransform(translation(-2, 1.678, .4) * scaling(0.4, 0.4, 0.4));
ref3.material.reflective = 1;
ref3.material.specular = 0.5;
w.addObject(&ref3);
/* Add light */
Light light = Light(POINT_LIGHT, Point(-10, 10, -10), Colour(1, 1, 1));
w.addLight(&light);
/* Set the camera */
Camera camera = Camera(100, 50, M_PI / 3);
camera.setTransform(viewTransform(Point(0, 1.5, -5),
Point(0, 1, 0),
Vector(0, 1, 0)));
/* Now render it */
Canvas image = camera.render(w);
image.SaveAsPNG("ch11_reflection.png");
return 0;
}

71
tests/ch11_refraction.cpp Normal file
View File

@@ -0,0 +1,71 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Render test for reflection in chapter 11.
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <world.h>
#include <light.h>
#include <sphere.h>
#include <plane.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();
/* First we need to construct the world */
Plane floor = Plane();
floor.material.pattern = new CheckersPattern(Colour(1, 1, 1), Colour(0, 0, 0));
floor.setTransform(translation(0, -10, 0));
w.addObject(&floor);
Sphere glassBall = Sphere();
glassBall.material.shininess = 300;
glassBall.material.transparency = 1;
glassBall.material.reflective = 1;
glassBall.material.refractiveIndex = 1.52;
glassBall.material.diffuse = 0.1;
w.addObject(&glassBall);
Sphere airBall = Sphere();
airBall.setTransform(scaling(0.5, 0.5, 0.5));
airBall.material.shininess = 300;
airBall.material.transparency = 1;
airBall.material.reflective = 1;
airBall.material.refractiveIndex = 1.0009;
airBall.material.diffuse = 0.1;
w.addObject(&airBall);
/* Add light */
Light light = Light(POINT_LIGHT, Point(20, 10, 0), Colour(0.7, 0.7, 0.7));
w.addLight(&light);
/* Set the camera */
Camera camera = Camera(100, 100, M_PI / 3);
camera.setTransform(viewTransform(Point(0, 2.5, 0),
Point(0, 0, 0),
Vector(1, 0, 0)));
/* Now render it */
Canvas image = camera.render(w);
image.SaveAsPNG("ch11_refraction.png");
return 0;
}

160
tests/ch11_test.cpp Normal file
View File

@@ -0,0 +1,160 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Render test for reflection in chapter 11.
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <world.h>
#include <light.h>
#include <sphere.h>
#include <plane.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();
Material wallMaterial = Material();
wallMaterial.pattern = new StripPattern(Colour(0.45, 0.45, 0.45), Colour(0.55, 0.55, 0.55));
wallMaterial.pattern->setTransform( scaling(0.25, 0.25, 0.25) * rotationY(1.5708));
wallMaterial.ambient = 0;
wallMaterial.diffuse = 0.4;
wallMaterial.specular = 0;
wallMaterial.reflective = 0.3;
/* ----------------------------- */
/* The flood */
Plane floor = Plane();
floor.setTransform(rotationY(0.31415));
floor.material.pattern = new CheckersPattern(Colour(0.35, 0.35, 0.35), Colour(0.65, 0.65, 0.65));
floor.material.specular = 0;
floor.material.reflective = 0.4;
w.addObject(&floor);
/* the ceiling */
Plane ceiling = Plane();
ceiling.setTransform(translation(0, 5, 0));
ceiling.material.colour = Colour(0.8, 0.8, 0.8);
ceiling.material.ambient = 0.3;
ceiling.material.specular = 0;
w.addObject(&ceiling);
/* West wall */
Plane westWall = Plane();
westWall.setTransform( translation(-5, 0, 0) * rotationZ(1.5708) * rotationY(1.5708));
westWall.setMaterial(wallMaterial);
w.addObject(&westWall);
/* east wall */
Plane eastWall = Plane();
eastWall.setTransform( translation(5, 0, 0) * rotationZ(1.5708) * rotationY(1.5708));
eastWall.setMaterial(wallMaterial);
w.addObject(&eastWall);
/* north wall */
Plane northWall = Plane();
northWall.setTransform( translation(0, 0, 5) * rotationX(1.5708));
northWall.setMaterial(wallMaterial);
w.addObject(&northWall);
/* south wall */
Plane southWall = Plane();
southWall.setTransform( translation(0, 0, -5) * rotationX(1.5708));
southWall.setMaterial(wallMaterial);
w.addObject(&southWall);
/* ----------------------------- */
/* Background balls */
Sphere bg1 = Sphere();
bg1.setTransform(translation(4.6, 0.4, 1) * scaling(0.4, 0.4, 0.4));
bg1.material.colour = Colour(0.8, 0.5, 0.3);
bg1.material.shininess = 50;
w.addObject(&bg1);
Sphere bg2 = Sphere();
bg2.setTransform(translation(4.7, 0.3, 0.4) * scaling(0.3, 0.3, 0.3));
bg2.material.colour = Colour(0.9, 0.4, 0.5);
bg2.material.shininess = 50;
w.addObject(&bg2);
Sphere bg3 = Sphere();
bg3.setTransform(translation(-1, 0.5, 4.5) * scaling(0.5, 0.5, 0.5));
bg3.material.colour = Colour(0.4, 0.9, 0.6);
bg3.material.shininess = 50;
w.addObject(&bg3);
Sphere bg4 = Sphere();
bg4.setTransform(translation(-1.7, 0.3, 4.7) * scaling(0.3, 0.3, 0.3));
bg4.material.colour = Colour(0.4, 0.6, 0.9);
bg4.material.shininess = 50;
w.addObject(&bg4);
/* Forground balls */
/* Red sphere */
Sphere redBall = Sphere();
redBall.setTransform(translation(-0.6, 1, 0.6));
redBall.material.colour = Colour(1, 0.3, 0.2);
redBall.material.shininess = 5;
redBall.material.specular = 0.4;
w.addObject(&redBall);
/* blue glass ball */
Sphere blueGlassBall = Sphere();
blueGlassBall.setTransform(translation(0.6, 0.7, -0.6) * scaling(0.7, 0.7, 0.7));
blueGlassBall.material.colour = Colour(0, 0, 0.2);
blueGlassBall.material.ambient = 0;
blueGlassBall.material.diffuse = 0.4;
blueGlassBall.material.specular = 0.9;
blueGlassBall.material.shininess = 300;
blueGlassBall.material.transparency = 0.9;
blueGlassBall.material.refractiveIndex = 1.5;
w.addObject(&blueGlassBall);
/* green glass ball */
Sphere greenGlassBall = Sphere();
greenGlassBall.setTransform(translation(-0.7, 0.5, -0.8) * scaling(0.5, 0.5, 0.5));
greenGlassBall.material.colour = Colour(0, 0.2, 0);
greenGlassBall.material.ambient = 0;
greenGlassBall.material.diffuse = 0.4;
greenGlassBall.material.specular = 0.9;
greenGlassBall.material.shininess = 300;
greenGlassBall.material.transparency = 0.9;
greenGlassBall.material.refractiveIndex = 1.5;
w.addObject(&greenGlassBall);
/* ----------------------------- */
/* Add light */
Light light = Light(POINT_LIGHT, Point(-4.9, 4.9, -1), Colour(1, 1, 1));
w.addLight(&light);
/* Set the camera */
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)));
/* Now render it */
Canvas image = camera.render(w);
image.SaveAsPNG("ch11_test.png");
return 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.25, -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;
}

View File

@@ -1,6 +1,6 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Render test for chapter 5 "Put it together".
* Render test for chapter 6 "Put it together".
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
@@ -45,7 +45,7 @@ int main()
Tuple hitPoint = r.position(hit.t);
Tuple hitNormalVector = hit.object->normalAt(hitPoint);
Tuple eye = -r.direction;
Colour pixelColour = hit.object->material.lighting(light, hitPoint, eye, hitNormalVector);
Colour pixelColour = hit.object->material.lighting(light, hitPoint, eye, hitNormalVector, hit.object);
c.putPixel(x, y, pixelColour);
}

View File

@@ -1,6 +1,6 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Render test for chapter 5 "Put it together".
* Render test for chapter 7 "Put it together".
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.

69
tests/ch9_test.cpp Normal file
View File

@@ -0,0 +1,69 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Render test for chapter 9.
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <world.h>
#include <light.h>
#include <sphere.h>
#include <plane.h>
#include <material.h>
#include <colour.h>
#include <canvas.h>
#include <camera.h>
#include <transformation.h>
int main()
{
/* First we need to construct the world */
Plane floor = Plane();
floor.material.colour = Colour(1, 0.9, 0.9);
floor.material.specular = 0;
Sphere middle = Sphere();
middle.setTransform(translation(-0.5, 1, 0.5));
middle.material.colour = Colour(0.1, 1, 0.5);
middle.material.diffuse = 0.7;
middle.material.specular = 0.3;
Sphere right = Sphere();
right.setTransform(translation(1.5, 0.5, -0.5) * scaling(0.5, 0.5, 0.5));
right.material.colour = Colour(0.5, 1, 0.1);
right.material.diffuse = 0.7;
right.material.specular = 0.3;
Sphere left = Sphere();
left.setTransform(translation(-1.5, 0.33, -0.75) * scaling(0.33, 0.33, 0.33));
left.material.colour = Colour(1, 0.8, 0.1);
left.material.diffuse = 0.7;
left.material.specular = 0.3;
World w = World();
w.addObject(&floor);
w.addObject(&middle);
w.addObject(&left);
w.addObject(&right);
/* Add light */
Light light = Light(POINT_LIGHT, Point(-10, 10, -10), Colour(1, 1, 1));
w.addLight(&light);
/* Set the camera */
Camera camera = Camera(100, 50, M_PI / 3);
camera.setTransform(viewTransform(Point(0, 1.5, -5),
Point(0, 1, 0),
Vector(0, 1, 0)));
/* Now render it */
Canvas image = camera.render(w);
image.SaveAsPNG("ch9_test.png");
return 0;
}

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]);
}
}

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

@@ -9,6 +9,7 @@
#include <intersect.h>
#include <intersection.h>
#include <sphere.h>
#include <plane.h>
#include <transformation.h>
#include <gtest/gtest.h>
@@ -189,4 +190,116 @@ TEST(IntersectTest, The_hit_should_offset_the_point)
ASSERT_LT(comps.overHitPoint.z, -getEpsilon() / 2);
ASSERT_GT(comps.hitPoint.z, comps.overHitPoint.z);
}
TEST(IntersectTest, Precomputing_the_reflection_vector)
{
Plane s = Plane();
Ray r = Ray(Point(0, 1, -1), Vector(0, -sqrt(2) / 2, sqrt(2) / 2));
Intersection i = Intersection(sqrt(2), &s);
Computation comps = i.prepareComputation(r);
ASSERT_EQ(comps.reflectVector, Vector(0, sqrt(2) / 2, sqrt(2) / 2));
}
TEST(IntersectTest, Finding_n1_and_n2_at_various_intersections)
{
int i;
double n1_res[6] = { 1.0, 1.5, 2.0, 2.5, 2.5, 1.5 };
double n2_res[6] = { 1.5, 2.0, 2.5, 2.5, 1.5, 1.0 };
GlassSphere A = GlassSphere();
A.setTransform(scaling(2, 2, 2));
A.material.refractiveIndex = 1.5;
GlassSphere B = GlassSphere();
B.setTransform(translation(0, 0, -0.25));
B.material.refractiveIndex = 2.0;
GlassSphere C = GlassSphere();
C.setTransform(translation(0, 0, 0.25));
C.material.refractiveIndex = 2.5;
Ray r = Ray(Point(0, 0, -4), Vector(0, 0, 1));
Intersect xs = Intersect();
xs.add(Intersection(2.0, &A));
xs.add(Intersection(2.75, &B));
xs.add(Intersection(3.25, &C));
xs.add(Intersection(4.75, &B));
xs.add(Intersection(5.25, &C));
xs.add(Intersection(6, &A));
for(i = 0; i < xs.count(); i++)
{
Intersection inter = xs[i];
Computation comps = inter.prepareComputation(r, &xs);
ASSERT_EQ(comps.n1, n1_res[i]);
ASSERT_EQ(comps.n2, n2_res[i]);
}
}
TEST(IntersectTest, The_under_point_is_offset_below_the_surface)
{
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
GlassSphere shape = GlassSphere();
shape.setTransform(translation(0, 0, 1));
Intersection i = Intersection(5, &shape);
Intersect xs = Intersect();
xs.add(i);
Computation comps = i.prepareComputation(r, &xs);
ASSERT_TRUE(double_equal(comps.underHitPoint.z, getEpsilon() / 2));
ASSERT_LT(comps.hitPoint.z, comps.underHitPoint.z);
}
TEST(IntersectTest, The_Schlick_approximation_under_total_internal_reflection)
{
GlassSphere shape = GlassSphere();
Ray r = Ray(Point(0, 0, sqrt(2)/2), Vector(0, 1, 0));
Intersect xs = Intersect();
xs.add(Intersection(-sqrt(2)/2, &shape));
xs.add(Intersection(sqrt(2)/2, &shape));
Computation comps = xs[1].prepareComputation(r, &xs);
double reflectance = comps.schlick();
ASSERT_EQ(reflectance, 1.0);
}
TEST(IntersectTest, The_Schlick_approximation_with_a_perpendicular_viewing_angle)
{
GlassSphere shape = GlassSphere();
Ray r = Ray(Point(0, 0, 0), Vector(0, 1, 0));
Intersect xs = Intersect();
xs.add(Intersection(-1, &shape));
xs.add(Intersection(1, &shape));
Computation comps = xs[1].prepareComputation(r, &xs);
double reflectance = comps.schlick();
ASSERT_TRUE(double_equal(reflectance, 0.04));
}
TEST(IntersectTest, The_Schlick_approximation_with_small_angle_and_n2_gt_n1)
{
GlassSphere shape = GlassSphere();
Ray r = Ray(Point(0, 0.99, -2), Vector(0, 0, 1));
Intersect xs = Intersect();
xs.add(Intersection(1.8589, &shape));
Computation comps = xs[0].prepareComputation(r, &xs);
double reflectance = comps.schlick();
/* Temporary lower the precision */
set_equal_precision(0.00001);
ASSERT_TRUE(double_equal(reflectance, 0.48873));
set_equal_precision(FLT_EPSILON);
}

View File

@@ -9,6 +9,7 @@
#include <material.h>
#include <math.h>
#include <colour.h>
#include <testshape.h>
#include <gtest/gtest.h>
TEST(MaterialTest, The_default_material)
@@ -28,33 +29,36 @@ static Point position = Point(0, 0, 0);
TEST(MaterialTest, Lighting_with_the_eye_between_the_light_and_the_surface)
{
TestShape t = TestShape();
Vector eyev = Vector(0, 0, -1);
Vector normalv = Vector(0, 0, -1);
Light light = Light(POINT_LIGHT, Point(0, 0, -10), Colour(1, 1, 1));
Colour result = m.lighting(light, position, eyev, normalv);
Colour result = m.lighting(light, position, eyev, normalv, &t);
ASSERT_EQ(result, Colour(1.9, 1.9, 1.9));
}
TEST(MaterialTest, Lighting_with_the_eye_between_the_light_and_the_surface_eye_offset_by_45)
{
TestShape t = TestShape();
Vector eyev = Vector(0, -sqrt(2)/2, -sqrt(2)/2);
Vector normalv = Vector(0, 0, -1);
Light light = Light(POINT_LIGHT, Point(0, 0, -10), Colour(1, 1, 1));
Colour result = m.lighting(light, position, eyev, normalv);
Colour result = m.lighting(light, position, eyev, normalv, &t);
ASSERT_EQ(result, Colour(1.0, 1.0, 1.0));
}
TEST(MaterialTest, Lighting_with_the_eye_opposite_surface_light_offset_45)
{
TestShape t = TestShape();
Vector eyev = Vector(0, 0, -1);
Vector normalv = Vector(0, 0, -1);
Light light = Light(POINT_LIGHT, Point(0, 10, -10), Colour(1, 1, 1));
Colour result = m.lighting(light, position, eyev, normalv);
Colour result = m.lighting(light, position, eyev, normalv, &t);
set_equal_precision(0.0001);
@@ -65,11 +69,12 @@ TEST(MaterialTest, Lighting_with_the_eye_opposite_surface_light_offset_45)
TEST(MaterialTest, Lighting_with_the_eye_in_the_path_of_the_reflection_vector)
{
TestShape t = TestShape();
Vector eyev = Vector(0, -sqrt(2)/2, -sqrt(2)/2);
Vector normalv = Vector(0, 0, -1);
Light light = Light(POINT_LIGHT, Point(0, 10, -10), Colour(1, 1, 1));
Colour result = m.lighting(light, position, eyev, normalv);
Colour result = m.lighting(light, position, eyev, normalv, &t);
set_equal_precision(0.0001);
@@ -80,23 +85,40 @@ TEST(MaterialTest, Lighting_with_the_eye_in_the_path_of_the_reflection_vector)
TEST(MaterialTest, Lighting_with_the_light_behind_the_surface)
{
TestShape t = TestShape();
Vector eyev = Vector(0, 0, -1);
Vector normalv = Vector(0, 0, -1);
Light light = Light(POINT_LIGHT, Point(0, 0, 10), Colour(1, 1, 1));
Colour result = m.lighting(light, position, eyev, normalv);
Colour result = m.lighting(light, position, eyev, normalv, &t);
ASSERT_EQ(result, Colour(0.1, 0.1, 0.1));
}
TEST(MaterialTest, Lighting_with_the_surface_in_shadow)
{
TestShape t = TestShape();
Vector eyev = Vector(0, 0, -1);
Vector normalv = Vector(0, 0, -1);
Light light = Light(POINT_LIGHT, Point(0, 0, -10), Colour(1, 1, 1));
bool inShadow = true;
Colour result = m.lighting(light, position, eyev, normalv, inShadow);
Colour result = m.lighting(light, position, eyev, normalv, &t, inShadow);
ASSERT_EQ(result, Colour(0.1, 0.1, 0.1));
}
TEST(MaterialTest, Reflectivity_for_the_default_material)
{
Material m = Material();
ASSERT_EQ(m.reflective, 0);
}
TEST(MaterialTest, Transparency_and_refractive_index_for_the_default_material)
{
Material m = Material();
ASSERT_EQ(m.transparency, 0.0);
ASSERT_EQ(m.refractiveIndex, 1.0);
}

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);
}

211
tests/pattern_test.cpp Normal file
View File

@@ -0,0 +1,211 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Pattern unit tests
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <pattern.h>
#include <strippattern.h>
#include <gradientpattern.h>
#include <ringpattern.h>
#include <checkerspattern.h>
#include <testpattern.h>
#include <transformation.h>
#include <colour.h>
#include <sphere.h>
#include <gtest/gtest.h>
#include <material.h>
Colour black = Colour(0, 0, 0);
Colour white = Colour(1, 1, 1);
TEST(PatternTest, Creating_a_stripe_pattern)
{
StripPattern p = StripPattern(white, black);
ASSERT_EQ(p.a, white);
ASSERT_EQ(p.b, black);
}
TEST(PatternTest, A_strip_pattern_is_constant_in_y)
{
StripPattern p = StripPattern(white, black);
ASSERT_EQ(p.patternAt(Point(0, 0, 0)), white);
ASSERT_EQ(p.patternAt(Point(0, 1, 0)), white);
ASSERT_EQ(p.patternAt(Point(0, 2, 0)), white);
}
TEST(PatternTest, A_strip_pattern_is_constant_in_z)
{
StripPattern p = StripPattern(white, black);
ASSERT_EQ(p.patternAt(Point(0, 0, 0)), white);
ASSERT_EQ(p.patternAt(Point(0, 0, 1)), white);
ASSERT_EQ(p.patternAt(Point(0, 0, 2)), white);
}
TEST(PatternTest, A_strip_pattern_alternate_in_x)
{
StripPattern p = StripPattern(white, black);
ASSERT_EQ(p.patternAt(Point(0, 0, 0)), white);
ASSERT_EQ(p.patternAt(Point(0.9, 0, 0)), white);
ASSERT_EQ(p.patternAt(Point(1, 0, 0)), black);
ASSERT_EQ(p.patternAt(Point(-0.1, 0, 0)), black);
ASSERT_EQ(p.patternAt(Point(-1, 0, 0)), black);
ASSERT_EQ(p.patternAt(Point(-1.1, 0, 0)), white);
}
TEST(PatternTest, Lightning_with_a_pattern_applied)
{
Sphere s = Sphere();
Material m;
StripPattern p = StripPattern(white, black);
m.pattern = &p;
m.ambient = 1;
m.diffuse = 0;
m.specular = 0;
Tuple eyev = Vector(0, 0, -1);
Tuple normalv = Vector(0, 0, -1);
Light light = Light(POINT_LIGHT, Point(0, 0, -10), Colour(1, 1, 1));
Colour c1 = m.lighting(light, Point(0, 9, 0), eyev, normalv, &s, false);
Colour c2 = m.lighting(light, Point(1, 1, 0), eyev, normalv, &s, false);
ASSERT_EQ(c1, Colour(1, 1, 1));
ASSERT_EQ(c2, Colour(0, 0, 0));
}
TEST(PatternTest, Stripe_with_an_object_transformation)
{
Sphere s = Sphere();
s.setTransform(scaling(2, 2, 2));
StripPattern pattern = StripPattern(white, black);
Colour c = pattern.patternAtObject(&s, Point(1.5, 0, 0));
ASSERT_EQ(c, white);
}
TEST(PatternTest, Stripe_with_a_pattern_transformation)
{
Sphere s = Sphere();
StripPattern pattern = StripPattern(white, black);
pattern.setTransform(scaling(2, 2, 2));
Colour c = pattern.patternAtObject(&s, Point(1.5, 0, 0));
ASSERT_EQ(c, white);
}
TEST(PatternTest, Stripe_with_both_an_object_and_a_pattern_transformation)
{
Sphere s = Sphere();
s.setTransform(scaling(2, 2, 2));
StripPattern pattern = StripPattern(white, black);
pattern.setTransform(translation(0.5, 0, 0));
Colour c = pattern.patternAtObject(&s, Point(2.5, 0, 0));
ASSERT_EQ(c, white);
}
TEST(PatternTest, The_default_pattern_transformation)
{
TestPattern pattern = TestPattern();
ASSERT_EQ(pattern.transformMatrix, Matrix4().identity());
}
TEST(PatternTest, Assigning_a_transformation)
{
TestPattern pattern = TestPattern();
pattern.setTransform(translation(1, 2, 3));
ASSERT_EQ(pattern.transformMatrix, translation(1, 2, 3));
}
TEST(PatternTest, A_pattern_with_an_object_transformation)
{
Sphere s = Sphere();
s.setTransform(scaling(2, 2, 2));
TestPattern pattern = TestPattern();
Colour c = pattern.patternAtObject(&s, Point(2, 3, 4));
ASSERT_EQ(c, Colour(1, 1.5, 2));
}
TEST(PatternTest, A_pattern_with_a_pattern_transformation)
{
Sphere s = Sphere();
TestPattern pattern = TestPattern();
pattern.setTransform(scaling(2, 2, 2));
Colour c = pattern.patternAtObject(&s, Point(2, 3, 4));
ASSERT_EQ(c, Colour(1, 1.5, 2));
}
TEST(PatternTest, A_pattern_with_an_object_and_a_pattern_transformation)
{
Sphere s = Sphere();
s.setTransform(scaling(2, 2, 2));
TestPattern pattern = TestPattern();
pattern.setTransform(translation(0.5, 1, 1.5));
Colour c = pattern.patternAtObject(&s, Point(2.5, 3, 3.5));
ASSERT_EQ(c, Colour(0.75, 0.5, 0.25));
}
TEST(PatternTest, A_gradient_linearly_interpolates_betweens_colors)
{
GradientPattern pattern = GradientPattern(white, black);
ASSERT_EQ(pattern.patternAt(Point(0.25, 0, 0)), Colour(0.75, 0.75, 0.75));
ASSERT_EQ(pattern.patternAt(Point(0.5, 0, 0)), Colour(0.5, 0.5, 0.5));
ASSERT_EQ(pattern.patternAt(Point(0.75, 0, 0)), Colour(0.25, 0.25, 0.25));
}
TEST(PatternTest, A_ring_should_extend_in_both_x_and_z)
{
RingPattern pattern = RingPattern(white, black);
ASSERT_EQ(pattern.patternAt(Point(0, 0, 0)), white);
ASSERT_EQ(pattern.patternAt(Point(1, 0, 0)), black);
ASSERT_EQ(pattern.patternAt(Point(0, 0, 1)), black);
/* 0.708 is just bit more than sqrt(2)/2 */
ASSERT_EQ(pattern.patternAt(Point(0.708, 0, 0.708)), black);
}
TEST(PatternTest, Checkers_should_repeat_in_x)
{
CheckersPattern pattern = CheckersPattern(white, black);
ASSERT_EQ(pattern.patternAt(Point(0, 0, 0)), white);
ASSERT_EQ(pattern.patternAt(Point(0.99, 0, 0)), white);
ASSERT_EQ(pattern.patternAt(Point(1.01, 0, 0)), black);
}
TEST(PatternTest, Checkers_should_repeat_in_y)
{
CheckersPattern pattern = CheckersPattern(white, black);
ASSERT_EQ(pattern.patternAt(Point(0, 0, 0)), white);
ASSERT_EQ(pattern.patternAt(Point(0, 0.99, 0)), white);
ASSERT_EQ(pattern.patternAt(Point(0, 1.01, 0)), black);
}
TEST(PatternTest, Checkers_should_repeat_in_z)
{
CheckersPattern pattern = CheckersPattern(white, black);
ASSERT_EQ(pattern.patternAt(Point(0, 0, 0)), white);
ASSERT_EQ(pattern.patternAt(Point(0, 0, 0.99)), white);
ASSERT_EQ(pattern.patternAt(Point(0, 0, 1.01)), black);
}

71
tests/plane_test.cpp Normal file
View File

@@ -0,0 +1,71 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Plane unit tests
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <ray.h>
#include <shape.h>
#include <plane.h>
#include <material.h>
#include <transformation.h>
#include <gtest/gtest.h>
TEST(PlaneTest, The_normal_of_a_plane_is_constant_everywhere)
{
Plane p = Plane();
Tuple n1 = p.normalAt(Point(0, 0, 0));
Tuple n2 = p.normalAt(Point(10, 0, -10));
Tuple n3 = p.normalAt(Point(-5, 0, 0150));
ASSERT_EQ(n1, Vector(0, 1, 0));
ASSERT_EQ(n2, Vector(0, 1, 0));
ASSERT_EQ(n3, Vector(0, 1, 0));
}
TEST(PlaneTest, Intersect_with_a_ray_parallel_to_the_plane)
{
Plane p = Plane();
Ray r = Ray(Point(0, 10, 0), Vector(0, 0, 1));
Intersect xs = p.intersect(r);
ASSERT_EQ(xs.count(), 0);
}
TEST(PlaneTest, Intersect_with_a_coplanar_ray)
{
Plane p = Plane();
Ray r = Ray(Point(0, 0, 0), Vector(0, 0, 1));
Intersect xs = p.intersect(r);
ASSERT_EQ(xs.count(), 0);
}
TEST(PlaneTest, A_ray_intersecting_a_plane_from_above)
{
Plane p = Plane();
Ray r = Ray(Point(0, 1, 0), Vector(0, -1, 0));
Intersect xs = p.intersect(r);
ASSERT_EQ(xs.count(), 1);
ASSERT_EQ(xs[0].t, 1);
ASSERT_EQ(xs[0].object, &p);
}
TEST(PlaneTest, A_ray_intersecting_a_plane_from_below)
{
Plane p = Plane();
Ray r = Ray(Point(0, -1, 0), Vector(0, 1, 0));
Intersect xs = p.intersect(r);
ASSERT_EQ(xs.count(), 1);
ASSERT_EQ(xs[0].t, 1);
ASSERT_EQ(xs[0].object, &p);
}

View File

@@ -9,6 +9,7 @@
#include <ray.h>
#include <transformation.h>
#include <shape.h>
#include <testshape.h>
#include <gtest/gtest.h>
@@ -38,7 +39,7 @@ TEST(RayTest, Translating_a_ray)
Ray r = Ray(Point(1, 2, 3), Vector(0, 1, 0));
Matrix m = translation(3, 4, 5);
Shape o = Shape();
TestShape o = TestShape();
o.setTransform(m);
@@ -53,7 +54,7 @@ TEST(RayTest, Scaling_a_ray)
Ray r = Ray(Point(1, 2, 3), Vector(0, 1, 0));
Matrix m = scaling(2, 3, 4);
Shape o = Shape();
TestShape o = TestShape();
o.setTransform(m);

98
tests/shape_test.cpp Normal file
View File

@@ -0,0 +1,98 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Shape unit tests
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <shape.h>
#include <testshape.h>
#include <matrix.h>
#include <transformation.h>
#include <gtest/gtest.h>
TEST(ShapeTest, The_default_transformation)
{
TestShape s = TestShape();
ASSERT_EQ(s.transformMatrix, Matrix4().identity());
}
TEST(ShapeTest, Assigning_a_transformation)
{
TestShape s = TestShape();
s.setTransform(translation(2, 3, 4));
ASSERT_EQ(s.transformMatrix, translation(2, 3, 4));
}
TEST(ShapeTest, The_default_material)
{
TestShape s = TestShape();
ASSERT_EQ(s.material, Material());
}
TEST(ShapeTest, Assigning_a_material)
{
TestShape s = TestShape();
Material m = Material();
m.ambient = 1;
s.material = m;
ASSERT_EQ(s.material, m);
}
TEST(ShapeTest, Intersecting_a_scaled_shape_with_a_ray)
{
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
TestShape s = TestShape();
s.setTransform(scaling(2, 2, 2));
Intersect xs = s.intersect(r);
ASSERT_EQ(s.localRay.origin, Point(0, 0, -2.5));
ASSERT_EQ(s.localRay.direction, Vector(0, 0, 0.5));
}
TEST(ShapeTest, Intersecting_a_translated_shape_with_a_ray)
{
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
TestShape s = TestShape();
s.setTransform(translation(5, 0, 0));
Intersect xs = s.intersect(r);
ASSERT_EQ(s.localRay.origin, Point(-5, 0, -5));
ASSERT_EQ(s.localRay.direction, Vector(0, 0, 1));
}
TEST(ShapeTest, Computing_the_normal_on_a_translated_shape)
{
TestShape s = TestShape();
s.setTransform(translation(0, 1, 0));
Tuple n = s.normalAt(Point(0, 1.70711, -0.70711));
/* Temporary lower the precision */
set_equal_precision(0.00001);
ASSERT_EQ(n, Vector(0, 0.70711, -0.70711));
set_equal_precision(FLT_EPSILON);
}
TEST(ShapeTest, Computing_the_normal_on_a_tranformed_shape)
{
TestShape s = TestShape();
s.setTransform(scaling(1, 0.5, 1) * rotationZ(M_PI / 5));
Tuple n = s.normalAt(Point(0, sqrt(2)/2, -sqrt(2)/2));
/* Temporary lower the precision */
set_equal_precision(0.00001);
ASSERT_EQ(n, Vector(0, 0.97014, -0.24254));
set_equal_precision(FLT_EPSILON);
}

View File

@@ -198,4 +198,13 @@ TEST(SphereTest, A_sphere_may_be_assigned_a_material)
s.setMaterial(m);
ASSERT_EQ(s.material, m);
}
TEST(SphereTest, A_helper_for_producing_a_sphere_with_a_glassy_material)
{
GlassSphere s = GlassSphere();
ASSERT_EQ(s.transformMatrix, Matrix4().identity());
ASSERT_EQ(s.material.transparency, 1.0);
ASSERT_EQ(s.material.refractiveIndex, 1.5);
}

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

@@ -12,8 +12,10 @@
#include <material.h>
#include <transformation.h>
#include <worldbuilder.h>
#include <testpattern.h>
#include <math.h>
#include <gtest/gtest.h>
#include <plane.h>
TEST(WorldTest, Creating_a_world)
@@ -167,4 +169,261 @@ TEST(WorldTest, Shade_hit_is_given_an_intersection_in_shadow)
Tuple c = w.shadeHit(comps);
ASSERT_EQ(c, Colour(0.1, 0.1, 0.1));
};
};
TEST(WorldTest, The_reflected_colour_for_a_non_reflective_material)
{
World w = DefaultWorld();
Ray r = Ray(Point(0, 0, 0), Vector(0, 0, 1));
Shape *shape = w.getObject(1); /* The second object */
shape->material.ambient = 1; /* We use this to get a predictable colour */
Intersection i = Intersection(1, shape);
Computation comps = i.prepareComputation(r);
Colour colour = w.reflectColour(comps);
ASSERT_EQ(colour, Colour(0, 0, 0));
}
TEST(WorldTest, The_reflected_colour_for_a_reflective_material)
{
World w = DefaultWorld();
Plane shape = Plane();
shape.material.reflective = 0.5;
shape.setTransform(translation(0, -1, 0));
w.addObject(&shape);
Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2));
Intersection i = Intersection(sqrt(2), &shape);
Computation comps = i.prepareComputation(r);
Colour colour = w.reflectColour(comps);
/* Temporary lower the precision */
set_equal_precision(0.00002);
ASSERT_EQ(colour, Colour(0.19032, 0.2379, 0.14274));
set_equal_precision(FLT_EPSILON);
}
TEST(WorldTest, Shade_hit_with_a_reflective_material)
{
World w = DefaultWorld();
Plane shape = Plane();
shape.material.reflective = 0.5;
shape.setTransform(translation(0, -1, 0));
w.addObject(&shape);
Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2));
Intersection i = Intersection(sqrt(2), &shape);
Computation comps = i.prepareComputation(r);
Tuple colour = w.shadeHit(comps);
/* Temporary lower the precision */
set_equal_precision(0.00005);
ASSERT_EQ(colour, Colour(0.87677, 0.92436, 0.82918));
set_equal_precision(FLT_EPSILON);
}
TEST(WorldTest, Colour_at_with_mutually_reflective_surfaces)
{
World w = World();
Light l = Light(POINT_LIGHT, Point(0, 0, 0), Colour(1, 1, 1));
w.addLight(&l);
Plane lower = Plane();
lower.material.reflective = 1;
lower.setTransform(translation(0, -1, 0));
Plane higher = Plane();
higher.material.reflective = 1;
higher.setTransform(translation(0, 1, 0));
w.addObject(&lower);
w.addObject(&higher);
Ray r = Ray(Point(0, 0, 0), Vector(0, 1, 0));
/* It should just exit, we don't care about the actual colour */
w.colourAt(r);
SUCCEED();
}
TEST(WorldTest, The_reflected_colour_at_the_maximum_recursion_depth)
{
World w = DefaultWorld();
Plane shape = Plane();
shape.material.reflective = 0.5;
shape.setTransform(translation(0, -1, 0));
w.addObject(&shape);
Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2));
Intersection i = Intersection(sqrt(2), &shape);
Computation comps = i.prepareComputation(r);
Tuple colour = w.reflectColour(comps, 0);
/* Temporary lower the precision */
ASSERT_EQ(colour, Colour(0, 0, 0));
}
TEST(WorldTest, The_refracted_colour_with_an_opaque_surface)
{
World w = DefaultWorld();
Shape *shape = w.getObject(0);
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
Intersect xs = Intersect();
xs.add(Intersection(4, shape));
xs.add(Intersection(6, shape));
Computation comps = xs[0].prepareComputation(r, &xs);
Colour c = w.refractedColour(comps, 5);
ASSERT_EQ(c, Colour(0, 0, 0));
}
TEST(WorldTest, The_refracted_colour_at_the_maximum_recursive_depth)
{
World w = DefaultWorld();
Shape *shape = w.getObject(0);
shape->material.transparency = 1.0;
shape->material.refractiveIndex = 1.5;
Ray r = Ray(Point(0, 0, -5), Vector(0, 0, 1));
Intersect xs = Intersect();
xs.add(Intersection(4, shape));
xs.add(Intersection(6, shape));
Computation comps = xs[0].prepareComputation(r, &xs);
Colour c = w.refractedColour(comps, 0);
ASSERT_EQ(c, Colour(0, 0, 0));
}
TEST(WorldTest, The_refracted_colour_under_total_internal_reflection)
{
World w = DefaultWorld();
Shape *shape = w.getObject(0);
shape->material.transparency = 1.0;
shape->material.refractiveIndex = 1.5;
Ray r = Ray(Point(0, 0, sqrt(2)/2), Vector(0, 1, 0));
Intersect xs = Intersect();
xs.add(Intersection(-sqrt(2)/2, shape));
xs.add(Intersection(sqrt(2)/2, shape));
Computation comps = xs[1].prepareComputation(r, &xs);
Colour c = w.refractedColour(comps, 5);
ASSERT_EQ(c, Colour(0, 0, 0));
}
TEST(WorldTest, The_refracted_coloud_with_a_refracted_ray)
{
World w = DefaultWorld();
Shape *A = w.getObject(0);
A->material.ambient = 1.0;
A->material.pattern = new TestPattern();
Shape *B = w.getObject(1);
B->material.transparency = 1.0;
B->material.refractiveIndex = 1.5;
Ray r = Ray(Point(0, 0, 0.1), Vector(0, 1, 0));
Intersect xs = Intersect();
xs.add(Intersection(-0.9899, A));
xs.add(Intersection(-0.4899, B));
xs.add(Intersection(0.4899, B));
xs.add(Intersection(0.9899, A));
Computation comps = xs[2].prepareComputation(r, &xs);
Colour c = w.refractedColour(comps, 5);
/* Temporary lower the precision */
set_equal_precision(0.00005);
ASSERT_EQ(c, Colour(0, 0.99888, 0.04725));
set_equal_precision(FLT_EPSILON);
}
TEST(WorldTest, Shade_hit_with_a_transparent_material)
{
World w = DefaultWorld();
Plane floor = Plane();
floor.setTransform(translation(0, -1, 0));
floor.material.transparency = 0.5;
floor.material.refractiveIndex = 1.5;
w.addObject(&floor);
Sphere ball = Sphere();
ball.material.colour = Colour(1, 0, 0);
ball.material.ambient = 0.5;
ball.setTransform(translation(0, -3.5, -0.5));
w.addObject(&ball);
Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2));
Intersect xs = Intersect();
xs.add(Intersection(sqrt(2), &floor));
Computation comps = xs[0].prepareComputation(r, &xs);
Tuple c = w.shadeHit(comps, 5);
/* Temporary lower the precision */
set_equal_precision(0.00001);
ASSERT_EQ(c, Colour(0.93642, 0.68642, 0.68642));
set_equal_precision(FLT_EPSILON);
}
TEST(WorldTest, Shade_hit_with_a_reflective_transparent_material)
{
World w = DefaultWorld();
Ray r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2)/2, sqrt(2)/2));
Plane floor = Plane();
floor.setTransform(translation(0, -1, 0));
floor.material.transparency = 0.5;
floor.material.reflective = 0.5;
floor.material.refractiveIndex = 1.5;
w.addObject(&floor);
Sphere ball = Sphere();
ball.material.colour = Colour(1, 0, 0);
ball.material.ambient = 0.5;
ball.setTransform(translation(0, -3.5, -0.5));
w.addObject(&ball);
Intersect xs = Intersect();
xs.add(Intersection(sqrt(2), &floor));
Computation comps = xs[0].prepareComputation(r, &xs);
Tuple c = w.shadeHit(comps, 5);
/* Temporary lower the precision */
set_equal_precision(0.00001);
ASSERT_EQ(c, Colour(0.93391, 0.69643, 0.69243));
set_equal_precision(FLT_EPSILON);
}