]> code.bitgloo.com Git - clyne/raytracer.git/commitdiff
move classes to headers
authorClyne Sullivan <clyne@bitgloo.com>
Tue, 14 May 2024 21:26:52 +0000 (17:26 -0400)
committerClyne Sullivan <clyne@bitgloo.com>
Tue, 14 May 2024 21:26:52 +0000 (17:26 -0400)
main.cpp
random.h [new file with mode: 0644]
sphere.h [new file with mode: 0644]
vec3.h
view.h [new file with mode: 0644]
world.h [new file with mode: 0644]

index 329b495f55e2b1caf0a21192a85b6ba78f875835..034aefa2ec85a655fc450fb208b0f4116659ffd9 100644 (file)
--- a/main.cpp
+++ b/main.cpp
@@ -1,16 +1,15 @@
-#include <random>
-
-inline double randomN()
-{
-    static std::uniform_real_distribution<double> distribution (0.0, 1.0);
-    static std::mt19937 generator;
-    return distribution(generator);
-}
+constexpr unsigned Width = 1000;
+constexpr double   Aspect = 16.0 / 9.0;
+constexpr unsigned Height = Width / Aspect;
+constexpr unsigned Threads = 8;
 
 #include "color.h"
 #include "ray.h"
 #include "renderer.h"
+#include "sphere.h"
 #include "vec3.h"
+#include "view.h"
+#include "world.h"
 
 #include "imgui.h"
 #include "imgui_impl_sdl2.h"
@@ -21,205 +20,19 @@ inline double randomN()
 #include <chrono>
 #include <cstring>
 #include <iostream>
-#include <memory>
-#include <optional>
 #include <ranges>
 #include <thread>
-#include <tuple>
-#include <vector>
-
-constexpr unsigned Width = 1000;
-constexpr double   Aspect = 16.0 / 9.0;
-constexpr unsigned Height = Width / Aspect;
-constexpr unsigned Threads = 8;
-
-enum class Material : int {
-    Lambertian,
-    Metal,
-    Dielectric
-};
-
-struct View
-{
-    static constexpr auto lookat = point3(0, 0, -1); // Point camera is looking at
-    static constexpr auto vup    = vec3(0, 1, 0);    // Camera-relative "up" direction
-
-    float fieldOfView = 90.f;
-    float focalLength;
-    float viewportHeight;
-    float viewportWidth;
-
-    point3 camera;
-    vec3 viewportX;
-    vec3 viewportY;
-    vec3 pixelDX;
-    vec3 pixelDY;
-    vec3 viewportUL;
-    vec3 pixelUL;
-
-    View() {
-        recalculate();
-    }
-
-    void recalculate() {
-        focalLength = (camera - lookat).length();
-        viewportHeight = 2 * std::tan(fieldOfView * 3.14159265 / 180.0 / 2.0) * focalLength;
-        viewportWidth  = viewportHeight * Aspect;
-
-        const auto w = (camera - lookat).normalize();
-        const auto u = cross(vup, w).normalize();
-        const auto v = cross(w, u);
-
-        viewportX = viewportWidth * u;
-        viewportY = -viewportHeight * v;
-
-        pixelDX = viewportX / Width;
-        pixelDY = viewportY / Height;
-        viewportUL = camera - focalLength * w - viewportX / 2 - viewportY / 2;
-        pixelUL = viewportUL + 0.5 * (pixelDX + pixelDY);
-    }
-
-    ray getRay(int x, int y, bool addRandom = false) const {
-        double X = x;
-        double Y = y;
-
-        if (addRandom) {
-            X += randomN() - 0.5;
-            Y += randomN() - 0.5;
-        }
-
-        auto pixel = pixelUL + X * pixelDX + Y * pixelDY;
-        return ray(camera, pixel - camera);
-    }
-};
-
-struct Sphere
-{
-    point3 center;
-    double radius;
-    Material M;
-    color tint;
-
-    std::pair<color, ray> scatter(const ray& r, double root) const {
-        const auto p = r.at(root);
-        auto normal = (p - center) / radius;
-
-        if (M == Material::Lambertian) {
-            return {tint, ray(p, normal + randomUnitSphere())};
-        } else if (M == Material::Metal) {
-            return {tint, ray(p, r.direction().reflect(normal))};
-        } else if (M == Material::Dielectric) {
-            constexpr auto index = 1.0 / 1.33;
-
-            const bool front = r.direction().dot(normal) < 0;
-            const auto ri = front ? 1.0 / index : index;
-            if (!front)
-                normal *= -1;
-
-            const auto dir = r.direction().normalize();
-            const double costh = std::fmin((-dir).dot(normal), 1);
-            const double sinth = std::sqrt(1 - costh * costh);
-
-            if (ri * sinth > 1)
-                return {color(1, 1, 1), ray(p, dir.reflect(normal))};
-            else
-                return {color(1, 1, 1), ray(p, dir.refract(normal, ri))};
-        } else {
-            return {};
-        }
-    }
-
-    std::optional<double> hit(const ray& r, double tmin, double tmax) const {
-        const vec3 oc = center - r.origin();
-        const auto a = r.direction().length_squared();
-        const auto h = r.direction().dot(oc);
-        const auto c = oc.length_squared() - radius * radius;
-        const auto discriminant = h * h - a * c;
-
-        if (discriminant < 0) {
-            return {}; // No hit
-        } else {
-            const auto sqrtd = sqrt(discriminant);
-
-            // Find the nearest root that lies in the acceptable range.
-            auto root = (h - sqrtd) / a;
-            if (root <= tmin || tmax <= root) {
-                root = (h + sqrtd) / a;
-                if (root <= tmin || tmax <= root)
-                    return {};
-            }
-
-            return root;
-        }
-    }
-};
-
-struct World
-{
-    std::vector<Sphere> objects;
-
-    void add(auto&&... args) {
-        objects.emplace_back(args...);
-    }
-
-    std::optional<std::pair<double, Sphere>> hit(const ray& r) const {
-        double closest = std::numeric_limits<double>::infinity();
-        Sphere sphere;
-
-        for (const auto& o : objects) {
-            if (auto t = o.hit(r, 0.001, closest); t) {
-                closest = *t;
-                sphere = o;
-            }
-        }
-
-        if (closest != std::numeric_limits<double>::infinity())
-            return std::pair {closest, sphere};
-        else
-            return {};
-    }
-};
 
 static World world;
 
