Compare commits

...

5 Commits

Author SHA1 Message Date
ktyl 4ccba24017 ring.png 2022-06-03 21:16:46 +01:00
ktyl 3adf674871 cool pink spheres 2022-06-02 23:09:50 +01:00
ktyl f1269753f9 update readme 2022-06-02 23:09:22 +01:00
K Tyl c41297b074 camera lens + random scene 2020-06-07 04:13:39 +01:00
K Tyl ac6cc84bf5 add glass 2020-06-07 01:13:39 +01:00
7 changed files with 198 additions and 42 deletions

View File

@ -3,3 +3,9 @@
Ray tracing rendering experiments Ray tracing rendering experiments
https://raytracing.github.io/ https://raytracing.github.io/
Produced PPM images can be converted to PNG with `imagemagick`:
```
convert image.ppm image.png
```

BIN
screenshots/pinkspheres.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

BIN
screenshots/ring.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

View File

@ -5,22 +5,40 @@
class camera class camera
{ {
public: public:
const double ASPECT_RATIO = 16.0 / 9.0; camera(
const double VIEWPORT_HEIGHT = 2.0; point3 lookfrom,
const double VIEWPORT_WIDTH = ASPECT_RATIO * VIEWPORT_HEIGHT; point3 lookat,
const double FOCAL_LENGTH = 1.0; vec3 vup,
double vfov, // vertical field of view in degrees
camera() : double aspect_ratio,
origin_ (point3(0,0,0)), double aperture,
horizontal_ (vec3(VIEWPORT_WIDTH,0.0,0.0)), double focus_dist)
vertical_ (vec3(0.0, VIEWPORT_HEIGHT, 0.0))
{ {
lower_left_corner_ = origin_ - horizontal_/2 - vertical_/2 - vec3(0,0,FOCAL_LENGTH); auto theta = degrees_to_radians(vfov);
auto h = tan(theta/2);
auto viewport_height = 2.0 * h;
auto viewport_width = aspect_ratio * viewport_height;
w_ = unit_vector(lookfrom - lookat);
u_ = unit_vector(cross(vup, w_));
v_ = cross(w_, u_);
origin_ = lookfrom;
horizontal_ = focus_dist * viewport_width * u_;
vertical_ = focus_dist * viewport_height * v_;
lower_left_corner_ = origin_ - horizontal_/2 - vertical_/2 - focus_dist * w_;
lens_radius_ = aperture / 2;
} }
ray get_ray(double u, double v) const ray get_ray(double s, double t) const
{ {
return ray(origin_, lower_left_corner_ + u*horizontal_ + v*vertical_ - origin_); vec3 rd = lens_radius_ * random_in_unit_disk();
vec3 offset = (u_ * rd.x()) + (v_ * rd.y());
return ray(
origin_ + offset,
lower_left_corner_ + s*horizontal_ + t*vertical_ - origin_ - offset);
} }
private: private:
@ -28,4 +46,6 @@ class camera
point3 lower_left_corner_; point3 lower_left_corner_;
vec3 horizontal_; vec3 horizontal_;
vec3 vertical_; vec3 vertical_;
vec3 u_, v_, w_;
double lens_radius_;
}; };

View File

