Smooth triangles! And support for them in the OBJ File parser.

Also add an interesting tea party scene!
This commit is contained in:
Godzil
2020-03-06 15:07:26 +00:00
parent 73012b6dd1
commit e57b5715e8
38 changed files with 705 additions and 75 deletions

View File

@@ -6,9 +6,12 @@ DoRayMe
A Quick and dirty raytracer.
This raytracer is made following the book "[The Ray Tracer Challenge](https://pragprog.com/book/jbtracer/the-ray-tracer-challenge)" by Jamis Buck.
This raytracer is made following the book
"[The Ray Tracer Challenge](https://pragprog.com/book/jbtracer/the-ray-tracer-challenge)" by Jamis Buck.
It is writen in C++ with no STL and use [LodePNG](https://github.com/lvandeve/lodepng) to output PNG file.
It is writen in C++ with no STL and use [LodePNG](https://github.com/lvandeve/lodepng) to output PNG file and use them
as texture, also use [NanoJPEG](https://keyj.emphy.de/nanojpeg/) to use jpeg file as texture, and can use use
[Lua 5.3](https://www.lua.org/) for 3D pattern definition and more to come on the Lua side later..
Examples outputs
@@ -65,6 +68,8 @@ Bonus:
**From Chapter 14 - Groups & Bounding boxes:**
![Chapter 14 rendering test](output/ch14_test.png)
**From Chapter 15 - Triangles, Wavefrom OBJ files - Smooth trianges:**
![Chapter 15 Triangles and teapots](output/ch15_teapot_objfile.png)
**Bonus (from the forum):**

274
external/teapot-lowtri.obj vendored Normal file
View File

@@ -0,0 +1,274 @@
#
# object Teapot001
#
v 7.0000 0.0000 12.0000
v 4.9700 -4.9700 12.0000
v 4.9811 -4.9811 12.4922
v 7.0156 0.0000 12.4922
v 5.3250 -5.3250 12.0000
v 7.5000 0.0000 12.0000
v 0.0000 -7.0000 12.0000
v 0.0000 -7.0156 12.4922
v 0.0000 -7.5000 12.0000
v -5.1387 -4.9700 12.0000
v -5.0022 -4.9811 12.4922
v -5.3250 -5.3250 12.0000
v -7.0000 0.0000 12.0000
v -7.0156 0.0000 12.4922
v -7.5000 0.0000 12.0000
v -4.9700 4.9700 12.0000
v -4.9811 4.9811 12.4922
v -5.3250 5.3250 12.0000
v 0.0000 7.0000 12.0000
v 0.0000 7.0156 12.4922
v 0.0000 7.5000 12.0000
v 4.9700 4.9700 12.0000
v 4.9811 4.9811 12.4922
v 5.3250 5.3250 12.0000
v 6.5453 -6.5453 8.1094
v 9.2188 0.0000 8.1094
v 7.1000 -7.1000 4.5000
v 10.0000 0.0000 4.5000
v 0.0000 -9.2188 8.1094
v 0.0000 -10.0000 4.5000
v -6.5453 -6.5453 8.1094
v -7.1000 -7.1000 4.5000
v -9.2188 0.0000 8.1094
v -10.0000 0.0000 4.5000
v -6.5453 6.5453 8.1094
v -7.1000 7.1000 4.5000
v 0.0000 9.2188 8.1094
v 0.0000 10.0000 4.5000
v 6.5453 6.5453 8.1094
v 7.1000 7.1000 4.5000
v 6.2125 -6.2125 1.9219
v 8.7500 0.0000 1.9219
v 5.3250 -5.3250 0.7500
v 7.5000 0.0000 0.7500
v 0.0000 -8.7500 1.9219
v 0.0000 -7.5000 0.7500
v -6.2125 -6.2125 1.9219
v -5.3250 -5.3250 0.7500
v -8.7500 0.0000 1.9219
v -7.5000 0.0000 0.7500
v -6.2125 6.2125 1.9219
v -5.3250 5.3250 0.7500
v 0.0000 8.7500 1.9219
v 0.0000 7.5000 0.7500
v 6.2125 6.2125 1.9219
v 5.3250 5.3250 0.7500
v 4.5595 -4.5595 0.2344
v 6.4219 0.0000 0.2344
v 0.0000 0.0000 0.0000
v 0.0000 -6.4219 0.2344
v -4.5595 -4.5595 0.2344
v -6.4219 0.0000 0.2344
v -4.5595 4.5595 0.2344
v 0.0000 6.4219 0.2344
v 4.5595 4.5595 0.2344
v -8.0000 0.0000 10.1250
v -7.7500 -1.1250 10.6875
v -12.5938 -1.1250 10.4766
v -12.0625 0.0000 9.9844
v -14.2500 -1.1250 9.0000
v -13.5000 0.0000 9.0000
v -7.5000 0.0000 11.2500
v -13.1250 0.0000 10.9688
v -15.0000 0.0000 9.0000
v -7.7500 1.1250 10.6875
v -12.5938 1.1250 10.4766
v -14.2500 1.1250 9.0000
v -13.1719 -1.1250 6.2695
v -12.6875 0.0000 6.7500
v -9.7500 -1.1250 3.7500
v -13.6563 0.0000 5.7891
v -9.5000 0.0000 3.0000
v -13.1719 1.1250 6.2695
v -9.7500 1.1250 3.7500
v 8.5000 0.0000 7.1250
v 8.5000 -2.4750 5.0625
v 12.6875 -1.7062 8.1094
v 11.9375 0.0000 9.0000
v 15.0000 -0.9375 12.0000
v 13.5000 0.0000 12.0000
v 8.5000 0.0000 3.0000
v 13.4375 0.0000 7.2187
v 16.5000 0.0000 12.0000
v 8.5000 2.4750 5.0625
v 12.6875 1.7062 8.1094
v 15.0000 0.9375 12.0000
v 15.6328 -0.7500 12.3340
v 14.1250 0.0000 12.2813
v 15.0000 -0.5625 12.0000
v 14.0000 0.0000 12.0000
v 17.1406 0.0000 12.3867
v 16.0000 0.0000 12.0000
v 15.6328 0.7500 12.3340
v 15.0000 0.5625 12.0000
v 1.1552 -1.1552 14.9063
v 1.6250 0.0000 14.9063
v 0.0000 0.0000 15.7500
v 0.7100 -0.7100 13.5000
v 1.0000 0.0000 13.5000
v 0.0000 -1.6250 14.9063
v 0.0000 -1.0000 13.5000
v -1.1552 -1.1552 14.9063
v -0.7100 -0.7100 13.5000
v -1.6250 0.0000 14.9063
v -1.0000 0.0000 13.5000
v -1.1552 1.1552 14.9063
v -0.7100 0.7100 13.5000
v 0.0000 1.6250 14.9063
v 0.0000 1.0000 13.5000
v 1.1552 1.1552 14.9063
v 0.7100 0.7100 13.5000
v 2.9288 -2.9288 12.7500
v 4.1250 0.0000 12.7500
v 4.6150 -4.6150 12.0000
v 6.5000 0.0000 12.0000
v 0.0000 -4.1250 12.7500
v 0.0000 -6.5000 12.0000
v -2.9288 -2.9288 12.7500
v -4.6150 -4.6150 12.0000
v -4.1250 0.0000 12.7500
v -6.5000 0.0000 12.0000
v -2.9288 2.9288 12.7500
v -4.6150 4.6150 12.0000
v 0.0000 4.1250 12.7500
v 0.0000 6.5000 12.0000
v 2.9288 2.9288 12.7500
v 4.6150 4.6150 12.0000
# 137 vertices
g Teapot001
f 1 2 3 4
f 4 3 5 6
f 2 7 8 3
f 3 8 9 5
f 7 10 11 8
f 8 11 12 9
f 10 13 14 11
f 11 14 15 12
f 13 16 17 14
f 14 17 18 15
f 16 19 20 17
f 17 20 21 18
f 19 22 23 20
f 20 23 24 21
f 22 1 4 23
f 23 4 6 24
f 6 5 25 26
f 26 25 27 28
f 5 9 29 25
f 25 29 30 27
f 9 12 31 29
f 29 31 32 30
f 12 15 33 31
f 31 33 34 32
f 15 18 35 33
f 33 35 36 34
f 18 21 37 35
f 35 37 38 36
f 21 24 39 37
f 37 39 40 38
f 24 6 26 39
f 39 26 28 40
f 28 27 41 42
f 42 41 43 44
f 27 30 45 41
f 41 45 46 43
f 30 32 47 45
f 45 47 48 46
f 32 34 49 47
f 47 49 50 48
f 34 36 51 49
f 49 51 52 50
f 36 38 53 51
f 51 53 54 52
f 38 40 55 53
f 53 55 56 54
f 40 28 42 55
f 55 42 44 56
f 44 43 57 58
f 58 57 59
f 43 46 60 57
f 57 60 59
f 46 48 61 60
f 60 61 59
f 48 50 62 61
f 61 62 59
f 50 52 63 62
f 62 63 59
f 52 54 64 63
f 63 64 59
f 54 56 65 64
f 64 65 59
f 56 44 58 65
f 65 58 59
f 66 67 68 69
f 69 68 70 71
f 67 72 73 68
f 68 73 74 70
f 72 75 76 73
f 73 76 77 74
f 75 66 69 76
f 76 69 71 77
f 71 70 78 79
f 79 78 80 34
f 70 74 81 78
f 78 81 82 80
f 74 77 83 81
f 81 83 84 82
f 77 71 79 83
f 83 79 34 84
f 85 86 87 88
f 88 87 89 90
f 86 91 92 87
f 87 92 93 89
f 91 94 95 92
f 92 95 96 93
f 94 85 88 95
f 95 88 90 96
f 90 89 97 98
f 98 97 99 100
f 89 93 101 97
f 97 101 102 99
f 93 96 103 101
f 101 103 104 102
f 96 90 98 103
f 103 98 100 104
f 105 106 107
f 106 105 108 109
f 110 105 107
f 105 110 111 108
f 112 110 107
f 110 112 113 111
f 114 112 107
f 112 114 115 113
f 116 114 107
f 114 116 117 115
f 118 116 107
f 116 118 119 117
f 120 118 107
f 118 120 121 119
f 106 120 107
f 120 106 109 121
f 109 108 122 123
f 123 122 124 125
f 108 111 126 122
f 122 126 127 124
f 111 113 128 126
f 126 128 129 127
f 113 115 130 128
f 128 130 131 129
f 115 117 132 130
f 130 132 133 131
f 117 119 134 132
f 132 134 135 133
f 119 121 136 134
f 134 136 137 135
f 121 109 123 136
f 136 123 125 137
# 112 polygons - 16 triangles

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

View File

@@ -19,7 +19,7 @@ class Cone : public Shape {
protected:
Intersect localIntersect(Ray r);
Tuple localNormalAt(Tuple point);
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
bool checkCap(Ray r, double t, double y);
void intersectCaps(Ray r, Intersect &xs);

View File

@@ -16,12 +16,12 @@
#include <stdio.h>
class Cube : public Shape {
private:
protected:
void checkAxis(double axeOrigin, double axeDirection, double *axeMin, double *axeMax);
Intersect localIntersect(Ray r);
Tuple localNormalAt(Tuple point);
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
public:
Cube() : Shape(SHAPE_CUBE) { stats.addCube(); };

View File

@@ -16,10 +16,11 @@
#include <stdio.h>
class Cylinder : public Shape {
private:
protected:
Intersect localIntersect(Ray r);
Tuple localNormalAt(Tuple point);
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
bool checkCap(Ray r, double t);
void intersectCaps(Ray r, Intersect &xs);

View File

@@ -27,7 +27,7 @@ private:
protected:
Intersect localIntersect(Ray r);
Tuple localNormalAt(Tuple point);
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
BoundingBox bounds;

View File

@@ -74,8 +74,10 @@ public:
double t;
Shape *object;
double u, v;
public:
Intersection(double t, Shape *object) : t(t), object(object) { stats.addIntersection(); };
Intersection(double t, Shape *object, double u = NAN, double v = NAN) : t(t), object(object), u(u), v(v) { stats.addIntersection(); };
bool nothing() { return (this->object == nullptr); };
Computation prepareComputation(Ray r, Intersect *xs = nullptr);

View File

@@ -26,9 +26,13 @@ private:
Point* *vertexList;
uint32_t vertexCount;
uint32_t allocatedVertexNormalCount;
Vector* *vertexNormalList;
uint32_t vertexNormalCount;
private:
Intersect localIntersect(Ray r);
Tuple localNormalAt(Tuple point);
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
public:
/* Some stats */
@@ -37,6 +41,7 @@ public:
protected:
void addGroup(Group *group);
void addVertex(Point *vertex);
void addVertexNormal(Vector *vertexNormal);
void parseLine(char *line, uint32_t currentLine);
int execLine(int argc, char *argv[], uint32_t currentLine);
@@ -50,6 +55,7 @@ public:
/* OBJ file expect the first vertice to be 1 and not 0 */
Point vertices(uint32_t i) { return *this->vertexList[i - 1]; };
Vector verticesNormal(uint32_t i) { return *this->vertexNormalList[i - 1]; };
Group *groups(uint32_t i) { return this->faceGroupList[i]; };
Intersect intersect(Ray r);
BoundingBox getLocalBounds();

View File

@@ -13,9 +13,9 @@
class Plane : public Shape
{
private:
protected:
Intersect localIntersect(Ray r);
Tuple localNormalAt(Tuple point);
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
public:
Plane() : Shape(SHAPE_PLANE) { stats.addPlane(); };

View File

@@ -22,6 +22,7 @@ private:
uint64_t planeCount; /* Total number of plane */
uint64_t sphereCount; /* Total number of sphere */
uint64_t triangleCount; /* Total number of triangle */
uint64_t smoothTriangleCount; /* Total number of smooth triangle */
uint64_t objfileCount; /* Total number of OBJ File */
uint64_t pixelCount; /* Total number of rendered pixels */
@@ -40,7 +41,7 @@ private:
public:
RenderStats() : coneCount(0), cylinderCount(0), cubeCount(0), groupCount(0), lightCount(0), planeCount(0), sphereCount(0), triangleCount(0),
pixelCount(0), rayCount(0), lightRayEmitedCount(0), reflectionRayCount(0), refractedRayCount(0),
intersectCount(0), intersectionCount(0), reallocCallCount(0), mallocCallCount(0),
intersectCount(0), intersectionCount(0), reallocCallCount(0), mallocCallCount(0), smoothTriangleCount(0),
discardedIntersectCount(0), maxDepthAttained(UINT64_MAX), maxIntersectOnARay(0), objfileCount(0) {};
#ifdef RENDER_STATS
void addCone();
@@ -52,6 +53,7 @@ public:
void addSphere();
void addOBJFile();
void addTriangle();
void addSmoothTriangle();
void printStats();
void addPixel();
void addRay();
@@ -74,6 +76,7 @@ public:
static void addPlane() {};
static void addSphere() {};
static void addTriangle() {};
static void addSmoothTriangle() {};
static void printStats() {};
static void addPixel() {};
static void addRay() {};

View File

@@ -30,17 +30,19 @@ enum ShapeType
SHAPE_GROUP,
SHAPE_TRIANGLE,
SHAPE_OBJFILE,
SHAPE_SMOOTHTRIANGLE,
};
/* Base class for all object that can be presented in the world */
class Shape
{
private:
protected:
ShapeType type;
Matrix localTransformMatrix;
protected:
virtual Intersect localIntersect(Ray r) = 0;
virtual Tuple localNormalAt(Tuple point) = 0;
virtual Tuple localNormalAt(Tuple point, Intersection *hit) = 0;
public:
Matrix transformMatrix;
@@ -50,13 +52,14 @@ public:
Material material;
bool dropShadow;
Shape *parent;
bool materialSet;
public:
Shape(ShapeType = SHAPE_NONE);
virtual Intersect intersect(Ray r);
virtual Intersect intersectOOB(Ray r) { return this->intersect(r); };
Tuple normalAt(Tuple point);
Tuple normalAt(Tuple point, Intersection *hit = nullptr);
/* Bounding box points are always world value */
virtual BoundingBox getLocalBounds();

View File

@@ -0,0 +1,28 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Smooth Triangle header
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#ifndef DORAYME_SMOOTHTRIANGLE_H
#define DORAYME_SMOOTHTRIANGLE_H
#include <triangle.h>
class SmoothTriangle : public Triangle
{
public:
Vector n1;
Vector n2;
Vector n3;
protected:
Tuple localNormalAt(Tuple point, Intersection *hit);
public:
SmoothTriangle(Point p1, Point p2, Point p3, Vector n1, Vector n2, Vector n3);
};
#endif /* DORAYME_SMOOTHTRIANGLE_H */

View File

@@ -19,7 +19,7 @@ class Sphere : public Shape
{
protected:
Intersect localIntersect(Ray r);
Tuple localNormalAt(Tuple point);
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
public:
Sphere() : Shape(SHAPE_SPHERE) { stats.addSphere(); };

View File

@@ -17,7 +17,7 @@ class TestShape : public Shape
{
private:
Intersect localIntersect(Ray r);
Tuple localNormalAt(Tuple point);
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
public:
Ray localRay;

View File

@@ -14,8 +14,9 @@
class Triangle : public Shape
{
protected:
Intersect localIntersect(Ray r);
Tuple localNormalAt(Tuple point);
Tuple localNormalAt(Tuple point, Intersection *hit = nullptr);
public:
Tuple p1, p2, p3;

View File

@@ -60,7 +60,7 @@ void Intersect::add(Intersection i)
this->list = (Intersection **)realloc(this->list, sizeof(Intersection *) * this->allocated);
}
this->list[this->num++] = new Intersection(i.t, i.object);
this->list[this->num++] = new Intersection(i.t, i.object, i.u, i.v);
stats.setMaxIntersect(this->num);

View File

@@ -16,7 +16,18 @@ Computation Intersection::prepareComputation(Ray r, Intersect *xs)
double n2 = 1.0;
Tuple hitP = r.position(this->t);
Tuple normalV = this->object->normalAt(hitP);
Tuple normalV;
if (xs != nullptr)
{
Intersection hit = xs->hit();
normalV = this->object->normalAt(hitP, &hit);
}
else
{
normalV = this->object->normalAt(hitP, nullptr);
}
Tuple eyeV = -r.direction;
bool inside = false;
@@ -70,8 +81,9 @@ Computation Intersection::prepareComputation(Ray r, Intersect *xs)
}
Shape *s = this->object;
/* For now don't get root group material */
//while(s->parent != nullptr) { s = s->parent; }
while((!s->materialSet) && (s->parent != nullptr)) { s = s->parent; }
return Computation(this->object,
this->t,

View File

@@ -61,6 +61,12 @@ void RenderStats::addTriangle()
this->triangleCount++;
};
void RenderStats::addSmoothTriangle()
{
#pragma omp atomic
this->smoothTriangleCount++;
};
void RenderStats::addOBJFile()
{
#pragma omp atomic
@@ -154,6 +160,8 @@ void RenderStats::printStats()
printf("Planes : %lld\n", this->planeCount);
printf("Spheres : %lld\n", this->sphereCount);
printf("Triangles : %lld\n", this->triangleCount);
printf("Smooth Triangles : %lld\n", this->smoothTriangleCount);
printf("OBJ File : %lld\n", this->objfileCount);
printf("==================================================\n");
printf("Pixel rendered : %lld\n", this->pixelCount);
printf("Ray casted : %lld\n", this->rayCount);

View File

@@ -99,7 +99,7 @@ Intersect Cone::localIntersect(Ray r)
return ret;
}
Tuple Cone::localNormalAt(Tuple point)
Tuple Cone::localNormalAt(Tuple point, Intersection *hit)
{
/* Compute the square of the distance from the Y axis */
double dist = point.x * point.x + point.z * point.z;

View File

@@ -59,7 +59,7 @@ Intersect Cube::localIntersect(Ray r)
return ret;
}
Tuple Cube::localNormalAt(Tuple point)
Tuple Cube::localNormalAt(Tuple point, Intersection *hit)
{
double maxC = max3(fabs(point.x), fabs(point.y), fabs(point.z));

View File

@@ -90,7 +90,7 @@ Intersect Cylinder::localIntersect(Ray r)
return ret;
}
Tuple Cylinder::localNormalAt(Tuple point)
Tuple Cylinder::localNormalAt(Tuple point, Intersection *hit)
{
/* Compute the square of the distance from the Y axis */
double dist = point.x * point.x + point.z * point.z;

View File

@@ -72,7 +72,7 @@ Intersect Group::localIntersect(Ray r)
return Intersect();
}
Tuple Group::localNormalAt(Tuple point)
Tuple Group::localNormalAt(Tuple point, Intersection *hit)
{
return Vector(1, 0, 0);
}

View File

@@ -17,6 +17,7 @@
#include <math_helper.h>
#include <group.h>
#include <triangle.h>
#include <smoothtriangle.h>
#define MIN_ALLOC (2)
#define DEFAULT_GROUP (0)
@@ -33,6 +34,11 @@ OBJFile::OBJFile() : Shape(SHAPE_OBJFILE), ignoredLines(0)
this->vertexList = (Point **)calloc(sizeof(Point **), MIN_ALLOC);
this->vertexCount = 0;
this->allocatedVertexNormalCount = MIN_ALLOC;
this->vertexNormalList = (Vector **)calloc(sizeof(Vector **), MIN_ALLOC);
this->vertexNormalCount = 0;
/* There is always a default group */
this->addGroup(new Group());
};
@@ -89,13 +95,24 @@ void OBJFile::addVertex(Point *vertex)
this->vertexList[this->vertexCount++] = vertex;
}
void OBJFile::addVertexNormal(Vector *vertexNormal)
{
if ((this->vertexNormalCount + 1) > this->allocatedVertexNormalCount)
{
this->allocatedVertexNormalCount *= 2;
this->vertexNormalList = (Vector **)realloc(this->vertexNormalList, sizeof(Vector **) * this->allocatedVertexNormalCount);
}
this->vertexNormalList[this->vertexNormalCount++] = vertexNormal;
}
Intersect OBJFile::intersect(Ray r)
{
Intersect ret;
int i, j;
if (this->faceGroupCount > 0)
{
//if (this->bounds.intesectMe(r))
if (this->bounds.intesectMe(r))
{
for (i = 0 ; i < this->faceGroupCount ; i++)
{
@@ -118,7 +135,7 @@ Intersect OBJFile::localIntersect(Ray r)
return Intersect();
}
Tuple OBJFile::localNormalAt(Tuple point)
Tuple OBJFile::localNormalAt(Tuple point, Intersection *hit)
{
return Vector(0, 1, 0);
}
@@ -262,11 +279,51 @@ void OBJFile::parseLine(char *line, uint32_t currentLine)
}
}
static int parseFaceVertex(char *buf, uint32_t &v, uint32_t &vt, uint32_t &vn)
{
uint32_t bufPos = 0;
uint32_t lineLength = strlen(buf);
char *tmp = buf;
vt = INT32_MAX;
vn = INT32_MAX;
int ret = 0;
int token = 0;
while(bufPos < lineLength)
{
char *next = strchr(buf, '/');
if (next != nullptr)
{
*next = '\0';
bufPos = next - buf;
}
else
{
bufPos = lineLength;
}
if (strlen(buf) > 0)
{
switch(token)
{
case 0: v = atol(buf); break;
case 1: vt = atol(buf); break;
case 2: vn = atol(buf); break;
default: printf("ERROR: Too many entry for a face vertice!"); ret = 1;
}
}
buf = next + 1;
token++;
}
return ret;
}
/* Actually execute the line */
int OBJFile::execLine(int argc, char *argv[], uint32_t currentLine)
{
int ret = 1;
if (strncmp(argv[0], "v", 1) == 0)
if (strncmp(argv[0], "v", 2) == 0)
{
/* Vertice entry */
if (argc != 4)
@@ -279,25 +336,71 @@ int OBJFile::execLine(int argc, char *argv[], uint32_t currentLine)
ret = 0;
}
}
else if (strncmp(argv[0], "f", 1) == 0)
else if (strncmp(argv[0], "vn", 3) == 0)
{
/* Vertice entry */
/* Vertice Normal entry */
if (argc != 4)
{
printf("ERROR: Malformed file at line %d: Vertices normal expect 3 parameters!\n", currentLine);
}
else
{
this->addVertexNormal(new Vector(atof(argv[1]), atof(argv[2]), atof(argv[3])));
ret = 0;
}
}
else if (strncmp(argv[0], "f", 2) == 0)
{
/* Faces entry */
int i;
uint32_t v[MAX_ARGS], vt[MAX_ARGS], vn[MAX_ARGS];
for(i = 1; i < argc; i++)
{
parseFaceVertex(argv[i], v[i], vt[i], vn[i]);
}
if (argc == 4)
{
Shape *t = new Triangle(this->vertices(atoi(argv[1])),
this->vertices(atoi(argv[2])),
this->vertices(atoi(argv[3])));
Shape *t;
if (vn[1] == INT32_MAX)
{
t = new Triangle(this->vertices(v[1]),
this->vertices(v[2]),
this->vertices(v[3]));
}
else
{
t = new SmoothTriangle(this->vertices(v[1]),
this->vertices(v[2]),
this->vertices(v[3]),
this->verticesNormal(vn[1]),
this->verticesNormal(vn[2]),
this->verticesNormal(vn[3]));
}
this->faceGroupList[this->faceGroupCount - 1]->addObject(t);
ret = 0;
}
else if (argc > 4)
{
int i;
for(i = 2; i < (argc - 1); i++)
{
Shape *t = new Triangle(this->vertices(atoi(argv[1])),
this->vertices(atoi(argv[i])),
this->vertices(atoi(argv[i+1])));
Shape *t;
if (vn[1] == INT32_MAX)
{
t = new Triangle(this->vertices(v[1]),
this->vertices(v[i]),
this->vertices(v[i + 1]));
}
else
{
t = new SmoothTriangle(this->vertices(v[1]),
this->vertices(v[i]),
this->vertices(v[i + 1]),
this->verticesNormal(vn[1]),
this->verticesNormal(vn[i]),
this->verticesNormal(vn[i + 1]));
}
this->faceGroupList[this->faceGroupCount - 1]->addObject(t);
}
ret = 0;
@@ -307,7 +410,7 @@ int OBJFile::execLine(int argc, char *argv[], uint32_t currentLine)
printf("ERROR: Malformed file at line %d: Too few/many parameters!\n", currentLine);
}
}
else if (strncmp(argv[0], "g", 1) == 0)
else if (strncmp(argv[0], "g", 2) == 0)
{
if (argc == 2)
{

View File

@@ -30,7 +30,7 @@ Intersect Plane::localIntersect(Ray r)
return ret;
}
Tuple Plane::localNormalAt(Tuple point)
Tuple Plane::localNormalAt(Tuple point, Intersection *hit)
{
return Vector(0, 1, 0);
}

View File

@@ -20,6 +20,7 @@ Shape::Shape(ShapeType type)
this->type = type;
this->localTransformMatrix = Matrix4().identity();
this->updateTransform();
this->materialSet = false;
}
Intersect Shape::intersect(Ray r)
@@ -37,11 +38,11 @@ Tuple Shape::normalToWorld(Tuple normalVector)
return world_normal.normalise();
};
Tuple Shape::normalAt(Tuple point)
Tuple Shape::normalAt(Tuple point, Intersection *hit)
{
Tuple local_point = this->worldToObject(point);
Tuple local_normal = this->localNormalAt(local_point);
Tuple local_normal = this->localNormalAt(local_point, hit);
Tuple world_normal = this->normalToWorld(local_normal);

View File

@@ -0,0 +1,28 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Smooth Triangle implementation
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <ray.h>
#include <shape.h>
#include <triangle.h>
#include <smoothtriangle.h>
#include <math_helper.h>
#include <renderstat.h>
SmoothTriangle::SmoothTriangle(Point p1, Point p2, Point p3, Vector n1, Vector n2, Vector n3) : Triangle(p1, p2, p3),
n1(n1), n2(n2), n3(n3)
{
this->type = SHAPE_SMOOTHTRIANGLE;
stats.addSmoothTriangle();
}
Tuple SmoothTriangle::localNormalAt(Tuple point, Intersection *hit)
{
return (this->n2 * hit->u +
this->n3 * hit->v +
this->n1 * (1 - hit->u - hit->v)).normalise();
}

View File

@@ -35,7 +35,7 @@ Intersect Sphere::localIntersect(Ray r)
return ret;
}
Tuple Sphere::localNormalAt(Tuple point)
Tuple Sphere::localNormalAt(Tuple point, Intersection *hit)
{
return (point - Point(0, 0, 0)).normalise();
}

View File

@@ -19,7 +19,7 @@ Intersect TestShape::localIntersect(Ray r)
return Intersect();
}
Tuple TestShape::localNormalAt(Tuple point)
Tuple TestShape::localNormalAt(Tuple point, Intersection *hit)
{
return Vector(point.x, point.y, point.z);
}

View File

@@ -1,6 +1,6 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Cone implementation
* Triangle implementation
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
@@ -49,12 +49,12 @@ Intersect Triangle::localIntersect(Ray r)
}
double t = f * this->e2.dot(originCrossE1);
ret.add(Intersection(t, this));
ret.add(Intersection(t, this, u, v));
return ret;
}
Tuple Triangle::localNormalAt(Tuple point)
Tuple Triangle::localNormalAt(Tuple point, Intersection *hit)
{
return this->normal;
}

View File

@@ -10,7 +10,7 @@ link_libraries(rayonnement)
set(TESTS_SRC math_test.cpp tuple_test.cpp colour_test.cpp canvas_test.cpp matrix_test.cpp transformation_test.cpp
ray_test.cpp intersect_test.cpp sphere_test.cpp light_test.cpp material_test.cpp world_test.cpp camera_test.cpp
shape_test.cpp plane_test.cpp pattern_test.cpp cube_test.cpp cylinder_test.cpp cone_test.cpp group_test.cpp
boundingbox_test.cpp triangle_test.cpp sequence_test.cpp objfile_test.cpp)
boundingbox_test.cpp triangle_test.cpp sequence_test.cpp objfile_test.cpp smoothtriangle_test.cpp)
add_executable(testMyRays)
target_include_directories(testMyRays PUBLIC ${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
@@ -147,6 +147,7 @@ add_test(NAME Chapter12_Test COMMAND $<TARGET_FILE:ch12_test>)
add_test(NAME Chapter13_Test COMMAND $<TARGET_FILE:ch13_test>)
add_test(NAME Chapter13_ConeBonus COMMAND $<TARGET_FILE:ch13_cone>)
add_test(NAME Chapter14_Test COMMAND $<TARGET_FILE:ch14_test>)
add_test(NAME Chapter15_Teapots COMMAND $<TARGET_FILE:ch15_teapot_objfile>)
add_test(NAME AreaLight_Test COMMAND $<TARGET_FILE:arealight_test>)
add_test(NAME UVMap_CheckeredSphere COMMAND $<TARGET_FILE:uvmap_checkeredsphere>)
add_test(NAME UVMap_CheckeredPlane COMMAND $<TARGET_FILE:uvmap_checkeredplane>)

View File

@@ -80,7 +80,7 @@ int main()
/* ----------------------------- */
/* Set the camera */
Camera camera = Camera(400, 160, 0.7854);
Camera camera = Camera(40, 16, 0.7854);
camera.setTransform(viewTransform(Point(-3, 1, 2.5),
Point(0, 0.5, 0),
Vector(0, 1, 0)));

View File

@@ -104,7 +104,7 @@ int main()
bg4.material.shininess = 50;
w.addObject(&bg4);
/* Forground balls */
/* Foreground balls */
/* Red sphere */
Sphere redBall = Sphere();

View File

@@ -28,9 +28,9 @@ int main()
World w = World();
/* Add lights */
Light light1 = Light(POINT_LIGHT, Point(0, 20, 2), Colour(1, 1, 1));
Light light1 = Light(POINT_LIGHT, Point(50, 100, 20), Colour(.5, .5, .5));
w.addLight(&light1);
Light light2 = Light(POINT_LIGHT, Point(0, 2, 20), Colour(1, 1, 1));
Light light2 = Light(POINT_LIGHT, Point(2, 50, 100), Colour(.5, .5, .5));
w.addLight(&light2);
/* ----------------------------- */
@@ -42,6 +42,7 @@ int main()
p.material.ambient = 1;
p.material.diffuse = 0;
p.material.specular = 0;
p.material.reflective = 0.1;
w.addObject(&p);
Plane p2 = Plane();
@@ -53,26 +54,31 @@ int main()
w.addObject(&p2);
OBJFile teapot = OBJFile("teapot-low.obj");
teapot.setTransform(rotationY(M_PI) * rotationX(-M_PI/2) * scaling(0.4, 0.4, 0.4));
teapot.material.colour = Colour(1, 0.2, 0.1);
teapot.material.ambient = 0.2;
teapot.material.specular = 0.2;
teapot.material.diffuse = 20;
teapot.setTransform(translation(7, 0, 3) * rotationY(M_PI*23/22) * rotationX(-M_PI/2) * scaling(0.3, 0.3, 0.3));
teapot.material.colour = Colour(1, 0.3, 0.2);
teapot.material.shininess = 5;
teapot.material.specular = 0.4;
w.addObject(&teapot);
/* ----------------------------- */
OBJFile teapot2 = OBJFile("teapot-lowtri.obj");
teapot2.setTransform(translation(-7, 0, 3) * rotationY(-M_PI*46/22) * rotationX(-M_PI/2) * scaling(0.3, 0.3, 0.3));
teapot2.material.colour = Colour(1, 0.3, 0.2);
teapot2.material.shininess = 5;
teapot2.material.specular = 0.4;
w.addObject(&teapot2);
FILE *fpOut = fopen("teapot_worlddump.json", "wt");
if (fpOut)
{
w.dumpMe(fpOut);
fclose(fpOut);
}
OBJFile teapot3= OBJFile("teapot.obj");
teapot3.setTransform(translation(0, 0, -5) * rotationY(-M_PI) * rotationX(-M_PI/2) * scaling(0.4, 0.4, 0.4));
teapot3.material.colour = Colour(0.3, 1, 0.2);
teapot3.material.shininess = 5;
teapot3.material.specular = 0.4;
teapot3.material.reflective = 0.5;
w.addObject(&teapot3);
/* ----------------------------- */
/* Set the camera */
Camera camera = Camera(800, 400, M_PI/2);
Camera camera = Camera(80, 40, M_PI/2);
camera.setTransform(viewTransform(Point(0, 7, 13),
Point(0, 1, 0),
Vector(0, 1, 0)));

View File

@@ -10,6 +10,7 @@
#include <intersection.h>
#include <sphere.h>
#include <plane.h>
#include <triangle.h>
#include <transformation.h>
#include <gtest/gtest.h>
@@ -303,3 +304,12 @@ TEST(IntersectTest, The_Schlick_approximation_with_small_angle_and_n2_gt_n1)
set_equal_precision(FLT_EPSILON);
}
TEST(IntersectTest, An_intersection_can_encapsulage_u_and_v)
{
Triangle s = Triangle(Point(0, 1, 0), Point(-1, 0, 0), Point(1, 0, 0));
Intersection i = Intersection(3.5, &s, 0.2, 0.4);
ASSERT_EQ(i.u, 0.2);
ASSERT_EQ(i.v, 0.4);
}

View File

@@ -10,6 +10,7 @@
#include <math.h>
#include <gtest/gtest.h>
#include <triangle.h>
#include <smoothtriangle.h>
TEST(OBJFileTest, Ignoring_unrecognised_lines)
{
@@ -119,3 +120,57 @@ TEST(OBJFileTest, Triangle_in_groups)
ASSERT_EQ(t2->p2, parser.vertices(3));
ASSERT_EQ(t2->p3, parser.vertices(4));
}
TEST(OBJFileTest, Vertex_normal_record)
{
const char file[] = "vn 0 0 1\n"
"vn 0.707 0 -0.707\n"
"vn 1 2 3\n";
OBJFile parser = OBJFile();
parser.parseOBJFile(file);
ASSERT_EQ(parser.verticesNormal(1), Vector(0, 0, 1));
ASSERT_EQ(parser.verticesNormal(2), Vector(0.707, 0, -0.707));
ASSERT_EQ(parser.verticesNormal(3), Vector(1, 2, 3));
}
TEST(OBJFileTest, Faces_with_normal)
{
const char file[] = "v 0 1 0\n"
"v -1 0 0\n"
"v 1 0 0\n"
"\n"
"vn -1 0 0\n"
"vn 1 0 0\n"
"vn 0 1 0\n"
"\n"
"f 1//3 2//1 3//2\n"
"f 1/0/3 2/102/1 3/14/2\n";
OBJFile parser = OBJFile();
parser.parseOBJFile(file);
Group *g0 = parser.groups(0);
SmoothTriangle *t1 = (SmoothTriangle *)(*g0)[0];
SmoothTriangle *t2 = (SmoothTriangle *)(*g0)[1];
ASSERT_EQ(t1->p1, parser.vertices(1));
ASSERT_EQ(t1->p2, parser.vertices(2));
ASSERT_EQ(t1->p3, parser.vertices(3));
ASSERT_EQ(t1->n1, parser.verticesNormal(3));
ASSERT_EQ(t1->n2, parser.verticesNormal(1));
ASSERT_EQ(t1->n3, parser.verticesNormal(2));
ASSERT_EQ(t2->p1, parser.vertices(1));
ASSERT_EQ(t2->p2, parser.vertices(2));
ASSERT_EQ(t2->p3, parser.vertices(3));
ASSERT_EQ(t2->n1, parser.verticesNormal(3));
ASSERT_EQ(t2->n2, parser.verticesNormal(1));
ASSERT_EQ(t2->n3, parser.verticesNormal(2));
}

View File

@@ -0,0 +1,83 @@
/*
* DoRayMe - a quick and dirty Raytracer
* Smooth Triangle unit tests
*
* Created by Manoël Trapier
* Copyright (c) 2020 986-Studio.
*
*/
#include <smoothtriangle.h>
#include <math.h>
#include <gtest/gtest.h>
class SmoothTriTest : public SmoothTriangle
{
public:
SmoothTriTest(Point p1, Point p2, Point p3, Vector n1, Vector n2, Vector n3) : SmoothTriangle(p1, p2, p3, n1, n2, n3) {};
Intersect doLocalIntersect(Ray ray)
{
return this->localIntersect(ray);
};
Tuple doLocalNormalAt(Tuple point, Intersection *hit)
{
return this->localNormalAt(point, hit);
};
};
Point p1 = Point(0, 1, 0);
Point p2 = Point(-1, 0, 0);
Point p3 = Point(1, 0, 0);
Vector n1 = Vector(0, 1, 0);
Vector n2 = Vector(-1, 0, 0);
Vector n3 = Vector(1, 0, 0);
SmoothTriTest tri = SmoothTriTest(p1, p2, p3, n1, n2, n3);
TEST(SmoothTriangleTest, Construcring_a_smooth_triangle)
{
ASSERT_EQ(tri.p1, p1);
ASSERT_EQ(tri.p2, p2);
ASSERT_EQ(tri.p3, p3);
ASSERT_EQ(tri.n1, n1);
ASSERT_EQ(tri.n2, n2);
ASSERT_EQ(tri.n3, n3);
}
TEST(SmoothTriangleTest, An_intersection_with_a_smooth_triangle_stores_u_v)
{
Ray r = Ray(Point(-0.2, 0.3, -2), Vector(0, 0, 1));
Intersect xs = tri.doLocalIntersect(r);
ASSERT_TRUE(double_equal(xs[0].u, 0.45));
ASSERT_TRUE(double_equal(xs[0].v, 0.25));
}
TEST(SmoothTriangleTest, A_smooth_triangle_uses_u_v_to_interpolate_the_normal)
{
Intersection i = Intersection(1, &tri, 0.45, 0.25);
Tuple n = tri.doLocalNormalAt(Point(0, 0, 0), &i);
/* Temporary lower the precision */
set_equal_precision(0.00001);
ASSERT_EQ(n, Vector(-0.5547, 0.83205, 0));
set_equal_precision(FLT_EPSILON);
}
TEST(SmoothTriangleTest, Preparing_a_normal_on_a_smooth_triangle)
{
Intersection i = Intersection(1, &tri, 0.45, 0.25);
Tuple n = tri.normalAt(Point(0, 0, 0), &i);
/* Temporary lower the precision */
set_equal_precision(0.00001);
ASSERT_EQ(n, Vector(-0.5547, 0.83205, 0));
set_equal_precision(FLT_EPSILON);
}

View File

@@ -119,7 +119,7 @@ int main()
/* ----------------------------- */
/* Set the camera */
Camera camera = Camera(1280, 900, deg_to_rad(90));
Camera camera = Camera(40, 10, deg_to_rad(90));
camera.setTransform(viewTransform(Point(-2, 2.5, -3.5),
Point(2, 0, 3),
Vector(0, 1, 0)));