Browse Source

A new scene and some optimisations.

Godzil 4 years ago
parent
commit
c858b4dcde

+ 1 - 26
source/include/intersection.h

@@ -25,31 +25,7 @@ struct Computation
           object(object), t(t), hitPoint(point), eyeVector(eyev), normalVector(normalv), inside(inside),
           overHitPoint(overHitP), underHitPoint(underHitP), reflectVector(reflectV), n1(n1), n2(n2), material(objMat) { };
 
-    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));
-    };
+    double schlick();
 
     Shape *object;
     double t;
@@ -73,7 +49,6 @@ class Intersection
 public:
     double t;
     Shape *object;
-
     double u, v;
 
 public:

+ 7 - 0
source/include/shape.h

@@ -42,11 +42,14 @@ protected:
     ShapeType type;
     Matrix localTransformMatrix;
     bool locked;
+    uint64_t objectId;
 
 protected:
     virtual void localIntersect(Ray r, Intersect &xs) = 0;
     virtual Tuple localNormalAt(Tuple point, Intersection *hit) = 0;
 
+    static uint64_t newObjectId();
+
 public:
     Matrix transformMatrix;
     Matrix inverseTransform;
@@ -65,6 +68,9 @@ public:
     virtual void intersect(Ray &r, Intersect &xs);
     Tuple normalAt(Tuple point, Intersection *hit = nullptr);
 
+    uint64_t getObjectId() { return this->objectId; };
+    void setObjectId(uint64_t oid) { this->objectId = oid; };
+
     /* Bounding box points are always world value */
     virtual BoundingBox getLocalBounds();
     virtual BoundingBox getBounds();
@@ -92,6 +98,7 @@ public:
 
     void setTransform(Matrix transform);
     void setMaterial(Material material) { this->material = material; this->materialSet = true; };
+    Material *getMaterial();
     Ray transform(Ray &r) { return Ray(this->transformMatrix * r.origin, this->transformMatrix * r.direction); };
     Ray invTransform(Ray &r) { return Ray(this->inverseTransform * r.origin, this->inverseTransform * r.direction); };
 

+ 17 - 4
source/intersect.cpp

@@ -24,9 +24,17 @@ Intersect::Intersect()
 {
     this->allocated = MIN_ALLOC;
     this->list = (Intersection **)calloc(sizeof(Intersection *), MIN_ALLOC);
-    stats.addMalloc();
-    stats.addIntersect();
-    this->num = 0;
+    if (this->list != nullptr)
+    {
+        stats.addMalloc();
+        stats.addIntersect();
+        this->num = 0;
+    }
+    else
+    {
+        printf("ABORT: Allocation error [%s]!\n", __FUNCTION__);
+        exit(-1);
+    }
 }
 
 Intersect::~Intersect()
@@ -34,12 +42,17 @@ Intersect::~Intersect()
     int i;
     for(i = 0; i < this->num; i++)
     {
-        delete this->list[i];
+        if (this->list[i] != nullptr)
+        {
+            delete this->list[i];
+            this->list[i] = nullptr;
+        }
     }
     /* Free stuff */
     if (this->list != nullptr)
     {
         free(this->list);
+        this->list = nullptr;
     }
 }
 

+ 32 - 9
source/intersection.cpp

@@ -10,6 +10,32 @@
 #include <shape.h>
 #include <list.h>
 
+double Computation::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));
+};
+
 Computation Intersection::prepareComputation(Ray r, Intersect *xs)
 {
     double n1 = 1.0;
@@ -42,7 +68,7 @@ Computation Intersection::prepareComputation(Ray r, Intersect *xs)
     Tuple reflectV = r.direction.reflect(normalV);
 
     /* If the hit object is not transparent, there is no need to do that. I think .*/
-    if ((xs != nullptr)  && (xs->hit().object->material.transparency > 0))
+    if ((xs != nullptr) && (xs->hit().object->getMaterial()->transparency > 0))
     {
         List containers;
         int j;
@@ -54,7 +80,7 @@ Computation Intersection::prepareComputation(Ray r, Intersect *xs)
             {
                 if (!containers.isEmpty())
                 {
-                    n1 = containers.last()->material.refractiveIndex;
+                    n1 = containers.last()->getMaterial()->refractiveIndex;
                 }
             }
 
@@ -71,7 +97,7 @@ Computation Intersection::prepareComputation(Ray r, Intersect *xs)
             {
                 if (!containers.isEmpty())
                 {
-                    n2 = containers.last()->material.refractiveIndex;
+                    n2 = containers.last()->getMaterial()->refractiveIndex;
                 }
 
                 /* End the loop */
@@ -80,10 +106,7 @@ Computation Intersection::prepareComputation(Ray r, Intersect *xs)
         }
     }
 
-    Shape *s = this->object;
-
-    /* For now don't get root group material */
-    while((!s->materialSet) && (s->parent != nullptr)) { s = s->parent; }
+    Material *m = this->object->getMaterial();
 
     return Computation(this->object,
                        this->t,
@@ -96,5 +119,5 @@ Computation Intersection::prepareComputation(Ray r, Intersect *xs)
                        n1,
                        n2,
                        underHitP,
-                       &s->material);
-}
+                       m);
+}

