Browse Source

Add spherical mapping for UV Textures.

Godzil 4 years ago
parent
commit
3e37b5ca44

+ 89 - 0
source/pattern/texturemap.h

@@ -0,0 +1,89 @@
+/*
+ *  DoRayMe - a quick and dirty Raytracer
+ *  Texture Map header
+ *
+ *  Created by Manoël Trapier
+ *  Copyright (c) 2020 986-Studio.
+ *
+ */
+
+#ifndef DORAYME_TEXTUREMAP_H
+#define DORAYME_TEXTUREMAP_H
+
+#include <math.h>
+#include <tuple.h>
+#include <uv_pattern.h>
+#include <colour.h>
+enum TextureMapType
+{
+    SPHERICAL_MAP,
+    PLANAR_MAP,
+    CYLINDRICAL_MAP,
+};
+
+class TextureMap : public Pattern
+{
+private:
+    TextureMapType type;
+    UVPattern *pattern;
+public:
+    TextureMap(TextureMapType type, UVPattern *pattern) : Pattern(Colour(0, 0, 0), Colour(0, 0, 0)),
+                                                          type(type), pattern(pattern) { };
+
+    static void sphericalMap(Tuple point, double &u, double &v) {
+        /* First compute the azimuthal angle
+         * -π < theta <= π
+         * angle increases clockwise as viewed from above,
+         * which is opposite of what we want, but we'll fix it later.
+         */
+        double theta = atan2(point.x, point.z);
+
+        /* vec is the vector pointing from the sphere's origin (the world origin)
+         * to the point, which will also happen to be exactly equal to the sphere's
+         * radius.
+         */
+        Tuple vec = Vector(point.x, point.y, point.z);
+        double radius = vec.magnitude();
+
+        /* Let's compute the polar angle
+         * 0 <= phi <= π
+         */
+        double phi = acos(point.y / radius);
+
+        /* -0.5 < raw_u <= 0.5 */
+        double raw_u = theta / (2 * M_PI);
+
+        /* 0 <= u < 1
+         * here's also where we fix the direction of u. Subtract it from 1,
+         * so that it increases counterclockwise as viewed from above.
+         */
+        u = 1 - (raw_u + 0.5);
+
+        /* We want v to be 0 at the south pole of the sphere,
+         * and 1 at the north pole, so we have to "flip it over"
+         * by subtracting it from 1.
+         */
+        v = 1 - phi / M_PI;
+    }
+
+    Colour patternAt(Tuple point)
+    {
+        double u,v;
+        switch(this->type)
+        {
+        default:
+        case SPHERICAL_MAP:
+            this->sphericalMap(point, u, v);
+            break;
+        }
+
+        return this->pattern->uvPatternAt(u, v);
+    }
+
+    void dumpMe(FILE *fp) {
+        fprintf(fp, "\"Type\": \"TextureMap\",\n");
+        Pattern::dumpMe(fp);
+    }
+};
+
+#endif /* DORAYME_TEXTUREMAP_H */

+ 1 - 1
source/uvpattern/uv_checkers.h