-static color ray_color(const ray& r, int depth = 50)
-{
-    if (depth <= 0)
-        return {};
-
-    if (auto hit = world.hit(r); hit) {
-        const auto& [closest, sphere] = *hit;
-        const auto [atten, scat] = sphere.scatter(r, closest);
-        return atten * ray_color(scat, depth - 1);
-    } else {
-        const auto unitDir = r.direction().normalize();
-        const auto a = 0.5 * (unitDir.y() + 1.0);
-        return (1.0 - a) * color(1.0, 1.0, 1.0) + a * color(0.5, 0.7, 1.0);
-    }
-}
-
 static View Camera;
 static int SamplesPerPixel = 20;
 static std::unique_ptr<Renderer<Threads>> renderer;
 static std::chrono::time_point<std::chrono::high_resolution_clock> renderStart;
 static std::chrono::duration<double> renderTime;
 
-void initiateRender(SDL_Surface *canvas)
-{
-    renderStart = std::chrono::high_resolution_clock::now();
-    renderTime = std::chrono::duration<double>::zero();
-
-    auto func = [format = canvas->format](auto x, auto y, auto pbuf) {
-        auto col = std::ranges::fold_left(std::views::iota(0, SamplesPerPixel), color(),
-            [y, x](color c, int i) { return c + ray_color(Camera.getRay(x, y, true)); });
-
-        col = col / SamplesPerPixel * 255;
-        pbuf[y * Width + x] = SDL_MapRGBA(format, col.x(), col.y(), col.z(), 255);
-    };
-
-    Camera.recalculate();
-    renderer.reset(new Renderer<Threads>(func, Width, Height, (uint32_t *)canvas->pixels));
-}
+static color ray_color(const ray& r, int depth = 50);
+static void initiateRender(SDL_Surface *canvas);
 
 int main()
 {
@@ -302,10 +115,10 @@ int main()
             }
         } ImGui::End();
 
+        auto tex = SDL_CreateTextureFromSurface(painter, canvas);
+
         ImGui::Render();
-        //SDL_RenderSetScale(painter, io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
         SDL_RenderClear(painter);
-        auto tex = SDL_CreateTextureFromSurface(painter, canvas);
         SDL_RenderCopy(painter, tex, nullptr, nullptr);
         ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData());
         SDL_RenderPresent(painter);
@@ -324,3 +137,35 @@ int main()
     SDL_Quit();
 }
 
