aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClyne Sullivan <clyne@bitgloo.com>2024-05-14 17:26:52 -0400
committerClyne Sullivan <clyne@bitgloo.com>2024-05-14 17:26:52 -0400
commitaf1740bde341ef7703696cb7fd26d66cd8a37462 (patch)
treecc2276fbe88fd225f068a0eb89ea8b28846f4961
parent8a8fab0c68d867d1e1e870252819d15e2b6d0c6f (diff)
move classes to headers
-rw-r--r--main.cpp241
-rw-r--r--random.h14
-rw-r--r--sphere.h80
-rw-r--r--vec3.h18
-rw-r--r--view.h64
-rw-r--r--world.h38
6 files changed, 243 insertions, 212 deletions
diff --git a/main.cpp b/main.cpp
index 329b495..034aefa 100644
--- 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
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
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 d81a1a1..46367d2 100644
--- 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
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
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
+