+ 6 - 4
source/matrix.cpp

@@ -101,13 +101,15 @@ Matrix Matrix::operator*(const Matrix &b) const
     return ret;
 }
 
+#define FastGet4(_x, _y) (this->data[4 * (_x) + (_y)])
+
 /* TODO: Check if we can optimise this function. It is called a lot */
 Tuple Matrix::operator*(const Tuple &b) const
 {
-    return Tuple(b.x * this->get(0, 0) + b.y * this->get(0, 1) + b.z * this->get(0, 2) + b.w * this->get(0, 3),
-                 b.x * this->get(1, 0) + b.y * this->get(1, 1) + b.z * this->get(1, 2) + b.w * this->get(1, 3),
-                 b.x * this->get(2, 0) + b.y * this->get(2, 1) + b.z * this->get(2, 2) + b.w * this->get(2, 3),
-                 b.x * this->get(3, 0) + b.y * this->get(3, 1) + b.z * this->get(3, 2) + b.w * this->get(3, 3));
+    return Tuple(b.x * FastGet4(0, 0) + b.y * FastGet4(0, 1) + b.z * FastGet4(0, 2) + b.w * FastGet4(0, 3),
+                 b.x * FastGet4(1, 0) + b.y * FastGet4(1, 1) + b.z * FastGet4(1, 2) + b.w * FastGet4(1, 3),
+                 b.x * FastGet4(2, 0) + b.y * FastGet4(2, 1) + b.z * FastGet4(2, 2) + b.w * FastGet4(2, 3),
+                 b.x * FastGet4(3, 0) + b.y * FastGet4(3, 1) + b.z * FastGet4(3, 2) + b.w * FastGet4(3, 3));
 }
 
 Matrix Matrix::identity()

+ 1 - 1
source/renderstat.cpp

@@ -181,7 +181,7 @@ void RenderStats::printStats()
     printf("Malloc called           : %lld\n", this->mallocCallCount);
     printf("Realloc called          : %lld\n", this->reallocCallCount);
     printf("Bounding box missed     : %lld\n", this->discardedIntersectCount);
-    printf("Min depth atteined      : %lld\n", this->maxDepthAttained);
+    printf("Min depth attained      : %lld\n", this->maxDepthAttained);
     printf("Max intersect count     : %lld\n", this->maxIntersectOnARay);
     printf("==================================================\n");
 };

+ 4 - 1
source/shapes/objfile.cpp

@@ -484,6 +484,9 @@ int OBJFile::execLine(int argc, char *argv[], uint32_t currentLine)
                                        this->verticesNormal(vn[2]),
                                        this->verticesNormal(vn[3]));
             }
+            /* Set the object id to the OBJ one */
+            t->setObjectId(this->getObjectId());
+
             this->currentGroup->addObject(t);
             ret = 0;
         }
@@ -523,7 +526,7 @@ int OBJFile::execLine(int argc, char *argv[], uint32_t currentLine)
                                            this->verticesNormal(vn[i]),
                                            this->verticesNormal(vn[i + 1]));
                 }
-
+                t->setObjectId(this->getObjectId());
                 this->currentGroup->addObject(t);
             }
             ret = 0;

+ 24 - 2
source/shapes/shape.cpp

@@ -15,6 +15,7 @@
 
 Shape::Shape(ShapeType type)
 {
+    this->objectId = Shape::newObjectId();
     this->locked = false;
     this->parent = nullptr;
     this->dropShadow = true;
@@ -25,6 +26,17 @@ Shape::Shape(ShapeType type)
     this->updateTransform();
 }
 
+uint64_t Shape::newObjectId()
+{
+    static uint64_t id = 0;
+    uint64_t ret;
+
+    ret = id++;
+
+    return ret;
+}
+
+
 void Shape::intersect(Ray &r, Intersect &xs)
 {
     this->localIntersect(this->invTransform(r), xs);
@@ -95,6 +107,16 @@ BoundingBox Shape::getBounds()
     return ret;
 }
 
+Material *Shape::getMaterial()
+{
+    Shape *s = this;
+    while((!s->materialSet) && (s->parent != nullptr))
+    {
+        s = s->parent;
+    }
+    return &s->material;
+}
+
 void Shape::dumpMe(FILE *fp)
 {
     if (this->materialSet)
@@ -112,9 +134,9 @@ void Shape::dumpMe(FILE *fp)
         this->getBounds().dumpMe(fp);
         fprintf(fp, "},\n");
     }
