add hitable, add sphere, first working prototype!
This commit is contained in:
parent
ffc8cdbdeb
commit
a67c2d19d6
27
src/canvas.h
27
src/canvas.h
|
@ -1,29 +1,44 @@
|
|||
#ifndef CANVAS_H
|
||||
#define CANVAS_H
|
||||
|
||||
#include "vec3.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
struct Color {
|
||||
using Color = Vec3;
|
||||
|
||||
struct Rgb {
|
||||
uint8_t r, g, b;
|
||||
};
|
||||
|
||||
inline Rgb to_rgb(const Color &c) {
|
||||
auto clamp = [](double x) { return x < 0.0 ? 0.0 : (x > 1.0 ? 1.0 : x); };
|
||||
|
||||
return {static_cast<uint8_t>(255.999 * clamp(c.x())),
|
||||
static_cast<uint8_t>(255.999 * clamp(c.y())),
|
||||
static_cast<uint8_t>(255.999 * clamp(c.z()))};
|
||||
}
|
||||
|
||||
struct Canvas {
|
||||
std::vector<Color> pixels;
|
||||
std::vector<Rgb> pixels;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
Canvas(int w, int h) : pixels(w * h), width(w), height(h) {}
|
||||
|
||||
void set_pixel(int x, int y, Color color) {
|
||||
void set_pixel(int x, int y, Rgb color) {
|
||||
if (x >= 0 && x < width && y >= 0 && y < height) {
|
||||
pixels[y * width + x] = color;
|
||||
}
|
||||
}
|
||||
|
||||
Color get_pixel(int x, int y) const {
|
||||
return pixels[y * width + x];
|
||||
void set_pixel(int x, int y, Color color) {
|
||||
if (x >= 0 && x < width && y >= 0 && y < height) {
|
||||
pixels[y * width + x] = to_rgb(color);
|
||||
}
|
||||
}
|
||||
|
||||
Rgb get_pixel(int x, int y) const { return pixels[y * width + x]; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
#ifndef RTWEEKEND_H
|
||||
#define RTWEEKEND_H
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
|
||||
// C++ Std Usings
|
||||
|
||||
using std::make_shared;
|
||||
using std::shared_ptr;
|
||||
|
||||
// Constants
|
||||
|
||||
const double infinity = std::numeric_limits<double>::infinity();
|
||||
const double pi = 3.1415926535897932385;
|
||||
|
||||
// Utility Functions
|
||||
|
||||
inline double degrees_to_radians(double degrees) {
|
||||
return degrees * pi / 180.0;
|
||||
}
|
||||
|
||||
// Common Headers
|
||||
|
||||
#include "ray.h"
|
||||
#include "vec3.h"
|
||||
#include "canvas.h"
|
||||
|
||||
#endif
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef HITTABLE_H
|
||||
#define HITTABLE_H
|
||||
|
||||
#include "ray.h"
|
||||
|
||||
class hit_record {
|
||||
public:
|
||||
Point3 p;
|
||||
Vec3 normal;
|
||||
double t;
|
||||
};
|
||||
|
||||
class hittable {
|
||||
public:
|
||||
virtual ~hittable() = default;
|
||||
|
||||
virtual bool hit(const Ray& r, double ray_tmin, double ray_tmax, hit_record& rec) const = 0;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,41 @@
|
|||
#ifndef HITTABLE_LIST_H
|
||||
#define HITTABLE_LIST_H
|
||||
|
||||
#include "hittable.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
using std::make_shared;
|
||||
using std::shared_ptr;
|
||||
|
||||
class hittable_list : public hittable {
|
||||
public:
|
||||
std::vector<shared_ptr<hittable>> objects;
|
||||
|
||||
hittable_list() {}
|
||||
hittable_list(shared_ptr<hittable> object) { add(object); }
|
||||
|
||||
void clear() { objects.clear(); }
|
||||
|
||||
void add(shared_ptr<hittable> object) { objects.push_back(object); }
|
||||
|
||||
bool hit(const Ray &r, double ray_tmin, double ray_tmax,
|
||||
hit_record &rec) const override {
|
||||
hit_record temp_rec;
|
||||
bool hit_anything = false;
|
||||
auto closest_so_far = ray_tmax;
|
||||
|
||||
for (const auto &object : objects) {
|
||||
if (object->hit(r, ray_tmin, closest_so_far, temp_rec)) {
|
||||
hit_anything = true;
|
||||
closest_so_far = temp_rec.t;
|
||||
rec = temp_rec;
|
||||
}
|
||||
}
|
||||
|
||||
return hit_anything;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -5,7 +5,14 @@
|
|||
#include <thread>
|
||||
|
||||
int main() {
|
||||
Canvas canvas(800, 600);
|
||||
auto aspect_ratio = 16.0 / 9.0;
|
||||
int image_width = 400;
|
||||
|
||||
// Calculate the image height, and ensure that it's at least 1.
|
||||
int image_height = int(image_width / aspect_ratio);
|
||||
image_height = (image_height < 1) ? 1 : image_height;
|
||||
|
||||
Canvas canvas(image_width, image_height);
|
||||
RingBuffer<SimulationState> buffer(64);
|
||||
|
||||
std::thread sim(simulation_thread, std::ref(buffer));
|
||||
|
|
23
src/ray.h
23
src/ray.h
|
@ -1,24 +1,23 @@
|
|||
#ifndef RAY_H
|
||||
#define RAY_H
|
||||
|
||||
#include "canvas.h"
|
||||
#include "vec3.h"
|
||||
|
||||
class Ray {
|
||||
public:
|
||||
Ray() {}
|
||||
public:
|
||||
Ray() {}
|
||||
|
||||
Ray(const Point3& origin, const Vec3& direction) : orig(origin), dir(direction) {}
|
||||
Ray(const Point3 &origin, const Vec3 &direction)
|
||||
: orig(origin), dir(direction) {}
|
||||
|
||||
const Point3& origin() const { return orig; }
|
||||
const Vec3& direction() const { return dir; }
|
||||
const Point3 &origin() const { return orig; }
|
||||
const Vec3 &direction() const { return dir; }
|
||||
|
||||
Point3 at(double t) const {
|
||||
return orig + t*dir;
|
||||
}
|
||||
|
||||
private:
|
||||
Point3 orig;
|
||||
Vec3 dir;
|
||||
Point3 at(double t) const { return orig + t * dir; }
|
||||
private:
|
||||
Point3 orig;
|
||||
Vec3 dir;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#include "simulation.h"
|
||||
#include "canvas.h"
|
||||
#include "vec3.h"
|
||||
#include "common.h"
|
||||
#include "hittable.h"
|
||||
#include "hittable_list.h"
|
||||
#include "sphere.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
|
@ -14,98 +16,41 @@ std::atomic<bool> running(true);
|
|||
SimulationState initialize_solar_system() {
|
||||
SimulationState state;
|
||||
state.bodies.push_back(Body{"Sol", Vec3(0.0, 0.0, 0.0), Vec3(0.0, 0.0, 0.0),
|
||||
1.989e30}); // a star similar to the sun
|
||||
1.989e30, 100.0}); // a star similar to the sun
|
||||
state.bodies.push_back(Body{"Mercury", Vec3(57.909e9, 0.0, 0.0),
|
||||
Vec3(0.0, 47.36e3, 0.0),
|
||||
0.33011e24}); // a planet similar to mercury
|
||||
Vec3(0.0, 47.36e3, 0.0), 0.33011e24,
|
||||
10}); // a planet similar to mercury
|
||||
state.bodies.push_back(Body{"Venus", Vec3(108.209e9, 0.0, 0.0),
|
||||
Vec3(0.0, 35.02e3, 0.0),
|
||||
4.8675e24}); // a planet similar to venus
|
||||
Vec3(0.0, 35.02e3, 0.0), 4.8675e24,
|
||||
30}); // a planet similar to venus
|
||||
state.bodies.push_back(Body{"Earth", Vec3(149.596e9, 0.0, 0.0),
|
||||
Vec3(0.0, 29.78e3, 0.0),
|
||||
5.9724e24}); // a planet similar to earth
|
||||
Vec3(0.0, 29.78e3, 0.0), 5.9724e24,
|
||||
30}); // a planet similar to earth
|
||||
state.bodies.push_back(Body{"Mars", Vec3(227.923e9, 0.0, 0.0),
|
||||
Vec3(0.0, 24.07e3, 0.0),
|
||||
0.64171e24}); // a planet similar to mars
|
||||
Vec3(0.0, 24.07e3, 0.0), 0.64171e24,
|
||||
20}); // a planet similar to mars
|
||||
state.bodies.push_back(Body{"Jupiter", Vec3(778.570e9, 0.0, 0.0),
|
||||
Vec3(0.0, 13e3, 0.0),
|
||||
1898.19e24}); // a planet similar to jupiter
|
||||
Vec3(0.0, 13e3, 0.0), 1898.19e24,
|
||||
50}); // a planet similar to jupiter
|
||||
state.bodies.push_back(Body{"Saturn", Vec3(1433.529e9, 0.0, 0.0),
|
||||
Vec3(0.0, 9.68e3, 0.0),
|
||||
568.34e24}); // a planet similar to saturn
|
||||
Vec3(0.0, 9.68e3, 0.0), 568.34e24,
|
||||
40}); // a planet similar to saturn
|
||||
state.bodies.push_back(Body{"Uranus", Vec3(2872.463e9, 0.0, 0.0),
|
||||
Vec3(0.0, 6.80e3, 0.0),
|
||||
86.813e24}); // a planet similar to uranus
|
||||
Vec3(0.0, 6.80e3, 0.0), 86.813e24,
|
||||
30}); // a planet similar to uranus
|
||||
state.bodies.push_back(Body{"Neptune", Vec3(4495.060e9, 0.0, 0.0),
|
||||
Vec3(0.0, 5.43e3, 0.0),
|
||||
102.413e24}); // a planet similar to neptune
|
||||
Vec3(0.0, 5.43e3, 0.0), 102.413e24,
|
||||
30}); // a planet similar to neptune
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Step .
|
||||
*/
|
||||
void simulate_step(SimulationState state) {
|
||||
double t_0 = 0;
|
||||
double t = t_0;
|
||||
double dt = 86400;
|
||||
double BIG_G = 6.67e-11; // gravitational constant
|
||||
|
||||
size_t n_bodies = state.bodies.size();
|
||||
for (size_t m1_idx = 0; m1_idx < n_bodies; m1_idx++) {
|
||||
Vec3 a_g = {0, 0, 0};
|
||||
|
||||
for (size_t m2_idx = 0; m2_idx < n_bodies; m2_idx++) {
|
||||
if (m2_idx != m1_idx) {
|
||||
Vec3 r_vector;
|
||||
|
||||
r_vector.x() = state.bodies[m1_idx].position.x() -
|
||||
state.bodies[m2_idx].position.x();
|
||||
r_vector.y() = state.bodies[m1_idx].position.y() -
|
||||
state.bodies[m2_idx].position.y();
|
||||
r_vector.z() = state.bodies[m1_idx].position.z() -
|
||||
state.bodies[m2_idx].position.z();
|
||||
|
||||
double r_mag =
|
||||
sqrt(r_vector.x() * r_vector.x() + r_vector.y() * r_vector.y() +
|
||||
r_vector.z() * r_vector.z());
|
||||
|
||||
double acceleration =
|
||||
-1.0 * BIG_G * (state.bodies[m2_idx].mass) / pow(r_mag, 2.0);
|
||||
|
||||
Vec3 r_unit_vector = {r_vector.x() / r_mag, r_vector.y() / r_mag,
|
||||
r_vector.z() / r_mag};
|
||||
|
||||
a_g.x() += acceleration * r_unit_vector.x();
|
||||
a_g.y() += acceleration * r_unit_vector.y();
|
||||
a_g.z() += acceleration * r_unit_vector.z();
|
||||
}
|
||||
}
|
||||
|
||||
state.bodies[m1_idx].velocity.x() += a_g.x() * dt;
|
||||
state.bodies[m1_idx].velocity.y() += a_g.y() * dt;
|
||||
state.bodies[m1_idx].velocity.z() += a_g.z() * dt;
|
||||
}
|
||||
|
||||
for (size_t entity_idx = 0; entity_idx < n_bodies; entity_idx++) {
|
||||
state.bodies[entity_idx].position.x() +=
|
||||
state.bodies[entity_idx].velocity.x() * dt;
|
||||
state.bodies[entity_idx].position.y() +=
|
||||
state.bodies[entity_idx].velocity.y() * dt;
|
||||
state.bodies[entity_idx].position.z() +=
|
||||
state.bodies[entity_idx].velocity.z() * dt;
|
||||
}
|
||||
|
||||
t += dt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulation thread.
|
||||
*/
|
||||
void simulation_thread(RingBuffer<SimulationState> &buffer) {
|
||||
SimulationState state = initialize_solar_system();
|
||||
double dt = 0.01; // if we didnt do this it would run too fast for render
|
||||
std::chrono::duration<double> elapsed;
|
||||
float dt = 0.01; // if we didnt do this it would run too fast for render
|
||||
std::chrono::duration<float> elapsed;
|
||||
|
||||
auto last = std::chrono::high_resolution_clock::now();
|
||||
|
||||
|
@ -114,8 +59,57 @@ void simulation_thread(RingBuffer<SimulationState> &buffer) {
|
|||
elapsed = now - last;
|
||||
|
||||
if (elapsed.count() >= dt) {
|
||||
simulate_step(state);
|
||||
float t_0 = 0;
|
||||
float t = t_0;
|
||||
float dt = 86400;
|
||||
float BIG_G = 6.67e-11; // gravitational constant
|
||||
|
||||
size_t n_bodies = state.bodies.size();
|
||||
for (size_t m1_idx = 0; m1_idx < n_bodies; m1_idx++) {
|
||||
Vec3 a_g = {0, 0, 0};
|
||||
|
||||
for (size_t m2_idx = 0; m2_idx < n_bodies; m2_idx++) {
|
||||
if (m2_idx != m1_idx) {
|
||||
Vec3 r_vector;
|
||||
|
||||
r_vector.x() = state.bodies[m1_idx].position.x() -
|
||||
state.bodies[m2_idx].position.x();
|
||||
r_vector.y() = state.bodies[m1_idx].position.y() -
|
||||
state.bodies[m2_idx].position.y();
|
||||
r_vector.z() = state.bodies[m1_idx].position.z() -
|
||||
state.bodies[m2_idx].position.z();
|
||||
|
||||
float r_mag =
|
||||
sqrt(r_vector.x() * r_vector.x() + r_vector.y() * r_vector.y() +
|
||||
r_vector.z() * r_vector.z());
|
||||
|
||||
float acceleration =
|
||||
-1.0 * BIG_G * (state.bodies[m2_idx].mass) / pow(r_mag, 2.0);
|
||||
|
||||
Vec3 r_unit_vector = {r_vector.x() / r_mag, r_vector.y() / r_mag,
|
||||
r_vector.z() / r_mag};
|
||||
|
||||
a_g.x() += acceleration * r_unit_vector.x();
|
||||
a_g.y() += acceleration * r_unit_vector.y();
|
||||
a_g.z() += acceleration * r_unit_vector.z();
|
||||
}
|
||||
}
|
||||
|
||||
state.bodies[m1_idx].velocity.x() += a_g.x() * dt;
|
||||
state.bodies[m1_idx].velocity.y() += a_g.y() * dt;
|
||||
state.bodies[m1_idx].velocity.z() += a_g.z() * dt;
|
||||
}
|
||||
|
||||
for (size_t entity_idx = 0; entity_idx < n_bodies; entity_idx++) {
|
||||
state.bodies[entity_idx].position.x() +=
|
||||
state.bodies[entity_idx].velocity.x() * dt;
|
||||
state.bodies[entity_idx].position.y() +=
|
||||
state.bodies[entity_idx].velocity.y() * dt;
|
||||
state.bodies[entity_idx].position.z() +=
|
||||
state.bodies[entity_idx].velocity.z() * dt;
|
||||
}
|
||||
|
||||
t += dt;
|
||||
// Try to push it into the buffer
|
||||
while (!buffer.push(state)) {
|
||||
// Buffer full, drop or wait (e.g. yield or sleep)
|
||||
|
@ -130,21 +124,59 @@ void simulation_thread(RingBuffer<SimulationState> &buffer) {
|
|||
}
|
||||
}
|
||||
|
||||
Color ray_color(const Ray &r, const hittable &world) {
|
||||
hit_record rec;
|
||||
if (world.hit(r, 0, infinity, rec)) {
|
||||
return 0.5 * (rec.normal + Color(1, 1, 1));
|
||||
}
|
||||
|
||||
Vec3 unit_direction = unit_vector(r.direction());
|
||||
auto a = 0.5 * (unit_direction.y() + 1.0);
|
||||
return (1.0 - a) * Color(1.0, 1.0, 1.0) + a * Color(0.5, 0.7, 1.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render raytracing.
|
||||
*/
|
||||
void render(SimulationState state, Canvas &canvas) {
|
||||
hittable_list world;
|
||||
|
||||
const double SCALE = 1e9;
|
||||
|
||||
for (auto b : state.bodies) {
|
||||
auto bb = b.position / SCALE;
|
||||
world.add(make_shared<sphere>(Vec3(bb.x(), bb.z(), bb.y()), b.radius));
|
||||
}
|
||||
|
||||
// Camera
|
||||
auto focal_length = 1.0;
|
||||
auto viewport_height = 2.0;
|
||||
auto viewport_width = viewport_height * (float(canvas.width) / canvas.height);
|
||||
auto camera_center = Point3(0, 0, -100);
|
||||
|
||||
// Calculate the vectors across the horizontal and down the vertical viewport
|
||||
// edges.
|
||||
auto viewport_u = Vec3(viewport_width, 0, 0);
|
||||
auto viewport_v = Vec3(0, -viewport_height, 0);
|
||||
|
||||
// Calculate the horizontal and vertical delta vectors from pixel to pixel.
|
||||
auto pixel_delta_u = viewport_u / canvas.width;
|
||||
auto pixel_delta_v = viewport_v / canvas.height;
|
||||
|
||||
// Calculate the location of the upper left pixel.
|
||||
auto viewport_upper_left = camera_center - Vec3(0, 0, focal_length) -
|
||||
viewport_u / 2 - viewport_v / 2;
|
||||
auto pixel00_loc =
|
||||
viewport_upper_left + 0.5 * (pixel_delta_u + pixel_delta_v);
|
||||
|
||||
for (int j = 0; j < canvas.height; j++) {
|
||||
for (int i = 0; i < canvas.width; i++) {
|
||||
auto r = double(i) / (canvas.width - 1);
|
||||
auto g = double(j) / (canvas.height - 1);
|
||||
auto b = 0.0;
|
||||
auto pixel_center =
|
||||
pixel00_loc + (i * pixel_delta_u) + (j * pixel_delta_v);
|
||||
auto ray_direction = pixel_center - camera_center;
|
||||
Ray r(camera_center, ray_direction);
|
||||
|
||||
uint8_t ir = uint8_t(255.999 * r);
|
||||
uint8_t ig = uint8_t(255.999 * g);
|
||||
uint8_t ib = uint8_t(255.999 * b);
|
||||
|
||||
canvas.set_pixel(i, j, Color(ir, ig, ib));
|
||||
canvas.set_pixel(i, j, ray_color(r, world));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +1,30 @@
|
|||
#ifndef SIMULATION_H
|
||||
#define SIMULATION_H
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include "vec3.h"
|
||||
#include "canvas.h"
|
||||
#include "ring_buffer.h"
|
||||
#include "vec3.h"
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
struct Body {
|
||||
std::string name;
|
||||
Vec3 position;
|
||||
Vec3 velocity;
|
||||
double mass;
|
||||
std::string name;
|
||||
Vec3 position;
|
||||
Vec3 velocity;
|
||||
double mass;
|
||||
double radius;
|
||||
};
|
||||
|
||||
struct SimulationState {
|
||||
std::vector<Body> bodies;
|
||||
uint64_t tick; // global simulation tick
|
||||
std::vector<Body> bodies;
|
||||
uint64_t tick; // global simulation tick
|
||||
};
|
||||
|
||||
extern std::atomic<bool> running;
|
||||
|
||||
void simulation_thread(RingBuffer<SimulationState> &buffer);
|
||||
void render_thread(RingBuffer<SimulationState> &buffer, Canvas& canvas);
|
||||
void render_thread(RingBuffer<SimulationState> &buffer, Canvas &canvas);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
#ifndef SPHERE_H
|
||||
#define SPHERE_H
|
||||
|
||||
#include "hittable.h"
|
||||
#include "vec3.h"
|
||||
|
||||
class sphere : public hittable {
|
||||
public:
|
||||
sphere(const Point3& center, double radius) : center(center), radius(std::fmax(0,radius)) {}
|
||||
|
||||
bool hit(const Ray& r, double ray_tmin, double ray_tmax, hit_record& rec) const override {
|
||||
Vec3 oc = center - r.origin();
|
||||
auto a = r.direction().length_squared();
|
||||
auto h = dot(r.direction(), oc);
|
||||
auto c = oc.length_squared() - radius*radius;
|
||||
|
||||
auto discriminant = h*h - a*c;
|
||||
if (discriminant < 0)
|
||||
return false;
|
||||
|
||||
auto sqrtd = std::sqrt(discriminant);
|
||||
|
||||
// Find the nearest root that lies in the acceptable range.
|
||||
auto root = (h - sqrtd) / a;
|
||||
if (root <= ray_tmin || ray_tmax <= root) {
|
||||
root = (h + sqrtd) / a;
|
||||
if (root <= ray_tmin || ray_tmax <= root)
|
||||
return false;
|
||||
}
|
||||
|
||||
rec.t = root;
|
||||
rec.p = r.at(rec.t);
|
||||
rec.normal = (rec.p - center) / radius;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Point3 center;
|
||||
double radius;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue