mirror of
https://github.com/Manoj-HV30/multithreaded-raytracer.git
synced 2026-05-16 19:35:24 +00:00
Add multithreaded ray tracer implementation
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 763 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 132 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
@@ -0,0 +1,187 @@
|
||||
#ifndef CAMERA_H
|
||||
#define CAMERA_H
|
||||
|
||||
#include "hittable.h"
|
||||
#include "material.h"
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
|
||||
class camera {
|
||||
public:
|
||||
|
||||
double aspect_ratio = 1.0;
|
||||
int image_width = 100;
|
||||
int samples_per_pixel = 10;
|
||||
int max_depth = 10;
|
||||
|
||||
double vfov = 90;
|
||||
point3 lookfrom = point3(0,0,0);
|
||||
point3 lookat = point3(0,0,-1);
|
||||
vec3 vup = vec3(0,1,0);
|
||||
|
||||
|
||||
double defocus_angle = 0;
|
||||
double focus_dist = 10;
|
||||
|
||||
void render(const hittable& world) {
|
||||
initialize();
|
||||
|
||||
std::vector<color> framebuffer(image_width * image_height);
|
||||
|
||||
int thread_count = std::thread::hardware_concurrency();
|
||||
if (thread_count == 0) thread_count = 8; // fallback
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
int rows_per_thread = image_height / thread_count;
|
||||
|
||||
auto render_rows = [&](int start_row, int end_row) {
|
||||
|
||||
for (int j = start_row; j < end_row; j++) {
|
||||
|
||||
for (int i = 0; i < image_width; i++) {
|
||||
|
||||
color pixel_color(0,0,0);
|
||||
|
||||
for (int sample = 0; sample < samples_per_pixel; sample++) {
|
||||
ray r = get_ray(i, j);
|
||||
pixel_color += ray_color(r, max_depth, world);
|
||||
}
|
||||
|
||||
framebuffer[j * image_width + i] =
|
||||
pixel_samples_scale * pixel_color;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (int t = 0; t < thread_count; t++) {
|
||||
int start = t * rows_per_thread;
|
||||
int end = (t == thread_count - 1)
|
||||
? image_height
|
||||
: start + rows_per_thread;
|
||||
|
||||
threads.emplace_back(render_rows, start, end);
|
||||
}
|
||||
|
||||
for (auto& t : threads)
|
||||
t.join();
|
||||
|
||||
// Print image after rendering completes (single-threaded)
|
||||
std::cout << "P3\n" << image_width << ' ' << image_height << "\n255\n";
|
||||
|
||||
for (int j = 0; j < image_height; j++) {
|
||||
for (int i = 0; i < image_width; i++) {
|
||||
write_color(std::cout,
|
||||
framebuffer[j * image_width + i]);
|
||||
}
|
||||
}
|
||||
|
||||
std::clog << "Done.\n";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private:
|
||||
/* Private Camera Variables Here */
|
||||
int image_height; // Rendered image height
|
||||
point3 center; // Camera center
|
||||
double pixel_samples_scale;// color scale factor for a sum of pixel samples
|
||||
point3 pixel00_loc; // Location of pixel 0, 0
|
||||
vec3 pixel_delta_u; // Offset to pixel to the right
|
||||
vec3 pixel_delta_v; // Offset to pixel below
|
||||
vec3 u,v,w;
|
||||
vec3 defocus_disk_u; // Defocus disk horizontal radius
|
||||
vec3 defocus_disk_v; // Defocus disk vertical radius
|
||||
|
||||
void initialize() {
|
||||
image_height = int(image_width / aspect_ratio);
|
||||
image_height = (image_height < 1) ? 1 : image_height;
|
||||
|
||||
pixel_samples_scale = 1.0/ samples_per_pixel;
|
||||
|
||||
center = lookfrom;
|
||||
|
||||
|
||||
|
||||
// Determine viewport dimensions.
|
||||
|
||||
auto theta = degrees_to_radians(vfov);
|
||||
auto h = std::tan(theta/2);
|
||||
auto viewport_height = 2 * h * focus_dist;
|
||||
auto viewport_width = viewport_height * (double(image_width)/image_height);
|
||||
|
||||
w = unit_vector(lookfrom - lookat);
|
||||
u = unit_vector(cross(vup,w));
|
||||
v = cross(w,u);
|
||||
|
||||
// Calculate the vectors across the horizontal and down the vertical viewport edges.
|
||||
vec3 viewport_u = viewport_width*u;
|
||||
vec3 viewport_v = viewport_height*-v;
|
||||
|
||||
// Calculate the horizontal and vertical delta vectors from pixel to pixel.
|
||||
pixel_delta_u = viewport_u / image_width;
|
||||
pixel_delta_v = viewport_v / image_height;
|
||||
|
||||
// Calculate the location of the upper left pixel.
|
||||
auto viewport_upper_left = center - (focus_dist * w) - viewport_u/2 - viewport_v/2;
|
||||
pixel00_loc = viewport_upper_left + 0.5 * (pixel_delta_u + pixel_delta_v);
|
||||
|
||||
// Calculate the camera defocus disk basis vectors.
|
||||
auto defocus_radius = focus_dist * std::tan(degrees_to_radians(defocus_angle / 2));
|
||||
defocus_disk_u = u * defocus_radius;
|
||||
defocus_disk_v = v * defocus_radius;
|
||||
|
||||
}
|
||||
|
||||
ray get_ray(int i, int j) const {
|
||||
// Construct a camera ray originating from the origin and directed at randomly sampled
|
||||
// point around the pixel location i, j.
|
||||
|
||||
auto offset = sample_square();
|
||||
auto pixel_sample = pixel00_loc
|
||||
+ ((i + offset.x()) * pixel_delta_u)
|
||||
+ ((j + offset.y()) * pixel_delta_v);
|
||||
|
||||
auto ray_origin = (defocus_angle <= 0) ? center : defocus_disk_sample();
|
||||
auto ray_direction = pixel_sample - ray_origin;
|
||||
|
||||
return ray(ray_origin, ray_direction);
|
||||
}
|
||||
|
||||
vec3 sample_square() const {
|
||||
// Returns the vector to a random point in the [-.5,-.5]-[+.5,+.5] unit square.
|
||||
return vec3(random_double() - 0.5, random_double() - 0.5, 0);
|
||||
}
|
||||
|
||||
point3 defocus_disk_sample() const {
|
||||
// Returns a random point in the camera defocus disk.
|
||||
auto p = random_in_unit_disk();
|
||||
return center + (p[0] * defocus_disk_u) + (p[1] * defocus_disk_v);
|
||||
}
|
||||
|
||||
|
||||
|
||||
color ray_color(const ray& r,int depth, const hittable& world) const {
|
||||
if(depth<=0)
|
||||
return color(0,0,0);
|
||||
|
||||
hit_record rec;
|
||||
|
||||
if (world.hit(r, interval(0.001, infinity), rec)) {
|
||||
ray scattered;
|
||||
color attenuation;
|
||||
if (rec.mat->scatter(r, rec, attenuation, scattered))
|
||||
return attenuation * ray_color(scattered, depth-1, world);
|
||||
return color(0,0,0);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef COLOR_H
|
||||
#define COLOR_H
|
||||
#include "interval.h"
|
||||
|
||||
#include "vec3.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using color = vec3;
|
||||
inline double linear_to_gamma(double linear_component)
|
||||
{
|
||||
if (linear_component > 0)
|
||||
return std::sqrt(linear_component);
|
||||
|
||||
return 0;
|
||||
}
|
||||
inline void write_color(std::ostream& out, const color& pixel_color) {
|
||||
auto r = pixel_color.x();
|
||||
auto g = pixel_color.y();
|
||||
auto b = pixel_color.z();
|
||||
|
||||
// Apply a linear to gamma transform for gamma 2
|
||||
r = linear_to_gamma(r);
|
||||
g = linear_to_gamma(g);
|
||||
b = linear_to_gamma(b);
|
||||
|
||||
// Translate the [0,1] component values to the byte range [0,255].
|
||||
static const interval intensity(0.000, 0.999);
|
||||
int rbyte = int(256 * intensity.clamp(r));
|
||||
int gbyte = int(256 * intensity.clamp(g));
|
||||
int bbyte = int(256 * intensity.clamp(b));
|
||||
|
||||
// Write out the pixel color components.
|
||||
out << rbyte << ' ' << gbyte << ' ' << bbyte << '\n';
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef HITTABLE_H
|
||||
#define HITTABLE_H
|
||||
|
||||
#include "ray.h"
|
||||
|
||||
class material;
|
||||
|
||||
class hit_record {
|
||||
public:
|
||||
point3 p;
|
||||
vec3 normal;
|
||||
double t;
|
||||
shared_ptr<material> mat;
|
||||
bool front_face;
|
||||
void set_face_normal(const ray& r, const vec3& outward_normal){
|
||||
// Sets the hit record normal vector.
|
||||
// NOTE: the parameter `outward_normal` is assumed to have unit length.
|
||||
|
||||
front_face = dot(r.direction(), outward_normal) < 0;
|
||||
normal = front_face ? outward_normal : -outward_normal;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class hittable {
|
||||
public:
|
||||
virtual ~hittable() = default;
|
||||
|
||||
|
||||
virtual bool hit(const ray& r, interval ray_t, hit_record& rec) const = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
#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, interval ray_t, hit_record& rec) const override {
|
||||
hit_record temp_rec;
|
||||
bool hit_anything = false;
|
||||
auto closest_so_far = ray_t.max;
|
||||
|
||||
for (const auto& object : objects) {
|
||||
if (object->hit(r, interval(ray_t.min, closest_so_far), temp_rec)){
|
||||
hit_anything = true;
|
||||
closest_so_far = temp_rec.t;
|
||||
rec = temp_rec;
|
||||
}
|
||||
}
|
||||
|
||||
return hit_anything;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,37 @@
|
||||
#ifndef INTERVAL_H
|
||||
#define INTERVAL_H
|
||||
|
||||
class interval {
|
||||
public:
|
||||
double min, max;
|
||||
|
||||
interval() : min(+infinity), max(-infinity) {} // Default interval is empty
|
||||
|
||||
interval(double min, double max) : min(min), max(max) {}
|
||||
|
||||
double size() const {
|
||||
return max - min;
|
||||
}
|
||||
|
||||
bool contains(double x) const {
|
||||
return min <= x && x <= max;
|
||||
}
|
||||
|
||||
bool surrounds(double x) const {
|
||||
return min < x && x < max;
|
||||
}
|
||||
|
||||
|
||||
double clamp(double x) const {
|
||||
if (x < min) return min;
|
||||
if (x > max) return max;
|
||||
return x;
|
||||
}
|
||||
|
||||
static const interval empty, universe;
|
||||
};
|
||||
|
||||
const interval interval::empty = interval(+infinity, -infinity);
|
||||
const interval interval::universe = interval(-infinity, +infinity);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,96 @@
|
||||
#ifndef MATERIAL_H
|
||||
#define MATERIAL_H
|
||||
|
||||
#include "hittable.h"
|
||||
#include "vec3.h"
|
||||
|
||||
class material {
|
||||
public:
|
||||
virtual ~material() = default;
|
||||
|
||||
virtual bool scatter(
|
||||
const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered
|
||||
) const {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class lambertian : public material {
|
||||
public:
|
||||
lambertian(const color& albedo) : albedo(albedo) {}
|
||||
|
||||
bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered)
|
||||
const override {
|
||||
auto scatter_direction = rec.normal + random_unit_vector();
|
||||
// Catch degenerate scatter direction
|
||||
if (scatter_direction.near_zero())
|
||||
scatter_direction = rec.normal;
|
||||
|
||||
scattered = ray(rec.p, scatter_direction);
|
||||
attenuation = albedo;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
color albedo;
|
||||
};
|
||||
|
||||
|
||||
class metal : public material {
|
||||
public:
|
||||
metal(const color& albedo, double fuzz) : albedo(albedo), fuzz(fuzz<1? fuzz : 1) {}
|
||||
|
||||
bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered)
|
||||
const override {
|
||||
vec3 reflected = reflect(r_in.direction(), rec.normal);
|
||||
reflected = unit_vector(reflected) + (fuzz*random_unit_vector());
|
||||
scattered = ray(rec.p, reflected);
|
||||
attenuation = albedo;
|
||||
return (dot(scattered.direction(), rec.normal)>0);
|
||||
}
|
||||
|
||||
private:
|
||||
color albedo;
|
||||
double fuzz;
|
||||
};
|
||||
|
||||
class dielectric : public material {
|
||||
public:
|
||||
dielectric(double refraction_index) : refraction_index(refraction_index) {}
|
||||
|
||||
bool scatter(const ray& r_in, const hit_record& rec, color& attenuation, ray& scattered)
|
||||
const override {
|
||||
attenuation = color(1.0, 1.0, 1.0);
|
||||
double ri = rec.front_face ? (1.0/refraction_index) : refraction_index;
|
||||
|
||||
vec3 unit_direction = unit_vector(r_in.direction());
|
||||
double cos_theta = std::fmin(dot(-unit_direction, rec.normal), 1.0);
|
||||
double sin_theta = std::sqrt(1.0 - cos_theta*cos_theta);
|
||||
|
||||
bool cannot_refract = ri * sin_theta > 1.0;
|
||||
vec3 direction;
|
||||
|
||||
if (cannot_refract || reflectance(cos_theta, ri)> random_double())
|
||||
direction = reflect(unit_direction, rec.normal);
|
||||
else
|
||||
direction = refract(unit_direction, rec.normal, ri);
|
||||
|
||||
scattered = ray(rec.p, direction);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
// Refractive index in vacuum or air, or the ratio of the material's refractive index over
|
||||
// the refractive index of the enclosing media
|
||||
double refraction_index;
|
||||
static double reflectance(double cosine, double refraction_index) {
|
||||
// Use Schlick's approximation for reflectance.
|
||||
auto r0 = (1 - refraction_index) / (1 + refraction_index);
|
||||
r0 = r0*r0;
|
||||
return r0 + (1-r0)*std::pow((1 - cosine),5);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef RAY_H
|
||||
#define RAY_H
|
||||
|
||||
#include "vec3.h"
|
||||
|
||||
class ray {
|
||||
public:
|
||||
ray() {}
|
||||
|
||||
ray(const point3& origin, const vec3& direction) : orig(origin), dir(direction) {}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef RTWEEKEND_H
|
||||
#define RTWEEKEND_H
|
||||
|
||||
#include <cmath>
|
||||
#include <random>
|
||||
#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;
|
||||
}
|
||||
inline double random_double() {
|
||||
thread_local static std::mt19937 generator(std::random_device{}());
|
||||
thread_local static std::uniform_real_distribution<double> distribution(0.0, 1.0);
|
||||
return distribution(generator);
|
||||
}
|
||||
|
||||
|
||||
inline double random_double(double min, double max) {
|
||||
// Returns a random real in [min,max).
|
||||
return min + (max-min)*random_double();
|
||||
}
|
||||
|
||||
// Common Headers
|
||||
|
||||
#include "color.h"
|
||||
#include "interval.h"
|
||||
#include "ray.h"
|
||||
#include "vec3.h"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef SPHERE_H
|
||||
#define SPHERE_H
|
||||
|
||||
#include "hittable.h"
|
||||
#include "vec3.h"
|
||||
|
||||
class sphere : public hittable {
|
||||
public:
|
||||
sphere(const point3& center, double radius, shared_ptr<material> mat) : center(center), radius(std::fmax(0,radius)), mat(mat) {}
|
||||
|
||||
bool hit(const ray& r, interval ray_t, 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 (!ray_t.surrounds(root)){
|
||||
root = (h + sqrtd) / a;
|
||||
if (!ray_t.surrounds(root))
|
||||
return false;
|
||||
}
|
||||
|
||||
rec.t = root;
|
||||
rec.p = r.at(rec.t);
|
||||
vec3 outward_normal = (rec.p - center) / radius;
|
||||
rec.set_face_normal(r, outward_normal);
|
||||
rec.mat = mat;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
point3 center;
|
||||
double radius;
|
||||
shared_ptr<material>mat;
|
||||
};
|
||||
|
||||
#endif
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
#ifndef VEC3_H
|
||||
#define VEC3_H
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
class vec3 {
|
||||
public:
|
||||
double e[3];
|
||||
|
||||
vec3() : e{0,0,0} {}
|
||||
vec3(double e0, double e1, double e2) : e{e0, e1, e2} {}
|
||||
|
||||
double x() const { return e[0]; }
|
||||
double y() const { return e[1]; }
|
||||
double z() const { return e[2]; }
|
||||
|
||||
vec3 operator-() const { return vec3(-e[0], -e[1], -e[2]); }
|
||||
double operator[](int i) const { return e[i]; }
|
||||
double& operator[](int i) { return e[i]; }
|
||||
|
||||
vec3& operator+=(const vec3& v) {
|
||||
e[0] += v.e[0];
|
||||
e[1] += v.e[1];
|
||||
e[2] += v.e[2];
|
||||
return *this;
|
||||
}
|
||||
|
||||
vec3& operator*=(double t) {
|
||||
e[0] *= t;
|
||||
e[1] *= t;
|
||||
e[2] *= t;
|
||||
return *this;
|
||||
}
|
||||
|
||||
vec3& operator/=(double t) {
|
||||
return *this *= 1/t;
|
||||
}
|
||||
|
||||
double length() const {
|
||||
return std::sqrt(length_squared());
|
||||
}
|
||||
|
||||
double length_squared() const {
|
||||
return e[0]*e[0] + e[1]*e[1] + e[2]*e[2];
|
||||
}
|
||||
|
||||
|
||||
bool near_zero() const {
|
||||
// Return true if the vector is close to zero in all dimensions.
|
||||
auto s = 1e-8;
|
||||
return (std::fabs(e[0]) < s) && (std::fabs(e[1]) < s) && (std::fabs(e[2]) < s);
|
||||
}
|
||||
|
||||
static vec3 random() {
|
||||
return vec3(random_double(), random_double(), random_double());
|
||||
}
|
||||
|
||||
static vec3 random(double min, double max) {
|
||||
return vec3(random_double(min,max), random_double(min,max), random_double(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) {
|
||||
return out << v.e[0] << ' ' << v.e[1] << ' ' << v.e[2];
|
||||
}
|
||||
|
||||
inline vec3 operator+(const vec3& u, const vec3& v) {
|
||||
return vec3(u.e[0] + v.e[0], u.e[1] + v.e[1], u.e[2] + v.e[2]);
|
||||
}
|
||||
|
||||
inline vec3 operator-(const vec3& u, const vec3& v) {
|
||||
return vec3(u.e[0] - v.e[0], u.e[1] - v.e[1], u.e[2] - v.e[2]);
|
||||
}
|
||||
|
||||
inline vec3 operator*(const vec3& u, const vec3& v) {
|
||||
return vec3(u.e[0] * v.e[0], u.e[1] * v.e[1], u.e[2] * v.e[2]);
|
||||
}
|
||||
|
||||
inline vec3 operator*(double t, const vec3& v) {
|
||||
return vec3(t*v.e[0], t*v.e[1], t*v.e[2]);
|
||||
}
|
||||
|
||||
inline vec3 operator*(const vec3& v, double t) {
|
||||
return t * v;
|
||||
}
|
||||
|
||||
inline vec3 operator/(const vec3& v, double t) {
|
||||
return (1/t) * v;
|
||||
}
|
||||
|
||||
inline double dot(const vec3& u, const vec3& v) {
|
||||
return u.e[0] * v.e[0]
|
||||
+ u.e[1] * v.e[1]
|
||||
+ u.e[2] * v.e[2];
|
||||
}
|
||||
|
||||
inline 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],
|
||||
u.e[0] * v.e[1] - u.e[1] * v.e[0]);
|
||||
}
|
||||
|
||||
inline vec3 unit_vector(const vec3& v) {
|
||||
return v / v.length();
|
||||
}
|
||||
|
||||
|
||||
inline vec3 random_in_unit_disk() {
|
||||
while (true) {
|
||||
auto p = vec3(random_double(-1,1), random_double(-1,1), 0);
|
||||
if (p.length_squared() < 1)
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
inline vec3 random_unit_vector() {
|
||||
while (true) {
|
||||
auto p = vec3::random(-1,1);
|
||||
auto lensq = p.length_squared();
|
||||
if (1e-160 < lensq && lensq <= 1)
|
||||
return p / sqrt(lensq);
|
||||
}
|
||||
}
|
||||
|
||||
inline vec3 random_on_hemisphere(const vec3& normal) {
|
||||
vec3 on_unit_sphere = random_unit_vector();
|
||||
if (dot(on_unit_sphere, normal) > 0.0) // In the same hemisphere as the normal
|
||||
return on_unit_sphere;
|
||||
else
|
||||
return -on_unit_sphere;
|
||||
}
|
||||
|
||||
inline vec3 reflect(const vec3& v, const vec3& n) {
|
||||
return v - 2*dot(v,n)*n;
|
||||
}
|
||||
inline vec3 refract(const vec3& uv, const vec3& n, double etai_over_etat){
|
||||
auto cos_theta = std::fmin(dot(-uv,n),1.0);
|
||||
vec3 r_out_perp = etai_over_etat*(uv+cos_theta*n);
|
||||
vec3 r_out_parallel = -std::sqrt(std::fabs(1.0 - r_out_perp.length_squared())) * n;
|
||||
return r_out_perp + r_out_parallel;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,69 @@
|
||||
#include<rtweekend.h>
|
||||
#include<camera.h>
|
||||
#include "hittable.h"
|
||||
#include "hittable_list.h"
|
||||
#include "sphere.h"
|
||||
|
||||
|
||||
int main() {
|
||||
|
||||
hittable_list world;
|
||||
|
||||
auto ground_material = make_shared<lambertian>(color(0.5, 0.5, 0.5));
|
||||
world.add(make_shared<sphere>(point3(0,-1000,0), 1000, ground_material));
|
||||
|
||||
for (int a = -11; a < 11; a++) {
|
||||
for (int b = -11; b < 11; b++) {
|
||||
auto choose_mat = random_double();
|
||||
point3 center(a + 0.9*random_double(), 0.2, b + 0.9*random_double());
|
||||
|
||||
if ((center - point3(4, 0.2, 0)).length() > 0.9) {
|
||||
shared_ptr<material> sphere_material;
|
||||
|
||||
if (choose_mat < 0.8) {
|
||||
// diffuse
|
||||
auto albedo = color::random() * color::random();
|
||||
sphere_material = make_shared<lambertian>(albedo);
|
||||
world.add(make_shared<sphere>(center, 0.2, sphere_material));
|
||||
} else if (choose_mat < 0.95) {
|
||||
// metal
|
||||
auto albedo = color::random(0.5, 1);
|
||||
auto fuzz = random_double(0, 0.5);
|
||||
sphere_material = make_shared<metal>(albedo, fuzz);
|
||||
world.add(make_shared<sphere>(center, 0.2, sphere_material));
|
||||
} else {
|
||||
// glass
|
||||
sphere_material = make_shared<dielectric>(1.5);
|
||||
world.add(make_shared<sphere>(center, 0.2, sphere_material));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto material1 = make_shared<dielectric>(1.5);
|
||||
world.add(make_shared<sphere>(point3(0, 1, 0), 1.0, material1));
|
||||
|
||||
auto material2 = make_shared<lambertian>(color(0.4, 0.2, 0.1));
|
||||
world.add(make_shared<sphere>(point3(-4, 1, 0), 1.0, material2));
|
||||
|
||||
auto material3 = make_shared<metal>(color(0.7, 0.6, 0.5), 0.0);
|
||||
world.add(make_shared<sphere>(point3(4, 1, 0), 1.0, material3));
|
||||
|
||||
camera cam;
|
||||
|
||||
cam.aspect_ratio = 16.0 / 9.0;
|
||||
cam.image_width = 1200;
|
||||
cam.samples_per_pixel = 500;
|
||||
cam.max_depth = 50;
|
||||
|
||||
cam.vfov = 20;
|
||||
cam.lookfrom = point3(13,2,3);
|
||||
cam.lookat = point3(0,0,0);
|
||||
cam.vup = vec3(0,1,0);
|
||||
|
||||
cam.defocus_angle = 0.6;
|
||||
cam.focus_dist = 10.0;
|
||||
|
||||
|
||||
cam.render(world);
|
||||
}
|
||||
Reference in New Issue
Block a user