@ -9,17 +9,22 @@
#include <iostream> #include <iostream>
const double ASPECT_RATIO = 16.0 / 9.0; const double ASPECT_RATIO = 16.0 / 9.0;
const int WIDTH = 384; const int WIDTH = 1920;
const int HEIGHT = static_cast<int>(WIDTH / ASPECT_RATIO); const int HEIGHT = static_cast<int>(WIDTH / ASPECT_RATIO);
const int SAMPLES_PER_PIXEL = 100; const int SAMPLES_PER_PIXEL = 8;
const int MAX_DEPTH = 50; const int MAX_DEPTH = 5;
// fee2aa
//
const colour pink(254.0/255.0, 226.0/255.0, 170.0/255.0);
const colour grey(0.133, 0.133, 0.133);
colour ray_colour(const ray& r, const hittable& world, int depth) colour ray_colour(const ray& r, const hittable& world, int depth)
{ {
hit_record rec; hit_record rec;
if (depth <= 0) if (depth <= 0)
{ {
return colour(0,0,0); return grey;
} }
if (world.hit(r, 0.001, infinity, rec)) if (world.hit(r, 0.001, infinity, rec))
@ -32,43 +37,96 @@ colour ray_colour(const ray& r, const hittable& world, int depth)
return attenuation * ray_colour(scattered, world, depth-1); return attenuation * ray_colour(scattered, world, depth-1);
} }
return colour(0,0,0); return grey;
} }
vec3 unit_direction = unit_vector(r.direction()); vec3 unit_direction = unit_vector(r.direction());
auto t = 0.5 * (unit_direction.y() + 1.0); auto t = 0.5 * (unit_direction.y() + 1.0) + 0.5;
auto a = colour(0.5, 0.6, 0.7); return lerp(grey, pink, t);
auto b = colour(1.0, 1.0, 1.0); }
return lerp(a, b, t); hittable_list random_scene()
{
hittable_list world;
//auto ground_material = make_shared<lambertian>(pink);
//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 centre(a + 0.9*random_double(), 0.2, b + 0.9*random_double());
// if ((centre - point3(4, 0.2, 0)).length() > 0.9)
// {
// shared_ptr<material> sphere_material;
// if (choose_mat < 0.8)
// {
// // diffuse
// //auto albedo = colour::random() * colour::random();
// sphere_material = make_shared<lambertian>(pink);
// world.add(make_shared<sphere>(centre, 0.2, sphere_material));
// }
// else if (choose_mat < 0.95)
// {
// // metal
// auto fuzz = random_double(0, 0.5);
// sphere_material = make_shared<metal>(pink, fuzz);
// world.add(make_shared<sphere>(centre, 0.2, sphere_material));
// }
// else
// {
// // glass
// sphere_material = make_shared<dielectric>(1.5);
// world.add(make_shared<sphere>(centre,0.2, sphere_material));
// }
// }
// }
//}
auto material1 = make_shared<dielectric>(1.5);
world.add(make_shared<sphere>(point3(0, 0, 0), 3.0, material1));
//auto material2 = make_shared<lambertian>(pink);
//world.add(make_shared<sphere>(point3(-4, 1, 0), 1.0, material2));
auto material3 = make_shared<metal>(pink, 0.5);
int sphere_count = 10;
for (int i = 0; i < sphere_count; i++)
{
float a = 6.28 * (float)i/sphere_count - 100.0;
float r = 8.0;
float x = r*sin(a);
float y = 2.0*cos(a);
float z = r*cos(a);
point3 pos(x,y,z);
world.add(make_shared<sphere>(pos, 2.0, material3));
}
return world;
} }
int main() int main()
{ {
std::cout << "P3\n" << WIDTH << ' ' << HEIGHT << "\n255\n"; std::cout << "P3\n" << WIDTH << ' ' << HEIGHT << "\n255\n";
hittable_list world; hittable_list world = random_scene();
world.add(make_shared<sphere>(
point3(0,0,-1),
0.5,
make_shared<lambertian>(colour(0.7,0.3,0.3))));
world.add(make_shared<sphere>(
point3(0,-100.5,-1),
100,
make_shared<lambertian>(colour(0.8,0.8,0.0))));
world.add(make_shared<sphere>( auto dist_to_target = 10.0;
point3(1,0,-1), auto dist_to_focus = dist_to_target + 1.0;
0.5, auto cam_y = 1.0;
make_shared<metal>(colour(0.8,0.6,0.2)))); point3 lookfrom(0,cam_y,-dist_to_target);
world.add(make_shared<sphere>( point3 lookat(0,0,0);
point3(-1,0,-1), vec3 vup(0,1,0);
0.5, auto aperture = 0.5;
make_shared<metal>(colour(0.8,0.8,0.8))));
camera cam; camera cam(lookfrom, lookat, vup, 47, ASPECT_RATIO, aperture, dist_to_focus);
for (int j = HEIGHT - 1; j >= 0; --j) for (int j = HEIGHT - 1; j >= 0; --j)
{ {

View File

@ -13,6 +13,13 @@ class material
ray& scattered) const = 0; ray& scattered) const = 0;
}; };
double schlick(double cosine, double refraction_index)
{
auto r0 = (1-refraction_index) / (1+refraction_index);
r0 = r0*r0;
return r0 + (1-r0)*pow(1-cosine, 5);
}
class lambertian : public material class lambertian : public material
{ {
public: public:
@ -37,7 +44,9 @@ class lambertian : public material
class metal : public material class metal : public material
{ {
public: public:
metal(const colour& a) : albedo_(a) {} metal(const colour& a, double f) :
albedo_(a),
fuzz_(f < 1 ? f : 1) {}
virtual bool scatter( virtual bool scatter(
const ray& r_in, const ray& r_in,
@ -46,11 +55,55 @@ class metal : public material
ray& scattered) const ray& scattered) const
{ {
vec3 reflected = reflect(unit_vector(r_in.direction()), rec.normal); vec3 reflected = reflect(unit_vector(r_in.direction()), rec.normal);
scattered = ray(rec.p, reflected); scattered = ray(rec.p, reflected + fuzz_*random_in_unit_sphere());
attenuation = albedo_; attenuation = albedo_;
return dot(scattered.direction(), rec.normal) > 0; return dot(scattered.direction(), rec.normal) > 0;
} }
private: private:
colour albedo_; colour albedo_;
double fuzz_;
};
class dielectric : public material
{
public:
dielectric(double ri) : refraction_index_(ri) {}
virtual bool scatter(
const ray& r_in,
const hit_record& rec,
colour& attenuation,
ray& scattered) const
{
attenuation = colour(1.0,1.0,1.0);
double etai_over_etat = rec.front_face ? (1.0 / refraction_index_) : refraction_index_;
vec3 unit_direction = unit_vector(r_in.direction());
double cos_theta = fmin(dot(-unit_direction, rec.normal), 1.0);
double sin_theta = sqrt(1.0 - cos_theta*cos_theta);
if (etai_over_etat * sin_theta > 1.0)
{
vec3 reflected = reflect(unit_direction, rec.normal);
scattered = ray(rec.p, reflected);
return true;
}
double reflect_prob = schlick(cos_theta, etai_over_etat);
if (random_double() < reflect_prob)
{
vec3 reflected = reflect(unit_direction, rec.normal);
scattered = ray(rec.p, reflected);
return true;
}
vec3 refracted = refract(unit_direction, rec.normal, etai_over_etat);
scattered = ray(rec.p, refracted);
return true;
}
private:
double refraction_index_;
}; };

View File

@ -160,7 +160,26 @@ vec3 random_in_hemisphere(const vec3& normal)
} }
} }
vec3 random_in_unit_disk()
{
while(true)
{
auto p = vec3(random_double(-1,1), random_double(-1,1), 0);
if (p.length_squared() >= 1) continue;
return p;
}
}
vec3 reflect(const vec3& v, const vec3& n) vec3 reflect(const vec3& v, const vec3& n)
{ {
return v - 2*dot(v,n)*n; return v - 2*dot(v,n)*n;
} }
vec3 refract(const vec3& uv, const vec3& n, double etai_over_etat)
{
auto cos_theta = dot(-uv, n);
vec3 r_out_parallel = etai_over_etat * (uv + cos_theta*n);
vec3 r_out_perp = -sqrt(1.0 - r_out_parallel.length_squared()) * n;
return r_out_parallel + r_out_perp;
}