A Scene Testing All New Features
Let’s put it all together, with a big thin mist covering everything, and a blue subsurface reflection sphere (we didn’t implement that explicitly, but a volume inside a dielectric is what a subsurface material is). The biggest limitation left in the renderer is no shadow rays, but that is why we get caustics and subsurface for free. It’s a double-edged design decision.
Also note that we'll parameterize this final scene to support a lower quality render for quick testing.
#include "bvh.h"
...
void final_scene(int image_width, int samples_per_pixel, int max_depth) {
hittable_list boxes1;
auto ground = make_shared<lambertian>(color(0.48, 0.83, 0.53));
int boxes_per_side = 20;
for (int i = 0; i < boxes_per_side; i++) {
for (int j = 0; j < boxes_per_side; j++) {
auto w = 100.0;
auto x0 = -1000.0 + i*w;
auto z0 = -1000.0 + j*w;
auto y0 = 0.0;
auto x1 = x0 + w;
auto y1 = random_double(1,101);
auto z1 = z0 + w;
boxes1.add(box(point3(x0,y0,z0), point3(x1,y1,z1), ground));
}
}
hittable_list world;
world.add(make_shared<bvh_node>(boxes1));
auto light = make_shared<diffuse_light>(color(7, 7, 7));
world.add(make_shared<quad>(point3(123,554,147), vec3(300,0,0), vec3(0,0,265), light));
auto center1 = point3(400, 400, 200);
auto center2 = center1 + vec3(30,0,0);
auto sphere_material = make_shared<lambertian>(color(0.7, 0.3, 0.1));
world.add(make_shared<sphere>(center1, center2, 50, sphere_material));
world.add(make_shared<sphere>(point3(260, 150, 45), 50, make_shared<dielectric>(1.5)));
world.add(make_shared<sphere>(
point3(0, 150, 145), 50, make_shared<metal>(color(0.8, 0.8, 0.9), 1.0)
));
auto boundary = make_shared<sphere>(point3(360,150,145), 70, make_shared<dielectric>(1.5));
world.add(boundary);
world.add(make_shared<constant_medium>(boundary, 0.2, color(0.2, 0.4, 0.9)));
boundary = make_shared<sphere>(point3(0,0,0), 5000, make_shared<dielectric>(1.5));
world.add(make_shared<constant_medium>(boundary, .0001, color(1,1,1)));
auto emat = make_shared<lambertian>(make_shared<image_texture>("earthmap.jpg"));
world.add(make_shared<sphere>(point3(400,200,400), 100, emat));
auto pertext = make_shared<noise_texture>(0.1);
world.add(make_shared<sphere>(point3(220,280,300), 80, make_shared<lambertian>(pertext)));
hittable_list boxes2;
auto white = make_shared<lambertian>(color(.73, .73, .73));
int ns = 1000;
for (int j = 0; j < ns; j++) {
boxes2.add(make_shared<sphere>(point3::random(0,165), 10, white));
}
world.add(make_shared<translate>(
make_shared<rotate_y>(
make_shared<bvh_node>(boxes2), 15),
vec3(-100,270,395)
)
);
camera cam;
cam.aspect_ratio = 1.0;
cam.image_width = image_width;
cam.samples_per_pixel = samples_per_pixel;
cam.max_depth = max_depth;
cam.background = color(0,0,0);
cam.vfov = 40;
cam.lookfrom = point3(478, 278, -600);
cam.lookat = point3(278, 278, 0);
cam.vup = vec3(0,1,0);
cam.defocus_angle = 0;
cam.render(world);
}
int main() {
switch (0) {
case 1: random_spheres(); break;
case 2: two_spheres(); break;
case 3: earth(); break;
case 4: two_perlin_spheres(); break;
case 5: quads(); break;
case 6: simple_light(); break;
case 7: cornell_box(); break;
case 8: cornell_smoke(); break;
case 9: final_scene(800, 10000, 40); break;
default: final_scene(400, 250, 4); break;
}
}
Running it with 10,000 rays per pixel (sweet dreams) yields:
Now go off and make a really cool image of your own! See https://in1weekend.blogspot.com/ for pointers to further reading and features, and feel free to email questions, comments, and cool images to me at ptrshrl@gmail.com.