+color ray_color(const ray& r, int depth)
+{
+    if (depth <= 0)
+        return {};
+
+    if (auto hit = world.hit(r); hit) {
+        const auto& [closest, sphere] = *hit;
+        const auto [atten, scat] = sphere.scatter(r, closest);
+        return atten * ray_color(scat, depth - 1);
+    } else {
+        const auto unitDir = r.direction().normalize();
+        const auto a = 0.5 * (unitDir.y() + 1.0);
+        return (1.0 - a) * color(1.0, 1.0, 1.0) + a * color(0.5, 0.7, 1.0);
+    }
+}
+
+void initiateRender(SDL_Surface *canvas)
+{
+    renderStart = std::chrono::high_resolution_clock::now();
+    renderTime = std::chrono::duration<double>::zero();
+
+    auto func = [format = canvas->format](auto x, auto y, auto pbuf) {
+        auto col = std::ranges::fold_left(std::views::iota(0, SamplesPerPixel), color(),
+            [y, x](color c, int i) { return c + ray_color(Camera.getRay(x, y, true)); });
+
+        col = col / SamplesPerPixel * 255;
+        pbuf[y * Width + x] = SDL_MapRGBA(format, col.x(), col.y(), col.z(), 255);
+    };
+
+    Camera.recalculate();
+    renderer.reset(new Renderer<Threads>(func, Width, Height, (uint32_t *)canvas->pixels));
+}
diff --git a/random.h b/random.h
new file mode 100644 (file)
index 0000000..fb0c7b3
--- /dev/null
+++ b/random.h
@@ -0,0 +1,14 @@
+#ifndef RANDOM_H
+#define RANDOM_H
+
+#include <random>
+
+inline double randomN()
+{
+    static std::uniform_real_distribution<double> distribution (0.0, 1.0);
+    static std::mt19937 generator;
+    return distribution(generator);
+}
+
+#endif // RANDOM_H
+
diff --git a/sphere.h b/sphere.h
new file mode 100644 (file)
index 0000000..c27f29b
--- /dev/null
+++ b/sphere.h
@@ -0,0 +1,80 @@
+#ifndef SPHERE_H
+#define SPHERE_H
+
+#include "color.h"
+#include "ray.h"
+#include "vec3.h"
+
+#include <cmath>
+#include <optional>
+#include <tuple>
+
+enum class Material : int {
+    Lambertian,
+    Metal,
+    Dielectric
+};
+
+struct Sphere
+{
+    point3 center;
+    double radius;
+    Material M;
+    color tint;
+
+    std::pair<color, ray> scatter(const ray& r, double root) const {
+        const auto p = r.at(root);
+        auto normal = (p - center) / radius;
+
+        if (M == Material::Lambertian) {
+            return {tint, ray(p, normal + randomUnitSphere())};
+        } else if (M == Material::Metal) {
+            return {tint, ray(p, r.direction().reflect(normal))};
+        } else if (M == Material::Dielectric) {
+            constexpr auto index = 1.0 / 1.33;
+
+            const bool front = r.direction().dot(normal) < 0;
+            const auto ri = front ? 1.0 / index : index;
+            if (!front)
+                normal *= -1;
+
+            const auto dir = r.direction().normalize();
+            const double costh = std::fmin((-dir).dot(normal), 1);
+            const double sinth = std::sqrt(1 - costh * costh);
+
+            if (ri * sinth > 1)
+                return {color(1, 1, 1), ray(p, dir.reflect(normal))};
+            else
+                return {color(1, 1, 1), ray(p, dir.refract(normal, ri))};
+        } else {
+            return {};
+        }
+    }
+
+    std::optional<double> hit(const ray& r, double tmin, double tmax) const {
+        const vec3 oc = center - r.origin();
+        const auto a = r.direction().length_squared();
+        const auto h = r.direction().dot(oc);
+        const auto c = oc.length_squared() - radius * radius;
+        const auto discriminant = h * h - a * c;
+
+        if (discriminant < 0) {
+            return {}; // No hit
+        } else {
+            const auto sqrtd = std::sqrt(discriminant);
+
+            // Find the nearest root that lies in the acceptable range.
+            auto root = (h - sqrtd) / a;
+            if (root <= tmin || tmax <= root) {
+                root = (h + sqrtd) / a;
+                if (root <= tmin || tmax <= root)
+                    return {};
+            }
+
+            return root;
+        }
+    }
+};
+
+#endif // SPHERE_H
+
diff --git a/vec3.h b/vec3.h
index d81a1a14f7ca04ae4ee5fc04d2970384fab951cc..46367d21a51c8baa357ce9e6b4e2ca967453c67f 100644 (file)
--- a/vec3.h
+++ b/vec3.h
@@ -1,11 +1,11 @@
 #ifndef VEC3_H
 #define VEC3_H
 
+#include "random.h"
+
 #include <cmath>
 #include <iostream>
 