-    fprintf(fp, "\"id\": %p,\n", this);
+    fprintf(fp, "\"id\": %ld,\n", this->getObjectId());
     if (this->parent)
     {
-        fprintf(fp, "\"parentId\": %p,\n", this->parent);
+        fprintf(fp, "\"parentId\": %ld,\n", this->parent->getObjectId());
     }
 }

+ 22 - 1
tests/CMakeLists.txt

@@ -128,7 +128,7 @@ file(DOWNLOAD
         )
 add_custom_command(
         TARGET uvmap_skybox POST_BUILD
-        COMMAND unzip -o ${CMAKE_SOURCE_DIR}/external/LancellottiChapel.zip -d LancellottiChapel
+        COMMAND unzip -qq -o ${CMAKE_SOURCE_DIR}/external/LancellottiChapel.zip -d LancellottiChapel
         WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/external/
 )
 add_custom_command(
@@ -138,6 +138,26 @@ add_custom_command(
         ${CMAKE_CURRENT_BINARY_DIR}/
 )
 
+# Dragon scene
+add_executable(dragon_scene)
+target_sources(dragon_scene PRIVATE dragon_scene.cpp)
+file(DOWNLOAD
+        http://raytracerchallenge.com/bonus/assets/dragon.zip
+        ${CMAKE_SOURCE_DIR}/external/dragon.zip
+        EXPECTED_HASH MD5=308b0f2aca1d48d24e6fc4584dfdf345
+        )
+add_custom_command(
+        TARGET dragon_scene POST_BUILD
+        COMMAND unzip -qq -o ${CMAKE_SOURCE_DIR}/external/dragon.zip
+        WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/external/
+)
+add_custom_command(
+        TARGET dragon_scene POST_BUILD
+        COMMAND ${CMAKE_COMMAND} -E copy
+        ${CMAKE_SOURCE_DIR}/external/dragon.obj
+        ${CMAKE_CURRENT_BINARY_DIR}/
+)
+
 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>)