@@ -19,7 +19,7 @@ public:
 
     Colour uvPatternAt(double u, double v) {
         double u2 = floor(u * this->width);
-        double v2 = floor(v * this->width);
+        double v2 = floor(v * this->height);
 
         if (fmod((u2 + v2), 2) == 0)
         {

+ 78 - 0
tests/pattern_test.cpp

@@ -20,6 +20,7 @@
 #include <material.h>
 #include <uv_pattern.h>
 #include <uv_checkers.h>
+#include <texturemap.h>
 
 #ifdef ENABLE_LUA_SUPPORT
 extern "C" {
@@ -273,4 +274,81 @@ TEST(PatternTest, Checkers_pattern_in_2D)
     ASSERT_EQ(checkers.uvPatternAt(0.0, 0.5), white);
     ASSERT_EQ(checkers.uvPatternAt(0.5, 0.5), black);
     ASSERT_EQ(checkers.uvPatternAt(1.0, 1.0), black);
+}
+
+TEST(PatternTest, Using_a_spherical_mapping_on_a_3d_point)
+{
+    Point testList[] = {
+            Point( 0,  0, -1),
+            Point( 1,  0,  0),
+            Point( 0,  0,  1),
+            Point(-1,  0,  0),
+            Point( 0,  1,  0),
+            Point( 0, -1,  0),
+            Point(sqrt(2)/2, sqrt(2)/2, 0),
+    };
+
+    double testResults[][2] {
+            {0.0,  0.5},
+            {0.25, 0.5},
+            {0.5,  0.5},
+            {0.75, 0.5},
+            {0.5,  1.0},
+            {0.5,  0.0},
+            {0.25, 0.75},
+    };
+
+    int testCount = sizeof(testList)/sizeof((testList)[0]);
+    int i;
+
+    TextureMap tm = TextureMap(SPHERICAL_MAP, nullptr);
+
+    for(i = 0; i < testCount; i++)
+    {
+        double u, v;
+        tm.sphericalMap(testList[i], u, v);
+        ASSERT_TRUE(double_equal(u, testResults[i][0]));
+        ASSERT_TRUE(double_equal(v, testResults[i][1]));
+    }
+}
+
+TEST(PatternTest, Using_a_texture_map_with_a_spherical_map)
+{
+    Point testList[] = {
+            Point(  0.4315,  0.4670,  0.7719),
+            Point( -0.9654,  0.2252, -0.0534),
+            Point(  0.1039,  0.7090,  0.6975),
+            Point( -0.4986, -0.7856, -0.3663),
+            Point( -0.0317, -0.9395,  0.3411),
+            Point(  0.4809, -0.7721,  0.4154),
+            Point(  0.0285, -0.9612, -0.2745),
+            Point( -0.5734, -0.2162, -0.7903),
+            Point(  0.7688, -0.1470,  0.6223),
+            Point( -0.7652,  0.2175,  0.6060),
+    };
+
+    Colour testResults[] {
+            white,
+            black,
+            white,
+            black,
+            black,
+            black,
+            black,
+            white,
+            black,
+            black,
+    };
+
+    int testCount = sizeof(testList)/sizeof((testList)[0]);
+    int i;
+
+    UVCheckers uvpat = UVCheckers(16, 8, black, white);
+    TextureMap tm = TextureMap(SPHERICAL_MAP, &uvpat);
+
+    for(i = 0; i < testCount; i++)
+    {
+        Colour ret = tm.patternAt(testList[i]);
+        EXPECT_EQ(ret, testResults[i]);
+    }
 }

+ 54 - 0
tests/uvmap_checkeredsphere.cpp

@@ -0,0 +1,54 @@
+/*
+ *  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 <texturemap.h>
+#include <uv_checkers.h>
+
+#include <transformation.h>
+
+int main()
+{
+    World w = World();
+
+    Light light = Light(POINT_LIGHT, Point(-10, 10, -10), Colour(1, 1, 1));
+    w.addLight(&light);
+
+    Sphere sp = Sphere();
+    UVCheckers checkers = UVCheckers(20, 10, Colour(0, 0.5, 0), Colour(1, 1, 1));
+    TextureMap tm = TextureMap(SPHERICAL_MAP, &checkers);
+    sp.material.pattern = &tm;
+    sp.material.ambient = 0.1;
+    sp.material.specular = 0.4;
+    sp.material.shininess = 10;
+    sp.material.diffuse = 0.6;
+
+    w.addObject(&sp);
+
+    /* Set the camera */
+    Camera camera = Camera(400, 400, 0.5);
+    camera.setTransform(viewTransform(Point(0, 0, -5),
+                                      Point(0, 0, 0),
+                                      Vector(0, 1, 0)));
+
+    /* Now render it */
+    Canvas image = camera.render(w);
+
+    image.SaveAsPNG("uvmap_checkeredsphere.png");
+
+    return 0;
+}