-using std::sqrt;
-
 struct vec3 {
   public:
     double e[3];
@@ -64,7 +64,7 @@ struct vec3 {
     }
     
     constexpr double length() const {
-        return sqrt(length_squared());
+        return std::sqrt(length_squared());
     }
 
     constexpr double length_squared() const {
@@ -94,20 +94,11 @@ struct vec3 {
         auto rpara = v * -std::sqrt(std::fabs(1.0 - rperp.length_squared()));
         return rperp + rpara;
     }
-
-    static vec3 random() {
-        return vec3(randomN(), randomN(), randomN());
-    }
-
-    //static vec3 random(double min, double max) {
-    //    return vec3(randomN(min,max), randomN(min,max), randomN(min,max));
-    //}
 };
 
 // point3 is just an alias for vec3, but useful for geometric clarity in the code.
 using point3 = vec3;
 
-
 // Vector Utility Functions
 
 inline std::ostream& operator<<(std::ostream& out, const vec3& v) {
@@ -122,7 +113,6 @@ constexpr inline vec3 operator/(double t, const vec3& v) {
     return v * (1 / t);
 }
 
-
 inline constexpr vec3 cross(const vec3& u, const vec3& v) {
     return vec3(u.e[1] * v.e[2] - u.e[2] * v.e[1],
                 u.e[2] * v.e[0] - u.e[0] * v.e[2],
@@ -135,7 +125,7 @@ inline constexpr vec3 unit_vector(const vec3& v) {
 
 inline vec3 randomUnitSphere() {
     for (;;) {
-        if (auto p = vec3::random() * 2 - vec3(1, 1, 1); p.length_squared() < 1)
+        if (auto p = vec3(randomN(), randomN(), randomN()) * 2 - vec3(1, 1, 1); p.length_squared() < 1)
             return p;
     }
 }
diff --git a/view.h b/view.h
new file mode 100644 (file)
index 0000000..e27176e
--- /dev/null
+++ b/view.h
@@ -0,0 +1,64 @@
+#ifndef VIEW_H
+#define VIEW_H
+
+#include "random.h"
+#include "vec3.h"
+
+#include <cmath>
+
+struct View
+{
+    static constexpr auto lookat = point3(0, 0, -1); // Point camera is looking at
+    static constexpr auto vup    = vec3(0, 1, 0);    // Camera-relative "up" direction
+
+    float fieldOfView = 90.f;
+    float focalLength;
+    float viewportHeight;
+    float viewportWidth;
+
+    point3 camera;
+    vec3 viewportX;
+    vec3 viewportY;
+    vec3 pixelDX;
+    vec3 pixelDY;
+    vec3 viewportUL;
+    vec3 pixelUL;
+
+    View() {
+        recalculate();
+    }
+
+    void recalculate() {
+        focalLength = (camera - lookat).length();
+        viewportHeight = 2 * std::tan(fieldOfView * 3.14159265 / 180.0 / 2.0) * focalLength;
+        viewportWidth  = viewportHeight * Aspect;
+
+        const auto w = (camera - lookat).normalize();
+        const auto u = cross(vup, w).normalize();
+        const auto v = cross(w, u);
+
+        viewportX = viewportWidth * u;
+        viewportY = -viewportHeight * v;
+
+        pixelDX = viewportX / Width;
+        pixelDY = viewportY / Height;
+        viewportUL = camera - focalLength * w - viewportX / 2 - viewportY / 2;
+        pixelUL = viewportUL + 0.5 * (pixelDX + pixelDY);
+    }
+
+    ray getRay(int x, int y, bool addRandom = false) const {
+        double X = x;
+        double Y = y;
+
+        if (addRandom) {
+            X += randomN() - 0.5;
+            Y += randomN() - 0.5;
+        }
+
+        auto pixel = pixelUL + X * pixelDX + Y * pixelDY;
+        return ray(camera, pixel - camera);
+    }
+};
+
+#endif // VIEW_H
+
diff --git a/world.h b/world.h
new file mode 100644 (file)
index 0000000..4e01bcf
--- /dev/null
+++ b/world.h
@@ -0,0 +1,38 @@
+#ifndef WORLD_H
+#define WORLD_H
+
+#include "sphere.h"
+
+#include <limits>
+#include <optional>
+#include <tuple>
+#include <vector>
+
+struct World
+{
+    std::vector<Sphere> objects;
+
+    void add(auto&&... args) {
+        objects.emplace_back(args...);
+    }
+
+    std::optional<std::pair<double, Sphere>> hit(const ray& r) const {
+        double closest = std::numeric_limits<double>::infinity();
+        Sphere sphere;
+
+        for (const auto& o : objects) {
+            if (auto t = o.hit(r, 0.001, closest); t) {
+                closest = *t;
+                sphere = o;
+            }
+        }
+
+        if (closest != std::numeric_limits<double>::infinity())
+            return std::pair {closest, sphere};
+        else
+            return {};
+    }
+};
+
+#endif // WORLD_H
+