@@ -160,6 +180,7 @@ add_test(NAME UVMap_AlignCheckPlane COMMAND $<TARGET_FILE:uvmap_aligncheckplane>
 add_test(NAME UVMap_CheckeredCube COMMAND $<TARGET_FILE:uvmap_checkeredcube>)
 add_test(NAME UVMap_Earth COMMAND $<TARGET_FILE:uvmap_earth>)
 add_test(NAME UVMap_Skybox COMMAND $<TARGET_FILE:uvmap_skybox>)
+add_test(NAME Dragon_Sceme COMMAND $<TARGET_FILE:dragon_scene>)
 add_test(NAME Test_Rendering COMMAND $<TARGET_FILE:test_render>)
 add_test(NAME Triangle_RenderTest COMMAND $<TARGET_FILE:triangle_rendertest>)
 add_test(NAME ChristmasBall_Rendering COMMAND $<TARGET_FILE:christmasball_render>)

+ 143 - 0
tests/dragon_scene.cpp

@@ -0,0 +1,143 @@
+/*
+ *  DoRayMe - a quick and dirty Raytracer
+ *  Render test for OBJ File using teapots in chapter 15.
+ *
+ *  Created by Manoël Trapier
+ *  Copyright (c) 2020 986-Studio.
+ *
+ */
+#include <world.h>
+#include <light.h>
+#include <plane.h>
+#include <cube.h>
+#include <sphere.h>
+#include <material.h>
+#include <colour.h>
+#include <canvas.h>
+#include <camera.h>
+#include <objfile.h>
+
+#include <pattern.h>
+#include <strippattern.h>
+#include <gradientpattern.h>
+#include <checkerspattern.h>
+#include <ringpattern.h>
+
+#include <transformation.h>
+#include <cylinder.h>
+
+int main()
+{
+    World w = World();
+
+    /* Add lights */
+   /* Light light1 = Light(POINT_LIGHT, Point(10, 100, 10), Colour(.1, .1, .1));
+    w.addLight(&light1);
+    Light light2 = Light(POINT_LIGHT, Point(10, 100, -10), Colour(.1, .1, .2));
+    w.addLight(&light2);
+    Light light3 = Light(POINT_LIGHT, Point(-10, 100, 10), Colour(.1, .1, .1));
+    w.addLight(&light3);
+    Light light4 = Light(POINT_LIGHT, Point(-10, 100, -10), Colour(.1, .1, .1));
+    w.addLight(&light4);*/
+
+    Light light1 = Light(POINT_LIGHT, Point(0, 100, 0), Colour(.3, .3, .3));
+    w.addLight(&light1);
+
+
+    Point lightPos = Point(3.8, 6.8, 4.5);
+    Light mouthLight = Light(POINT_LIGHT, lightPos, Colour(0.5, 0.5, 0.5));
+    w.addLight(&mouthLight);
+
+    Light mainLight = Light(POINT_LIGHT,  Point(8, 10, 16), Colour(1, 1, 1));
+    w.addLight(&mainLight);
+
+    /* ----------------------------- */
+#if 0
+    /* Spot light */
+    Sphere spot = Sphere();
+    spot.dropShadow = false;
+    spot.setTransform(translation(lightPos.x, lightPos.y, lightPos.z) * scaling(0.2, 0.2, 0.2));
+    w.addObject(&spot);
+
+    Cylinder X = Cylinder();
+    X.minCap = 0;
+    X.maxCap = 4;
+    Cylinder Y = Cylinder();
+    Y.minCap = 0;
+    Y.maxCap = 4;
+    Cylinder Z = Cylinder();
+    Z.minCap = 0;
+    Z.maxCap = 4;
+    Z.materialSet = Y.materialSet = X.materialSet = true;
+    X.material.ambient = 1;
+    X.material.specular = 0;
+    X.material.diffuse = 0;
+    Z.material = Y.material = X.material;
+    Z.dropShadow = Y.dropShadow = X.dropShadow = false;
+    X.material.colour = Colour(1, 0, 0);
+    Y.material.colour = Colour(0, 1, 0);
+    Z.material.colour = Colour(0, 0, 1);
+
+    Y.setTransform(translation(lightPos.x, lightPos.y, lightPos.z) * scaling(0.1, 1, 0.1));
+    X.setTransform(translation(lightPos.x, lightPos.y, lightPos.z) * rotationZ(M_PI/2) *  scaling(0.1, 1, 0.1));
+    Z.setTransform(translation(lightPos.x, lightPos.y, lightPos.z) * rotationX(M_PI/2) *  scaling(0.1, 1, 0.1));
+    w.addObject(&X);
+    w.addObject(&Y);
+    w.addObject(&Z);
+#endif
+
+    /* Floor */
+    Material floorMaterial;
+    floorMaterial.colour = Colour(0.2, 0.3, 0.3);
+    floorMaterial.reflective = 0.3;
+    floorMaterial.specular = 0.4;
+    floorMaterial.shininess = 5;
+    floorMaterial.ambient = 0;
+    floorMaterial.diffuse = 0.8;
+
+    /* Let's use a cube for the floor */
+    Cube floor = Cube();
+    floor.setMaterial(floorMaterial);
+    floor.setTransform(translation(0, -0.1, 0) * scaling(10, 0.1, 10));
+    w.addObject(&floor);
+
+    Material dragonMat;
+    dragonMat.colour = Colour(0.23, 0.75, 0.39);
+    dragonMat.reflective = 0.9;
+    dragonMat.transparency = 0.99;
+    dragonMat.specular = 0.9;
+    dragonMat.shininess = 50;
+    dragonMat.refractiveIndex = 1.6;
+    dragonMat.ambient = 0;
+    dragonMat.diffuse = 0.7;
+
+    Shape *dragon = OBJFile("dragon.obj").getBaseGroup();
+    dragon->setTransform(rotationY(M_PI * 0.75) * scaling(2, 2, 2));
+    dragon->setMaterial(dragonMat);
+    w.addObject(dragon);
+
+    /* ----------------------------- */
+/*
+    FILE *fpOut = fopen("dragon_worlddump.json", "wt");
+    if (fpOut)
+    {
+        w.dumpMe(fpOut);
+        fclose(fpOut);
+    }
+*/
+    OctreeOptimisation opt;
+    w.finalise(opt);
+
+    /* Set the camera */
+    Camera camera = Camera(800, 600, M_PI/2);
+    camera.setTransform(viewTransform(Point(-5, 10, 13),
+                                      Point(0, 1, 0),
+                                      Vector(0, 1, 0)));
+
+    /* Now render it */
+    Canvas image = camera.render(w, 5);
+
+    image.SaveAsPNG("ch15_teapot_objfile.png");
+
+    return 0;
+}

+ 2 - 2
tests/objfile_test.cpp

@@ -156,8 +156,8 @@ TEST(OBJFileTest, Faces_with_normal)
 
     Group *g0 = parser.groups(OBJ_DEFAULT_GROUP);
 
-    SmoothTriangle *t1 = (SmoothTriangle *)(*g0)[0];
-    SmoothTriangle *t2 = (SmoothTriangle *)(*g0)[1];
+    SmoothTriangle *t1 = (SmoothTriangle *)g0->getObject(0);
+    SmoothTriangle *t2 = (SmoothTriangle *)g0->getObject(1);
 
     ASSERT_EQ(t1->p1, parser.vertices(1));
     ASSERT_EQ(t1->p2, parser.vertices(2));