From 5adaed2cef2c0fb3c88172c66ce1ede8940d19a5 Mon Sep 17 00:00:00 2001 From: connortbot Date: Mon, 18 Mar 2024 00:50:54 -0400 Subject: [PATCH 001/142] CA-51 both texture and color constructors for emissive materials --- light.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/light.h b/light.h index 5cc5203..68efa72 100644 --- a/light.h +++ b/light.h @@ -8,14 +8,17 @@ /** @brief an emissive material is just one that does not scatter, and adds light. But this is not done from the material. */ class emissive : public material { public: - color emission_color; - emissive(color emission_color) : emission_color{emission_color} {} + emissive(shared_ptr a) : emit(a) {} + emissive(color emission_color) : emit(make_shared(emission_color)) {} bool scatter(const ray& r_in, const HitInfo& rec, color& attenuation, ray& scattered) const override { return false; } color emitted(double u, double v, const point3& p) const override { - return emission_color; + return emit->value(u, v, p); } + + private: + shared_ptr emit; }; /** From 7541c907646ddacae52e6fa2e7c1c67821b15c54 Mon Sep 17 00:00:00 2001 From: connortbot Date: Mon, 18 Mar 2024 10:11:04 -0400 Subject: [PATCH 002/142] CA-90 simple scattering pdf and cosine random sampling --- main.cc | 20 ++++++++++++-------- material.h | 10 ++++++++++ vec3.cc | 12 ++++++++++++ 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/main.cc b/main.cc index 772f62b..b5ea7b7 100644 --- a/main.cc +++ b/main.cc @@ -122,18 +122,22 @@ color colorize_ray(const ray& r, std::shared_ptr scene, int depth) { color color_from_emission = mat_ptr->emitted(record.u, record.v, record.pos); if (!mat_ptr->scatter(r, record, attenuation, scattered)) { return color_from_emission; - } + } + + double scattering_pdf = mat_ptr->scattering_pdf(r, record, scattered); + double pdf = scattering_pdf; - color color_from_scatter = attenuation * colorize_ray(scattered, scene, depth-1); + color color_from_scatter = (attenuation * scattering_pdf * colorize_ray(scattered, scene, depth-1)) / pdf; return color_from_emission + color_from_scatter; } + return color(0,0,0); // Sky background (gradient blue-white) - vec3 unit_direction = r.direction().unit_vector(); - auto t = 0.5*(unit_direction.y() + 1.0); + // vec3 unit_direction = r.direction().unit_vector(); + // auto t = 0.5*(unit_direction.y() + 1.0); - return (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0); // lerp formula (1.0-t)*start + t*endval + // return (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0); // lerp formula (1.0-t)*start + t*endval } struct RenderData { @@ -476,9 +480,9 @@ void output(RenderData& render_data, Camera& cam, std::shared_ptr scene_p for (int i=0; i < num_threads; i++) { // In the first thead, we want the first lines_per_thread lines to be rendered - threads.emplace_back(render_scanlines_sse,lines_per_thread,(image_height-1) - (i * lines_per_thread), scene_ptr, std::ref(render_data),cam); + threads.emplace_back(render_scanlines,lines_per_thread,(image_height-1) - (i * lines_per_thread), scene_ptr, std::ref(render_data),cam); } - threads.emplace_back(render_scanlines_sse,leftOver,(image_height-1) - (num_threads * lines_per_thread), scene_ptr, std::ref(render_data),cam); + threads.emplace_back(render_scanlines,leftOver,(image_height-1) - (num_threads * lines_per_thread), scene_ptr, std::ref(render_data),cam); for (auto &thread : threads) { thread.join(); @@ -810,7 +814,7 @@ void two_perlin_spheres(){ } int main() { - switch (8) { + switch (7) { case 1: random_spheres(); break; case 2: two_spheres(); break; case 3: earth(); break; diff --git a/material.h b/material.h index 421ce74..54c985e 100644 --- a/material.h +++ b/material.h @@ -15,6 +15,10 @@ class material { } virtual bool scatter(const ray& r_in, const HitInfo& rec, color& attenuation, ray& scattered) const = 0; + + virtual double scattering_pdf(const ray& r_in, const HitInfo& rec, const ray& scattered) const { + return 0; + } }; class lambertian : public material { @@ -36,6 +40,12 @@ class lambertian : public material { return true; } + + double scattering_pdf(const ray& r_in, const HitInfo& rec, const ray& scattered) const { + auto cos_theta = dot(rec.normal, scattered.direction().unit_vector()); + return cos_theta < 0 ? 0 : cos_theta/pi; + } + private: shared_ptr albedo; }; diff --git a/vec3.cc b/vec3.cc index 0e58088..b540340 100644 --- a/vec3.cc +++ b/vec3.cc @@ -119,6 +119,18 @@ vec3 random_in_unit_disk() { return rand_vec.unit_vector(); } +vec3 random_cosine_direction() { + auto r1 = random_double(); + auto r2 = random_double(); + + auto phi = 2*pi*r1; + auto x = cos(phi)*sqrt(r2); + auto y = sin(phi)*sqrt(r2); + auto z = sqrt(1-r2); + + return vec3(x, y, z); +} + vec3 reflect(const vec3& v, const vec3& n) { return v - 2*n * dot(v, n); } vec3 refract(const vec3& uv, const vec3& n, float etai_over_etat) { From 8319abf37e35735c18135f28e1ccf4706a122554 Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 22 Mar 2024 16:54:42 -0400 Subject: [PATCH 003/142] CA-58 add include guards --- intersects.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/intersects.h b/intersects.h index 28bd7ad..76c9ef5 100644 --- a/intersects.h +++ b/intersects.h @@ -1,3 +1,5 @@ +#ifndef INTERSECTS_H +#define INTERSECTS_H // Semi-temporary helper header file for the rtcIntersectX functions. // Helpers do not actually fire the ray, they just set up the RTCRayHit objects with rays. @@ -75,4 +77,6 @@ void setupRayHit16(struct RTCRayHit16& rayhit, std::vector& rays) { rayhit.hit.instID[0][ix] = RTC_INVALID_GEOMETRY_ID; ix += 1; } -} \ No newline at end of file +} + +#endif \ No newline at end of file From 588746a2145086a04fa46ae22ca7b879dd6882d4 Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 22 Mar 2024 17:02:52 -0400 Subject: [PATCH 004/142] CA-91 basic BRDF sampling in new render function with lambertians --- include/render.h | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ main.cc | 1 + material.h | 51 +++++++++++++++++++++-------------------- 3 files changed, 86 insertions(+), 25 deletions(-) create mode 100644 include/render.h diff --git a/include/render.h b/include/render.h new file mode 100644 index 0000000..3b057d2 --- /dev/null +++ b/include/render.h @@ -0,0 +1,59 @@ +#ifndef RENDER_H +#define RENDER_H + +#include +#include "intersects.h" +#include "scene.h" +#include "vec3.h" + +color trace_ray(const ray& r, std::shared_ptr scene, int depth) { + HitInfo record; + + color weight = color(1.0, 1.0, 1.0); + color accumulated_color = color(0,0,0); + + ray r_in = r; + + for (int i=0; irtc_scene, &rayhit); + if (rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID) { + std::shared_ptr geomhit = scene->geom_map[rayhit.hit.geomID]; + std::shared_ptr mat_ptr = geomhit->materialById(rayhit.hit.geomID); + record = geomhit->getHitInfo(r_in, r_in.at(rayhit.ray.tfar), rayhit.ray.tfar, rayhit.hit.geomID); + + color color_from_emission = mat_ptr->emitted(record.u, record.v, record.pos); + accumulated_color += weight * color_from_emission; + + if (!mat_ptr->scatter(r_in, record, attenuation, scattered)) { // sets scattered to sampled BRDF + return accumulated_color; + } + color brdf_value = mat_ptr->generate(r_in, scattered, record); + double pdf_value = mat_ptr->pdf(r_in, scattered, record); + double cos_theta = fmax(0.0, dot(record.normal, scattered.direction())); + + weight = weight * ((brdf_value * cos_theta) / pdf_value); + + // direct light checks occur here. + } else { + // Sky background (gradient blue-white) + vec3 unit_direction = r_in.direction().unit_vector(); + auto t = 0.5*(unit_direction.y() + 1.0); + + color sky = (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0); // lerp formula (1.0-t)*start + t*endval + accumulated_color += weight * sky; + return accumulated_color; + } + + r_in = scattered; + } + + return accumulated_color; +} + + +#endif \ No newline at end of file diff --git a/main.cc b/main.cc index 7a67418..99be889 100644 --- a/main.cc +++ b/main.cc @@ -8,6 +8,7 @@ #include "ray.h" #include "vec3.h" #include "material.h" +#include "render.h" #include "sphere_primitive.h" #include "quad_primitive.h" diff --git a/material.h b/material.h index 421ce74..f57be75 100644 --- a/material.h +++ b/material.h @@ -15,6 +15,12 @@ class material { } virtual bool scatter(const ray& r_in, const HitInfo& rec, color& attenuation, ray& scattered) const = 0; + virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { + return color(0,0,0); + } + virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { + return 0.0; + }; }; class lambertian : public material { @@ -25,42 +31,37 @@ class lambertian : public material { virtual bool scatter(const ray& r_in, const HitInfo& rec, color& attenuation, ray& scattered) const override { + // auto scatter_direction = rec.normal + random_unit_vector(); auto scatter_direction = rec.normal + random_unit_vector(); - if (scatter_direction.near_zero()) { scatter_direction = rec.normal; } scattered = ray(rec.pos, scatter_direction, r_in.time()); - attenuation = albedo->value(rec.u, rec.v, rec.pos); return true; } - private: - shared_ptr albedo; -}; - -class hemispheric : public material { - - public: - - hemispheric(const color& a) : albedo(a) {} - - virtual bool scatter(const ray& r_in, const HitInfo& rec, color& attenuation, ray& scattered) const override { - auto scatter_direction = random_in_hemisphere(rec.normal); - - if (scatter_direction.near_zero()) { - scatter_direction = rec.normal; - } - scattered = ray(rec.pos,scatter_direction, r_in.time()); - attenuation = albedo; - - return true; + // A lambertians BRDF value is its albedo / pi + virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { + color a = albedo->value(rec.u, rec.v, rec.pos); + return a / pi; + } + + /** + * @note some resources say a lambertians PDF is 1 / (2 * pi) + * But Shirley refers to a "perfect match" as cos_theta / pi. + * Whereas 1 / (2 * pi) seems to be the PDF for uniform hemispherical sampling, which we + * no longer support (or really ever did). + */ + //A lambertians PDF is 1 / (2 * pi) + virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { + // return 1 / (2 * pi); + double cos_theta = fmax(0.0, dot(rec.normal, scattered.direction())); + return cos_theta / pi; } - public: - - color albedo; + private: + shared_ptr albedo; }; class metal : public material { From e617c16828054d361d13911251de3566a2c45cba Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 22 Mar 2024 17:24:50 -0400 Subject: [PATCH 005/142] CA-93 orthonormal basis class --- include/onb.h | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 include/onb.h diff --git a/include/onb.h b/include/onb.h new file mode 100644 index 0000000..13dae3d --- /dev/null +++ b/include/onb.h @@ -0,0 +1,40 @@ +#ifndef ONB_H +#define ONB_H + +#include "general.h" + +class onb { + public: + onb() {} + + vec3 operator[](int i) const { return axis[i]; } + vec3& operator[](int i) { return axis[i]; } + + vec3 u() const { return axis[0]; } + vec3 v() const { return axis[1]; } + vec3 w() const { return axis[2]; } + + vec3 local(double a, double b, double c) const { + return a*u() + b*v() + c*w(); + } + + vec3 local(const vec3& a) const { + return a.x()*u() + a.y()*v() + a.z()*w(); + } + + void build_from_w(const vec3& w) { + vec3 unit_w = w.unit_vector(); + vec3 a = (fabs(unit_w.x()) > 0.9) ? vec3(0,1,0) : vec3(1,0,0); + vec3 v = cross(unit_w, a).unit_vector(); + vec3 u = cross(unit_w, v); + axis[0] = u; + axis[1] = v; + axis[2] = unit_w; + } + + public: + vec3 axis[3]; +}; + + +#endif \ No newline at end of file From b7a2945b54bc7de4d43fd7f5e5d010874592ce9f Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 22 Mar 2024 17:48:00 -0400 Subject: [PATCH 006/142] CA-91 cosine sampling lambertian --- material.h | 16 +++++++--------- vec3.cc | 12 ++++++++++++ vec3.h | 1 + 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/material.h b/material.h index f57be75..316b187 100644 --- a/material.h +++ b/material.h @@ -4,6 +4,7 @@ #include "general.h" #include "hitinfo.h" #include "texture.h" +#include "onb.h" class hit_record; @@ -30,12 +31,9 @@ class lambertian : public material { lambertian(shared_ptr a) : albedo(a) {} virtual bool scatter(const ray& r_in, const HitInfo& rec, color& attenuation, ray& scattered) const override { - - // auto scatter_direction = rec.normal + random_unit_vector(); - auto scatter_direction = rec.normal + random_unit_vector(); - if (scatter_direction.near_zero()) { - scatter_direction = rec.normal; - } + onb uvw; + uvw.build_from_w(rec.normal); + auto scatter_direction = uvw.local(random_cosine_direction()); scattered = ray(rec.pos, scatter_direction, r_in.time()); return true; @@ -55,9 +53,9 @@ class lambertian : public material { */ //A lambertians PDF is 1 / (2 * pi) virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { - // return 1 / (2 * pi); - double cos_theta = fmax(0.0, dot(rec.normal, scattered.direction())); - return cos_theta / pi; + onb uvw; + uvw.build_from_w(rec.normal); + return dot(uvw.w(), scattered.direction()) / pi; } private: diff --git a/vec3.cc b/vec3.cc index 0e58088..b540340 100644 --- a/vec3.cc +++ b/vec3.cc @@ -119,6 +119,18 @@ vec3 random_in_unit_disk() { return rand_vec.unit_vector(); } +vec3 random_cosine_direction() { + auto r1 = random_double(); + auto r2 = random_double(); + + auto phi = 2*pi*r1; + auto x = cos(phi)*sqrt(r2); + auto y = sin(phi)*sqrt(r2); + auto z = sqrt(1-r2); + + return vec3(x, y, z); +} + vec3 reflect(const vec3& v, const vec3& n) { return v - 2*n * dot(v, n); } vec3 refract(const vec3& uv, const vec3& n, float etai_over_etat) { diff --git a/vec3.h b/vec3.h index e8cd975..d6f4b58 100644 --- a/vec3.h +++ b/vec3.h @@ -80,6 +80,7 @@ vec3 random_unit_vector(); vec3 random_in_unit_sphere(); vec3 random_in_hemisphere(const vec3& normal); vec3 random_in_unit_disk(); +vec3 random_cosine_direction(); // reflection and refraction vec3 reflect(const vec3& v, const vec3& n); From 642af9bbe93ba66b8adf2606c13c7c6ffa72f72b Mon Sep 17 00:00:00 2001 From: connortbot Date: Sat, 23 Mar 2024 02:06:42 -0400 Subject: [PATCH 007/142] CA-91 moving files into includes and introducing light ptr vectors --- hitinfo.h => include/hitinfo.h | 0 scene.h => include/scene.h | 6 ++++++ scene.cc | 4 ++++ 3 files changed, 10 insertions(+) rename hitinfo.h => include/hitinfo.h (100%) rename scene.h => include/scene.h (85%) diff --git a/hitinfo.h b/include/hitinfo.h similarity index 100% rename from hitinfo.h rename to include/hitinfo.h diff --git a/scene.h b/include/scene.h similarity index 85% rename from scene.h rename to include/scene.h index ee3ddbc..9457ce7 100644 --- a/scene.h +++ b/include/scene.h @@ -6,6 +6,7 @@ #include "camera.h" #include "material.h" #include "primitive.h" +#include "light.h" #include "hitinfo.h" // SCENE INTERFACE @@ -29,6 +30,9 @@ class Scene { std::map> geom_map; RTCScene rtc_scene; + std::vector> physical_lights; + std::vector> lights; + // Default Constructor // requires a device to initialize RTCScene Scene(RTCDevice device, Camera cam); @@ -36,6 +40,8 @@ class Scene { void commitScene(); void releaseScene(); unsigned int add_primitive(std::shared_ptr prim); + + void add_physical_light(std::shared_ptr geom_ptr); }; void add_sphere(RTCDevice device, RTCScene scene); diff --git a/scene.cc b/scene.cc index cba8cda..da0ecf4 100644 --- a/scene.cc +++ b/scene.cc @@ -18,6 +18,10 @@ unsigned int Scene::add_primitive(std::shared_ptr prim) { return primID; } +void Scene::add_physical_light(std::shared_ptr geom_ptr) { + physical_lights.push_back(geom_ptr); +} + void Scene::commitScene() { rtcCommitScene(rtc_scene); } void Scene::releaseScene() { rtcReleaseScene(rtc_scene); } From 9475daed9220a7cbc71e71e182767c8efbff3dd3 Mon Sep 17 00:00:00 2001 From: connortbot Date: Mon, 25 Mar 2024 00:45:53 -0400 Subject: [PATCH 008/142] CA-91 direct light sampling (only impl. quad lights and physical lights) --- geometry.h | 7 +++++++ include/render.h | 47 ++++++++++++++++++++++++++++++++++++++++++++--- main.cc | 45 +++++++++++++++++++++++---------------------- quad_primitive.cc | 11 +++++++++++ quad_primitive.h | 3 +++ 5 files changed, 88 insertions(+), 25 deletions(-) diff --git a/geometry.h b/geometry.h index 6f43615..913c35b 100644 --- a/geometry.h +++ b/geometry.h @@ -14,6 +14,13 @@ class Geometry : public Visual { virtual shared_ptr materialById(unsigned int geomID) const = 0; virtual HitInfo getHitInfo(const ray& r, const vec3& p, const float t, unsigned int geomID) const = 0; + + virtual point3 sample(const HitInfo& rec) const { + return point3(0,0,0); + }; + virtual double pdf(const HitInfo& rec, ray sample_ray) const { + return 0.0; + }; }; #endif \ No newline at end of file diff --git a/include/render.h b/include/render.h index 3b057d2..35b4d9d 100644 --- a/include/render.h +++ b/include/render.h @@ -32,19 +32,60 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { if (!mat_ptr->scatter(r_in, record, attenuation, scattered)) { // sets scattered to sampled BRDF return accumulated_color; } + + if (i == 0) { + // Direct Light Sampling + for (auto& light_ptr : scene->physical_lights) { // only accounts for physical lights currently + point3 sampled_point = light_ptr->sample(record); + vec3 light_dir = (sampled_point - record.pos); + ray sampled_ray = ray(record.pos, light_dir, 0.0); + + // Trace a ray from here to the light + struct RTCRayHit light_rayhit; + setupRayHit1(light_rayhit, sampled_ray); + + rtcIntersect1(scene->rtc_scene, &light_rayhit); + if (light_rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID) { // we hit something + std::shared_ptr light_geomhit = scene->geom_map[light_rayhit.hit.geomID]; + if (light_geomhit == light_ptr) { // if it is the light, we are not obscured from the light + // Store hit data of tracing the ray from here to the light + HitInfo light_record; + light_record = light_geomhit->getHitInfo(sampled_ray, sampled_ray.at(light_rayhit.ray.tfar), light_rayhit.ray.tfar, light_rayhit.hit.geomID); + + // Get the light's material + std::shared_ptr light_mat_ptr = light_geomhit->materialById(light_rayhit.hit.geomID); + + ray inverse = ray(sampled_point, -light_dir, light_rayhit.ray.tfar); + ray light_scatter; + mat_ptr->scatter(inverse, record, attenuation, light_scatter); + + // Calculate direct light contribution + color light_color = light_mat_ptr->emitted(light_record.u, light_record.v, light_record.pos); + // BRDF of light as the incoming source + color light_brdf = mat_ptr->generate(inverse, light_scatter, record); + // Calculate cos using the surface normal of the hit object and the direction from the object to the light. + double light_cos_theta = fmax(0.0, dot(record.normal, light_dir.unit_vector())); + // Evaluate the PDF considering the probability of sampling the point on the light source from the objects POV. + double light_pdf_value = light_ptr->pdf(light_record, sampled_ray); + color direct_contribution = (light_brdf * light_color * light_cos_theta) / light_pdf_value; + accumulated_color += weight * direct_contribution; + } + } + } + } + color brdf_value = mat_ptr->generate(r_in, scattered, record); double pdf_value = mat_ptr->pdf(r_in, scattered, record); double cos_theta = fmax(0.0, dot(record.normal, scattered.direction())); weight = weight * ((brdf_value * cos_theta) / pdf_value); - - // direct light checks occur here. } else { // Sky background (gradient blue-white) vec3 unit_direction = r_in.direction().unit_vector(); auto t = 0.5*(unit_direction.y() + 1.0); - color sky = (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0); // lerp formula (1.0-t)*start + t*endval + color sky = color(0,0,0); + //color sky = (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0); // lerp formula (1.0-t)*start + t*endval accumulated_color += weight * sky; return accumulated_color; } diff --git a/main.cc b/main.cc index 99be889..2112495 100644 --- a/main.cc +++ b/main.cc @@ -177,7 +177,7 @@ void render_scanlines(int lines, int start_line, std::shared_ptr scene_pt auto u = (i + random_double()) / (image_width-1); auto v = (j + random_double()) / (image_height-1); ray r = cam.get_ray(u, v); - pixel_color += colorize_ray(r, scene_ptr, max_depth); + pixel_color += trace_ray(r, scene_ptr, max_depth); } int buffer_index = j * image_width + i; @@ -458,31 +458,31 @@ void output(RenderData& render_data, Camera& cam, std::shared_ptr scene_p render_data.completed_lines = 0; // To render entire thing without multithreading, uncomment this line and comment out num_threads -> threads.clear() - //render_scanlines_sse(image_height,image_height-1,scene_ptr,render_data,cam); + render_scanlines(image_height,image_height-1,scene_ptr,render_data,cam); - // Threading approach? : Divide the scanlines into N blocks - const int num_threads = std::thread::hardware_concurrency() - 1; + // // Threading approach? : Divide the scanlines into N blocks + // const int num_threads = std::thread::hardware_concurrency() - 1; - // Image height is the number of scanlines, suppose image_height = 800 - const int lines_per_thread = image_height / num_threads; - const int leftOver = image_height % num_threads; - // The first threads are dedicated lines, and the last thread is dedicated to + // // Image height is the number of scanlines, suppose image_height = 800 + // const int lines_per_thread = image_height / num_threads; + // const int leftOver = image_height % num_threads; + // // The first threads are dedicated lines, and the last thread is dedicated to - std::vector pixel_colors; - std::vector threads; + // std::vector pixel_colors; + // std::vector threads; - for (int i=0; i < num_threads; i++) { - // In the first thead, we want the first lines_per_thread lines to be rendered - threads.emplace_back(render_scanlines_sse,lines_per_thread,(image_height-1) - (i * lines_per_thread), scene_ptr, std::ref(render_data),cam); - } - threads.emplace_back(render_scanlines_sse,leftOver,(image_height-1) - (num_threads * lines_per_thread), scene_ptr, std::ref(render_data),cam); + // for (int i=0; i < num_threads; i++) { + // // In the first thead, we want the first lines_per_thread lines to be rendered + // threads.emplace_back(render_scanlines,lines_per_thread,(image_height-1) - (i * lines_per_thread), scene_ptr, std::ref(render_data),cam); + // } + // threads.emplace_back(render_scanlines,leftOver,(image_height-1) - (num_threads * lines_per_thread), scene_ptr, std::ref(render_data),cam); - for (auto &thread : threads) { - thread.join(); - } - std::cerr << "Joining all threads" << std::endl; - threads.clear(); + // for (auto &thread : threads) { + // thread.join(); + // } + // std::cerr << "Joining all threads" << std::endl; + // threads.clear(); int output_type = 2; // 0 for ppm, 1 for jpg, 2 for png // hardcoded, but will be updated for CLI in CA-83 @@ -826,7 +826,7 @@ void simple_light() { void cornell_box() { RenderData render_data; const auto aspect_ratio = 1.0; - setRenderData(render_data, aspect_ratio, 600, 20, 20); + setRenderData(render_data, aspect_ratio, 600, 10, 10); // Set up Camera point3 lookfrom(278, 278, -800); @@ -859,6 +859,7 @@ void cornell_box() { scene_ptr->add_primitive(quad1); scene_ptr->add_primitive(quad2); scene_ptr->add_primitive(quad3); + scene_ptr->add_physical_light(quad3); scene_ptr->add_primitive(quad4); scene_ptr->add_primitive(quad5); scene_ptr->add_primitive(quad6); @@ -907,7 +908,7 @@ void two_perlin_spheres(){ int main(int argc, char* argv[]) { Config config = parseArguments(argc, argv); - switch (5) { + switch (7) { case 1: random_spheres(); break; case 2: two_spheres(); break; case 3: earth(); break; diff --git a/quad_primitive.cc b/quad_primitive.cc index f29a7ff..e0f08e1 100644 --- a/quad_primitive.cc +++ b/quad_primitive.cc @@ -40,3 +40,14 @@ HitInfo QuadPrimitive::getHitInfo(const ray& r, const vec3& p, const float t, un return record; } + +point3 QuadPrimitive::sample(const HitInfo& rec) const { + vec3 random_point = this->position + random_double() * this->u + random_double() * this->v; + return random_point; +} +double QuadPrimitive::pdf(const HitInfo& light_record, ray sample_ray) const { + auto distance_squared = light_record.t * light_record.t * sample_ray.direction().length_squared(); + auto cosine = fabs(dot(sample_ray.direction().unit_vector(), light_record.normal)); + double area = cross(u,v).length(); + return distance_squared / (cosine * area); +} diff --git a/quad_primitive.h b/quad_primitive.h index dd90e43..27bff36 100644 --- a/quad_primitive.h +++ b/quad_primitive.h @@ -23,6 +23,9 @@ class QuadPrimitive : public Primitive { shared_ptr materialById(unsigned int geomID) const override; HitInfo getHitInfo(const ray& r, const vec3& p, const float t, unsigned int geomID) const override; + + point3 sample(const HitInfo& rec) const override; + double pdf(const HitInfo& rec, ray sample_ray) const override; }; #endif From 14b649c0b18e176841856277ef888caec15a3e72 Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 26 Mar 2024 16:13:36 -0400 Subject: [PATCH 009/142] CA-91 sample and pdf for SpherePrimitives --- sphere_primitive.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/sphere_primitive.h b/sphere_primitive.h index 8df29b1..9002bc6 100644 --- a/sphere_primitive.h +++ b/sphere_primitive.h @@ -27,6 +27,23 @@ class SpherePrimitive : public Primitive { return mat_ptr; } + point3 sample(const HitInfo& rec) const override { + vec3 direction = position - rec.pos; + vec3 to_sphere = random_in_hemisphere(direction.unit_vector()); + return position + (to_sphere * radius); + // auto distance_squared = direction.length_squared(); + // onb uvw; + // uvw.build_from_w(direction); + // return uvw.local(random_to_sphere(radius, distance_squared)); + } + + double pdf(const HitInfo& rec, ray sample_ray) const override { + auto cos_theta_max = sqrt(1 - radius*radius/(position - sample_ray.origin()).length_squared()); + auto solid_angle = 2*pi*(1-cos_theta_max); + return 1 / solid_angle; + //return (4 * pi); + } + HitInfo getHitInfo(const ray& r, const vec3& p, const float t, unsigned int geomID) const override { HitInfo record; record.pos = p; @@ -58,6 +75,18 @@ class SpherePrimitive : public Primitive { u = phi / (2*pi); v = theta / pi; } + + static vec3 random_to_sphere(double radius, double distance_squared) { + auto r1 = random_double(); + auto r2 = random_double(); + auto z = 1 + r2*(sqrt(1-radius*radius/distance_squared) - 1); + + auto phi = 2*pi*r1; + auto x = cos(phi)*sqrt(1-z*z); + auto y = sin(phi)*sqrt(1-z*z); + + return vec3(x, y, z); + } }; #endif \ No newline at end of file From d187e4f0029c43fb9178aefe3e366bcffc72740a Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 26 Mar 2024 16:13:55 -0400 Subject: [PATCH 010/142] CA-91 supporting physical lights in CSR by checking emitted fvalue --- include/CSRParser.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/CSRParser.h b/include/CSRParser.h index 7046122..838436f 100644 --- a/include/CSRParser.h +++ b/include/CSRParser.h @@ -111,11 +111,24 @@ class CSRParser { getNextLine(file, position); getNextLine(file, material); getNextLine(file, radius); auto sphere = make_shared(readXYZProperty(position), materials[readStringProperty(material)], readDoubleProperty(radius), device); scene_ptr->add_primitive(sphere); + + // This check is if the material emits anything, but it doesn't really work because it assumes + // that the u and v ints passed in hit a part that emits. In the future, if u and v actually plays into + // whether or not it emits, (e.g one part of the material emits and the rest don't) + // and the u v don't result in that one part, it'll incorrectly ignore it as a light. + if (materials[readStringProperty(material)]->emitted(0, 0, point3(0,0,0)).length() > 0) { + scene_ptr->add_physical_light(sphere); + } } else if (startsWith(line, "Quad")) { std::string position, u, v, material; getNextLine(file, position); getNextLine(file, u); getNextLine(file, v); getNextLine(file, material); auto quad = make_shared(readXYZProperty(position), readXYZProperty(u), readXYZProperty(v), materials[readStringProperty(material)], device); scene_ptr->add_primitive(quad); + + // see above warning in Sphere block + if (materials[readStringProperty(material)]->emitted(0, 0, point3(0,0,0)).length() > 0) { + scene_ptr->add_physical_light(quad); + } } } From 6737cb7702600c17e7081d91f6450cc484e273b7 Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 26 Mar 2024 22:16:03 -0400 Subject: [PATCH 011/142] Update main.cc --- main.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.cc b/main.cc index 2112495..13b87b6 100644 --- a/main.cc +++ b/main.cc @@ -908,7 +908,7 @@ void two_perlin_spheres(){ int main(int argc, char* argv[]) { Config config = parseArguments(argc, argv); - switch (7) { + switch (5) { case 1: random_spheres(); break; case 2: two_spheres(); break; case 3: earth(); break; From 453ca26198f3ea1e5158ad6a95e08901784a02e9 Mon Sep 17 00:00:00 2001 From: connortbot Date: Thu, 28 Mar 2024 00:37:40 -0400 Subject: [PATCH 012/142] CA-95 simple metal BRDF --- material.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/material.h b/material.h index 316b187..f24d8aa 100644 --- a/material.h +++ b/material.h @@ -20,7 +20,7 @@ class material { return color(0,0,0); } virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { - return 0.0; + return 1.0; }; }; @@ -76,6 +76,15 @@ class metal : public material { return (dot(scattered.direction(), rec.normal) > 0); } + virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { + return albedo; + } + + virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { + // Simplified PDF for educational purposes. In reality, specular reflection would require a different approach. + return 0.5 / pi; + } + public: color albedo; From e55e6acd058afcb480319de39200b69770422ad4 Mon Sep 17 00:00:00 2001 From: connortbot Date: Sat, 30 Mar 2024 19:21:17 -0400 Subject: [PATCH 013/142] CA-95 clean up BRDF foundation, cos_theta unique to lambertians. --- include/render.h | 10 +++++----- material.h | 30 +++++++++++++++--------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/include/render.h b/include/render.h index 35b4d9d..ad73917 100644 --- a/include/render.h +++ b/include/render.h @@ -33,7 +33,8 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { return accumulated_color; } - if (i == 0) { + bool direct = false; + if (i == 0 && direct) { // Direct Light Sampling for (auto& light_ptr : scene->physical_lights) { // only accounts for physical lights currently point3 sampled_point = light_ptr->sample(record); @@ -76,16 +77,15 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { color brdf_value = mat_ptr->generate(r_in, scattered, record); double pdf_value = mat_ptr->pdf(r_in, scattered, record); - double cos_theta = fmax(0.0, dot(record.normal, scattered.direction())); - weight = weight * ((brdf_value * cos_theta) / pdf_value); + weight = weight * (brdf_value / pdf_value); } else { // Sky background (gradient blue-white) vec3 unit_direction = r_in.direction().unit_vector(); auto t = 0.5*(unit_direction.y() + 1.0); - color sky = color(0,0,0); - //color sky = (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0); // lerp formula (1.0-t)*start + t*endval + //color sky = color(0,0,0); + color sky = (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0); // lerp formula (1.0-t)*start + t*endval accumulated_color += weight * sky; return accumulated_color; } diff --git a/material.h b/material.h index f24d8aa..8252140 100644 --- a/material.h +++ b/material.h @@ -35,27 +35,21 @@ class lambertian : public material { uvw.build_from_w(rec.normal); auto scatter_direction = uvw.local(random_cosine_direction()); scattered = ray(rec.pos, scatter_direction, r_in.time()); + attenuation = albedo->value(rec.u, rec.v, rec.pos); return true; } // A lambertians BRDF value is its albedo / pi virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { - color a = albedo->value(rec.u, rec.v, rec.pos); - return a / pi; + return albedo->value(rec.u, rec.v, rec.pos); } - /** - * @note some resources say a lambertians PDF is 1 / (2 * pi) - * But Shirley refers to a "perfect match" as cos_theta / pi. - * Whereas 1 / (2 * pi) seems to be the PDF for uniform hemispherical sampling, which we - * no longer support (or really ever did). - */ - //A lambertians PDF is 1 / (2 * pi) virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { - onb uvw; - uvw.build_from_w(rec.normal); - return dot(uvw.w(), scattered.direction()) / pi; + auto cos_theta = dot(rec.normal, scattered.direction().unit_vector()); + double scattering_pdf = (cos_theta < 0 ? 0 : cos_theta/pi); + + return fmax(0.0, cos_theta / pi) / scattering_pdf; } private: @@ -81,8 +75,7 @@ class metal : public material { } virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { - // Simplified PDF for educational purposes. In reality, specular reflection would require a different approach. - return 0.5 / pi; + return 1.0; } public: @@ -98,7 +91,6 @@ class dielectric : public material { dielectric(double index_of_refraction) : ir(index_of_refraction) {} virtual bool scatter(const ray& r_in, const HitInfo& rec, color& attenuation, ray& scattered) const override { - attenuation = color(1.0, 1.0, 1.0); // If the hit is on the front face, ir is the refracted index. // If the hit comes from the outside, then 1.0 is the refracted index (air) double refraction_ratio = rec.front_face ? (1.0/ir) : ir; @@ -117,6 +109,14 @@ class dielectric : public material { scattered = ray(rec.pos, direction, r_in.time()); return true; } + + virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { + return color(1.0, 1.0, 1.0); + } + + virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { + return 1.0; + } public: From daf5d0833a4bda1173bddcd983aa18d7470c5ab2 Mon Sep 17 00:00:00 2001 From: connortbot Date: Sat, 30 Mar 2024 19:21:35 -0400 Subject: [PATCH 014/142] CA-95 OrenNayar BRDF material --- material.h | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/material.h b/material.h index 8252140..ce15f36 100644 --- a/material.h +++ b/material.h @@ -42,6 +42,7 @@ class lambertian : public material { // A lambertians BRDF value is its albedo / pi virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { + // See note in Oren-Nayar generate() function. return albedo->value(rec.u, rec.v, rec.pos); } @@ -135,4 +136,65 @@ class dielectric : public material { } }; +class OrenNayar : public material { + + public: + OrenNayar(color albedo, float roughness) : albedo{albedo}, roughness{roughness} {} + + virtual bool scatter(const ray& r_in, const HitInfo& rec, color& attenuation, ray& scattered) const override { + onb uvw; + uvw.build_from_w(rec.normal); + auto scatter_direction = uvw.local(random_cosine_direction()); + scattered = ray(rec.pos, scatter_direction, r_in.time()); + attenuation = albedo; + + return true; + } + + virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { + vec3 w_i = scattered.direction().unit_vector(); + vec3 w_o = -(r_in.direction().unit_vector()); + + // Calculate azimuthal angles. + vec3 projected_i = (w_i - (dot(w_i, rec.normal) * rec.normal)).unit_vector(); + vec3 projected_o = (w_o - (dot(w_o, rec.normal) * rec.normal)).unit_vector(); + float cos_azimuth = dot(projected_i, projected_o); + + + float theta_i = acos(dot(w_i, rec.normal)); + float theta_o = acos(dot(w_o, rec.normal)); + + float sigma2 = roughness * roughness; + float A = 1 - (sigma2 / (2 * (sigma2 + 0.33))); + + float B = (0.45 * sigma2) / (sigma2 + 0.09); + + float alpha = fmax(theta_i, theta_o); + float beta = fmin(theta_i, theta_o); + + // Multiple sources say that the formula for the Oren-Nayar BRDF includes the R term as albedo, meaning + // diffuse term should be => albedo / pi. + // However, tests ran weirdly dark (on a simple ground sphere and default non-black sky.) Even fully white albedo and + // zero roughness came out gray. + // This problem extends here and default lambertian, which suggests a possible problem in pdf (unlikely, it very often converges to 1) + // or in the rest of the renderer. + color diffuse_term = albedo; + + + return diffuse_term * (A + B * (fmax(0, cos_azimuth) * sin(alpha) * tan(beta))); + } + + virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { + auto cos_theta = dot(rec.normal, scattered.direction().unit_vector()); + double scattering_pdf = (cos_theta < 0 ? 0 : cos_theta/pi); + + return scattering_pdf / fmax(0.0, cos_theta / pi); + //return 1.0; + } + + private: + color albedo; + float roughness; +}; + #endif From a95a7837420d71e8b88cd11c9f13dfa63cf4ba15 Mon Sep 17 00:00:00 2001 From: connortbot Date: Sun, 31 Mar 2024 23:01:54 -0400 Subject: [PATCH 015/142] CA-95 finally corrected rendering equation and proper diffuse and pdf terms for OrenNayar and Lambertian --- include/render.h | 3 ++- main.cc | 39 ++++++++++++++++++++++++++++++++++++++- material.h | 32 ++++++++++++++------------------ 3 files changed, 54 insertions(+), 20 deletions(-) diff --git a/include/render.h b/include/render.h index ad73917..9d2517b 100644 --- a/include/render.h +++ b/include/render.h @@ -76,9 +76,10 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { } color brdf_value = mat_ptr->generate(r_in, scattered, record); + double cos_theta = fmax(0.0, dot(record.normal, -(r_in.direction().unit_vector()))); double pdf_value = mat_ptr->pdf(r_in, scattered, record); - weight = weight * (brdf_value / pdf_value); + weight = weight * (brdf_value * cos_theta / pdf_value); } else { // Sky background (gradient blue-white) vec3 unit_direction = r_in.direction().unit_vector(); diff --git a/main.cc b/main.cc index 13b87b6..ad59bbd 100644 --- a/main.cc +++ b/main.cc @@ -906,9 +906,45 @@ void two_perlin_spheres(){ output(render_data, cam, scene_ptr); } +void brdf_tests() { + RenderData render_data; + const auto aspect_ratio = 16.0 / 9.0; + setRenderData(render_data, aspect_ratio, 1200, 20, 10); + + point3 lookfrom(10, 3, 0); + point3 lookat(0, 2, 0); + vec3 vup(0,1,0); + double vfov = 60; + double aperture = 0.0001; + double dist_to_focus = 10.0; + + Camera cam(lookfrom, lookat, vup, vfov, aspect_ratio, aperture, dist_to_focus, 0.0, 1.0); + + RTCDevice device = initializeDevice(); + auto scene_ptr = make_shared(device, cam); + + color test = color(1.0, 1.0, 1.0); + auto mt = make_shared(test); + auto mt2 = make_shared(test, 0.0); + + auto sphere1 = make_shared(point3(0, 2, 2), mt, 2, device); + auto sphere2 = make_shared(point3(0, 2, -2), mt2, 2, device); + scene_ptr->add_primitive(sphere1); + scene_ptr->add_primitive(sphere2); + + auto red = make_shared(color(1.0, 0.2, 0.2)); + auto ground = make_shared(point3(0,-1000,0), red, 1000, device); + scene_ptr->add_primitive(ground); + + scene_ptr->commitScene(); + rtcReleaseDevice(device); + + output(render_data, cam, scene_ptr); +} + int main(int argc, char* argv[]) { Config config = parseArguments(argc, argv); - switch (5) { + switch (9) { case 1: random_spheres(); break; case 2: two_spheres(); break; case 3: earth(); break; @@ -917,6 +953,7 @@ int main(int argc, char* argv[]) { case 6: simple_light(); break; case 7: cornell_box(); break; case 8: two_perlin_spheres(); break; + case 9: brdf_tests(); break; } } diff --git a/material.h b/material.h index ce15f36..739b4c8 100644 --- a/material.h +++ b/material.h @@ -35,22 +35,18 @@ class lambertian : public material { uvw.build_from_w(rec.normal); auto scatter_direction = uvw.local(random_cosine_direction()); scattered = ray(rec.pos, scatter_direction, r_in.time()); - attenuation = albedo->value(rec.u, rec.v, rec.pos); return true; } // A lambertians BRDF value is its albedo / pi virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { - // See note in Oren-Nayar generate() function. - return albedo->value(rec.u, rec.v, rec.pos); + return albedo->value(rec.u, rec.v, rec.pos) / pi; } virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { auto cos_theta = dot(rec.normal, scattered.direction().unit_vector()); - double scattering_pdf = (cos_theta < 0 ? 0 : cos_theta/pi); - - return fmax(0.0, cos_theta / pi) / scattering_pdf; + return fmax(0.0, cos_theta / pi); } private: @@ -136,6 +132,16 @@ class dielectric : public material { } }; +/** + * @class OrenNayar + * @brief Implements the Oren-Nayar reflectance model for simulating the appearance of rough diffuse surfaces. + * The Oren-Nayar reflectance model is an extension of the Lambertian model that accounts for the roughness of the + * surface, providing a more accurate representation of diffuse reflection from surfaces that are not perfectly smooth. + * + * @note The correctness of the pdf and scatter functions, which use cosine-weighted sampling similar to the Lambertian + * class, may need further verification. + * +*/ class OrenNayar : public material { public: @@ -146,7 +152,6 @@ class OrenNayar : public material { uvw.build_from_w(rec.normal); auto scatter_direction = uvw.local(random_cosine_direction()); scattered = ray(rec.pos, scatter_direction, r_in.time()); - attenuation = albedo; return true; } @@ -172,13 +177,7 @@ class OrenNayar : public material { float alpha = fmax(theta_i, theta_o); float beta = fmin(theta_i, theta_o); - // Multiple sources say that the formula for the Oren-Nayar BRDF includes the R term as albedo, meaning - // diffuse term should be => albedo / pi. - // However, tests ran weirdly dark (on a simple ground sphere and default non-black sky.) Even fully white albedo and - // zero roughness came out gray. - // This problem extends here and default lambertian, which suggests a possible problem in pdf (unlikely, it very often converges to 1) - // or in the rest of the renderer. - color diffuse_term = albedo; + color diffuse_term = albedo / pi; return diffuse_term * (A + B * (fmax(0, cos_azimuth) * sin(alpha) * tan(beta))); @@ -186,10 +185,7 @@ class OrenNayar : public material { virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { auto cos_theta = dot(rec.normal, scattered.direction().unit_vector()); - double scattering_pdf = (cos_theta < 0 ? 0 : cos_theta/pi); - - return scattering_pdf / fmax(0.0, cos_theta / pi); - //return 1.0; + return fmax(0.0, cos_theta / pi); } private: From ed9ae73acc1ad5a847c5260cb04def1d2642c564 Mon Sep 17 00:00:00 2001 From: connortbot Date: Mon, 1 Apr 2024 09:14:21 -0400 Subject: [PATCH 016/142] CA-95 unfinished Cook-Torrance --- include/hitinfo.h | 1 + light.h | 2 +- material.h | 126 +++++++++++++++++++++++++++++++++++++++++++--- vec3.cc | 4 ++ vec3.h | 1 + 5 files changed, 127 insertions(+), 7 deletions(-) diff --git a/include/hitinfo.h b/include/hitinfo.h index 6c5369a..cb8d08b 100644 --- a/include/hitinfo.h +++ b/include/hitinfo.h @@ -4,6 +4,7 @@ struct HitInfo { point3 pos; vec3 normal; + vec3 microfacet_normal; bool front_face; float t; double u; diff --git a/light.h b/light.h index 5cc5203..8dee3f3 100644 --- a/light.h +++ b/light.h @@ -10,7 +10,7 @@ class emissive : public material { public: color emission_color; emissive(color emission_color) : emission_color{emission_color} {} - bool scatter(const ray& r_in, const HitInfo& rec, color& attenuation, ray& scattered) const override { + bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const override { return false; } color emitted(double u, double v, const point3& p) const override { diff --git a/material.h b/material.h index 739b4c8..60e3e45 100644 --- a/material.h +++ b/material.h @@ -15,7 +15,7 @@ class material { return color(0,0,0); } - virtual bool scatter(const ray& r_in, const HitInfo& rec, color& attenuation, ray& scattered) const = 0; + virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const = 0; virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { return color(0,0,0); } @@ -30,7 +30,7 @@ class lambertian : public material { lambertian(const color& a) : albedo(make_shared(a)) {} lambertian(shared_ptr a) : albedo(a) {} - virtual bool scatter(const ray& r_in, const HitInfo& rec, color& attenuation, ray& scattered) const override { + virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const override { onb uvw; uvw.build_from_w(rec.normal); auto scatter_direction = uvw.local(random_cosine_direction()); @@ -59,10 +59,9 @@ class metal : public material { metal(const color& a, double f) : albedo(a), fuzz(f < 1 ? f : 1) {} - virtual bool scatter(const ray& r_in, const HitInfo& rec, color& attenuation, ray& scattered) const override { + virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const override { vec3 reflected = reflect(r_in.direction().unit_vector(), rec.normal); scattered = ray(rec.pos, reflected + fuzz*random_in_unit_sphere(), r_in.time()); - attenuation = albedo; return (dot(scattered.direction(), rec.normal) > 0); } @@ -87,7 +86,7 @@ class dielectric : public material { dielectric(double index_of_refraction) : ir(index_of_refraction) {} - virtual bool scatter(const ray& r_in, const HitInfo& rec, color& attenuation, ray& scattered) const override { + virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const override { // If the hit is on the front face, ir is the refracted index. // If the hit comes from the outside, then 1.0 is the refracted index (air) double refraction_ratio = rec.front_face ? (1.0/ir) : ir; @@ -147,7 +146,7 @@ class OrenNayar : public material { public: OrenNayar(color albedo, float roughness) : albedo{albedo}, roughness{roughness} {} - virtual bool scatter(const ray& r_in, const HitInfo& rec, color& attenuation, ray& scattered) const override { + virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const override { onb uvw; uvw.build_from_w(rec.normal); auto scatter_direction = uvw.local(random_cosine_direction()); @@ -193,4 +192,119 @@ class OrenNayar : public material { float roughness; }; + +class CookTorrance : public material { + + public: + CookTorrance(color albedo, float roughness, float metallic, float reflectance) + : albedo{albedo}, roughness{roughness}, metallic{metallic}, reflectance{reflectance} {} + + virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const override { + vec3 microfacet_normal = random_GGX_microfacet(rec.normal); + rec.microfacet_normal = microfacet_normal; + vec3 scatter_direction = reflect(r_in.direction().unit_vector(), microfacet_normal).unit_vector(); + + //(2 * dot(microfacet_normal, L) * microfacet_normal) - L; + scattered = ray(rec.pos, scatter_direction, r_in.time()); + + // vec3 reflected = reflect(r_in.direction().unit_vector(), rec.normal); + // scattered = ray(rec.pos, reflected + roughness*random_in_unit_sphere(), r_in.time()); + + return (dot(scattered.direction(), rec.normal) > 0); + } + + + virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { + // vec3 L = scattered.direction().unit_vector(); + // vec3 N = rec.normal; + // vec3 V = -(r_in.direction().unit_vector()); + // vec3 H = (V + L).unit_vector(); + + // float NoV = clamp(dot(N, V), 0.0, 1.0); + // float NoL = clamp(dot(N, L), 0.0, 1.0); + // float NoH = clamp(dot(N, H), 0.0, 1.0); + // float VoH = clamp(dot(V, H), 0.0, 1.0); + + // float amt = 0.16 * reflectance * reflectance; + // vec3 f0 = vec3(amt, amt, amt); + // f0 = mix(f0, albedo, metallic); + + // vec3 F = fresnelSchlick(VoH, f0); + // float D = D_GGX(NoH, roughness); + // float G = G_Smith(NoV, NoL, roughness); + + // vec3 spec = (F * D * G) / (4.0 * fmax(NoV, 0.001) * fmax(NoL, 0.001)); + // vec3 spec = color(0.1, 0.1, 0.1); + // vec3 rhoD = albedo; + + // // optionally + // rhoD = rhoD * (vec3(1.0, 1.0, 1.0) + (-F)); + // // rhoD *= disneyDiffuseFactor(NoV, NoL, VoH, roughness); + + // rhoD *= (1.0 - metallic); + // vec3 diff = rhoD / pi; + + // return diff + spec; + return albedo / pi; + } + + virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { + vec3 N = rec.normal; + vec3 M = rec.microfacet_normal; + float NoM = clamp(dot(N, M), 0.0, 1.0); + + float numerator = D_GGX(NoM, roughness) * fmax(0.0,dot(rec.normal, M)); + float denom = 4 * fabs(dot(r_in.direction(), M)); + return numerator / denom; + } + + vec3 random_GGX_microfacet(vec3 N) const { + auto e1 = random_double(); + auto e2 = random_double(); + + float theta = atan(roughness * sqrt(e1 / (1 - e1))); + float phi = 2 * pi * e2; + + // Claculate normal + float x = sin(theta)*cos(phi); + float y = sin(theta)*sin(phi); + float z = cos(theta); + vec3 microfacet_normal = vec3(x, y, z).unit_vector(); + + return (microfacet_normal + N).unit_vector(); + } + + private: + color albedo; + float roughness; // 0-1 + float metallic; // 0.0 or 1.0 + float reflectance; // 0-1 + + + // F, G, D functions + vec3 fresnelSchlick(float cosTheta, vec3 F0) const { + return F0 + (color(1.0, 1.0, 1.0) - F0) * pow(1.0 - cosTheta, 5.0); + } + + float D_GGX(float NoH, float roughness) const { + float alpha = roughness * roughness; + float alpha2 = alpha * alpha; + float NoH2 = NoH * NoH; + float b = (NoH2 * (alpha2 - 1.0) + 1.0); + return (alpha2 * pi) / (b * b); + } + + float G1_GGX_Schlick(float NoV, float roughness) const { + float alpha = roughness * roughness; + float k = alpha / 2.0; + return fmax(NoV, 0.001) / (NoV * (1.0 - k) + k); + } + + float G_Smith(float NoV, float NoL, float roughness) const { + return G1_GGX_Schlick(NoL, roughness) * G1_GGX_Schlick(NoV, roughness); + } + +}; + + #endif diff --git a/vec3.cc b/vec3.cc index b540340..31d10e9 100644 --- a/vec3.cc +++ b/vec3.cc @@ -88,6 +88,10 @@ vec3 cross(const vec3 &u, const vec3 &v) { u.x() * v.y() - u.y() * v.x()}; } +vec3 mix(vec3 x, vec3 y, float a) { + x * (1 - a) + y * a; +} + std::ostream& operator<<(std::ostream &out, const vec3 &v) { return out << v.x() << ' ' << v.y() << ' ' << v.z(); } diff --git a/vec3.h b/vec3.h index d6f4b58..25e4b51 100644 --- a/vec3.h +++ b/vec3.h @@ -71,6 +71,7 @@ vec3 operator/(vec3 v, float t); // vector multiplication float dot(const vec3 &u, const vec3 &v); vec3 cross(const vec3 &u, const vec3 &v); +vec3 mix(vec3 x, vec3 y, float a); /** @brief overloads std::ostream& operator<< to support vec3s */ std::ostream& operator<<(std::ostream &out, const vec3 &v); From f21cacae4135441d5d4a6797ea38ba51cd18452d Mon Sep 17 00:00:00 2001 From: connortbot Date: Mon, 1 Apr 2024 09:14:38 -0400 Subject: [PATCH 017/142] Testing func for BRDFs --- main.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/main.cc b/main.cc index ad59bbd..8efcbbe 100644 --- a/main.cc +++ b/main.cc @@ -923,12 +923,15 @@ void brdf_tests() { RTCDevice device = initializeDevice(); auto scene_ptr = make_shared(device, cam); + //auto mt = make_shared(color(0.7, 0.6, 0.77), 0.1); color test = color(1.0, 1.0, 1.0); auto mt = make_shared(test); auto mt2 = make_shared(test, 0.0); + auto mt3 = make_shared(color(0.8, 0.4, 0.4), 0.1, 1.0, 0.5); + //auto mt = make_shared(1.5); auto sphere1 = make_shared(point3(0, 2, 2), mt, 2, device); - auto sphere2 = make_shared(point3(0, 2, -2), mt2, 2, device); + auto sphere2 = make_shared(point3(0, 2, -2), mt3, 2, device); scene_ptr->add_primitive(sphere1); scene_ptr->add_primitive(sphere2); @@ -940,6 +943,10 @@ void brdf_tests() { rtcReleaseDevice(device); output(render_data, cam, scene_ptr); + + std::cout << mt3->random_GGX_microfacet(vec3(0,1,0)) << std::endl; + std::cout << mt3->random_GGX_microfacet(vec3(0,1,0)) << std::endl; + std::cout << mt3->random_GGX_microfacet(vec3(0,1,0)) << std::endl; } int main(int argc, char* argv[]) { From 2e2f2cdd9c291050448042586fad142f95dfaf75 Mon Sep 17 00:00:00 2001 From: connortbot Date: Mon, 1 Apr 2024 13:13:11 -0400 Subject: [PATCH 018/142] Update render.h --- include/render.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/render.h b/include/render.h index 9d2517b..3807668 100644 --- a/include/render.h +++ b/include/render.h @@ -76,7 +76,7 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { } color brdf_value = mat_ptr->generate(r_in, scattered, record); - double cos_theta = fmax(0.0, dot(record.normal, -(r_in.direction().unit_vector()))); + double cos_theta = fmax(0.0, dot(record.normal, (scattered.direction().unit_vector()))); double pdf_value = mat_ptr->pdf(r_in, scattered, record); weight = weight * (brdf_value * cos_theta / pdf_value); From 3ed2511a5d46da03da92c266c8afbfc62eccd530 Mon Sep 17 00:00:00 2001 From: connortbot Date: Mon, 1 Apr 2024 22:41:16 -0400 Subject: [PATCH 019/142] CA-95 account for exiting mediums instead of entering --- include/render.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/render.h b/include/render.h index 3807668..2a8258e 100644 --- a/include/render.h +++ b/include/render.h @@ -76,7 +76,7 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { } color brdf_value = mat_ptr->generate(r_in, scattered, record); - double cos_theta = fmax(0.0, dot(record.normal, (scattered.direction().unit_vector()))); + double cos_theta = fabs(dot(record.normal, (scattered.direction().unit_vector()))); double pdf_value = mat_ptr->pdf(r_in, scattered, record); weight = weight * (brdf_value * cos_theta / pdf_value); From c2a5b070fe8ca8e18aaef0165b81a67002a0d7d7 Mon Sep 17 00:00:00 2001 From: connortbot Date: Mon, 1 Apr 2024 22:41:31 -0400 Subject: [PATCH 020/142] update BRDF testing func --- main.cc | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/main.cc b/main.cc index 8efcbbe..9f45a49 100644 --- a/main.cc +++ b/main.cc @@ -927,7 +927,7 @@ void brdf_tests() { color test = color(1.0, 1.0, 1.0); auto mt = make_shared(test); auto mt2 = make_shared(test, 0.0); - auto mt3 = make_shared(color(0.8, 0.4, 0.4), 0.1, 1.0, 0.5); + auto mt3 = make_shared(color(1.0, 1.0, 1.0), 0.01, 0.0, 0.0); //auto mt = make_shared(1.5); auto sphere1 = make_shared(point3(0, 2, 2), mt, 2, device); @@ -943,10 +943,6 @@ void brdf_tests() { rtcReleaseDevice(device); output(render_data, cam, scene_ptr); - - std::cout << mt3->random_GGX_microfacet(vec3(0,1,0)) << std::endl; - std::cout << mt3->random_GGX_microfacet(vec3(0,1,0)) << std::endl; - std::cout << mt3->random_GGX_microfacet(vec3(0,1,0)) << std::endl; } int main(int argc, char* argv[]) { From 45e0c402f55fec61dc8b2374ea57f6094b1a7722 Mon Sep 17 00:00:00 2001 From: connortbot Date: Mon, 1 Apr 2024 22:42:04 -0400 Subject: [PATCH 021/142] CA-95 corrected GGX MDF, scatter function and Cook Torrance BRDF. Added PDF for GGX --- material.h | 105 ++++++++++++++++++++++++++++------------------------- vec3.cc | 2 +- 2 files changed, 56 insertions(+), 51 deletions(-) diff --git a/material.h b/material.h index 60e3e45..4a76e10 100644 --- a/material.h +++ b/material.h @@ -202,76 +202,61 @@ class CookTorrance : public material { virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const override { vec3 microfacet_normal = random_GGX_microfacet(rec.normal); rec.microfacet_normal = microfacet_normal; - vec3 scatter_direction = reflect(r_in.direction().unit_vector(), microfacet_normal).unit_vector(); - - //(2 * dot(microfacet_normal, L) * microfacet_normal) - L; + vec3 scatter_direction = reflect(r_in.direction().unit_vector(), microfacet_normal); scattered = ray(rec.pos, scatter_direction, r_in.time()); - // vec3 reflected = reflect(r_in.direction().unit_vector(), rec.normal); - // scattered = ray(rec.pos, reflected + roughness*random_in_unit_sphere(), r_in.time()); - return (dot(scattered.direction(), rec.normal) > 0); } virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { - // vec3 L = scattered.direction().unit_vector(); - // vec3 N = rec.normal; - // vec3 V = -(r_in.direction().unit_vector()); - // vec3 H = (V + L).unit_vector(); + vec3 L = scattered.direction().unit_vector(); + vec3 N = rec.normal; + vec3 V = -(r_in.direction().unit_vector()); + vec3 H = (V + L).unit_vector(); - // float NoV = clamp(dot(N, V), 0.0, 1.0); - // float NoL = clamp(dot(N, L), 0.0, 1.0); - // float NoH = clamp(dot(N, H), 0.0, 1.0); - // float VoH = clamp(dot(V, H), 0.0, 1.0); + float NoV = clamp(dot(N, V), 0.0, 1.0); + float NoL = clamp(dot(N, L), 0.0, 1.0); + float NoH = clamp(dot(N, H), 0.0, 1.0); + float VoH = clamp(dot(V, H), 0.0, 1.0); - // float amt = 0.16 * reflectance * reflectance; - // vec3 f0 = vec3(amt, amt, amt); - // f0 = mix(f0, albedo, metallic); + float amt = 0.16 * reflectance * reflectance; + vec3 f0 = vec3(amt, amt, amt); + f0 = mix(f0, albedo, metallic); - // vec3 F = fresnelSchlick(VoH, f0); - // float D = D_GGX(NoH, roughness); - // float G = G_Smith(NoV, NoL, roughness); + vec3 F = fresnelSchlick(VoH, f0); + float D = D_GGX(NoH, roughness); + float G = G_Smith(NoV, NoL, roughness); - // vec3 spec = (F * D * G) / (4.0 * fmax(NoV, 0.001) * fmax(NoL, 0.001)); - // vec3 spec = color(0.1, 0.1, 0.1); - // vec3 rhoD = albedo; + vec3 spec = (F * D * G) / (4.0 * fmax(NoV, 0.001) * fmax(NoL, 0.001)); + vec3 rhoD = albedo; - // // optionally - // rhoD = rhoD * (vec3(1.0, 1.0, 1.0) + (-F)); - // // rhoD *= disneyDiffuseFactor(NoV, NoL, VoH, roughness); + // optionally + rhoD = rhoD * (vec3(1.0, 1.0, 1.0) + (-F)); - // rhoD *= (1.0 - metallic); - // vec3 diff = rhoD / pi; + rhoD *= (1.0 - metallic); + vec3 diff = rhoD / pi; - // return diff + spec; - return albedo / pi; + return diff + spec; } virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { + vec3 V = -r_in.direction().unit_vector(); + vec3 L = scattered.direction().unit_vector(); + vec3 H = (V + L).unit_vector(); vec3 N = rec.normal; - vec3 M = rec.microfacet_normal; - float NoM = clamp(dot(N, M), 0.0, 1.0); - float numerator = D_GGX(NoM, roughness) * fmax(0.0,dot(rec.normal, M)); - float denom = 4 * fabs(dot(r_in.direction(), M)); - return numerator / denom; - } + float NoH = clamp(dot(N, H), 0.0, 1.0); + float VoH = clamp(dot(V, H), 0.0, 1.0); - vec3 random_GGX_microfacet(vec3 N) const { - auto e1 = random_double(); - auto e2 = random_double(); - - float theta = atan(roughness * sqrt(e1 / (1 - e1))); - float phi = 2 * pi * e2; - - // Claculate normal - float x = sin(theta)*cos(phi); - float y = sin(theta)*sin(phi); - float z = cos(theta); - vec3 microfacet_normal = vec3(x, y, z).unit_vector(); + float D = D_GGX(NoH, roughness); + // Convert D(N·H) to pdf based on the microfacet normal distribution. + // The Jacobian of the half-vector reflection transformation is |4 * (V·H)|. + // This accounts for the change in area density when mapping from H to L. + float jacobian = 4.0 * abs(dot(V, H)); + if (jacobian < 0.0001) return 0; - return (microfacet_normal + N).unit_vector(); + return D / jacobian; } private: @@ -291,7 +276,7 @@ class CookTorrance : public material { float alpha2 = alpha * alpha; float NoH2 = NoH * NoH; float b = (NoH2 * (alpha2 - 1.0) + 1.0); - return (alpha2 * pi) / (b * b); + return (alpha2 / pi) / (b * b); } float G1_GGX_Schlick(float NoV, float roughness) const { @@ -304,6 +289,26 @@ class CookTorrance : public material { return G1_GGX_Schlick(NoL, roughness) * G1_GGX_Schlick(NoV, roughness); } + vec3 random_GGX_microfacet(vec3 N) const { + onb ortho; + ortho.build_from_w(N); + + auto e1 = random_double(); + auto e2 = random_double(); + + float theta = atan(roughness * sqrt(e1 / (1 - e1))); + float phi = 2 * pi * e2; + + // Calculate normal with y as the up vector + float x = sin(theta) * cos(phi); + float y = sin(theta) * sin(phi); + float z = cos(theta); + vec3 microfacet_normal = vec3(x, y, z).unit_vector(); + + vec3 adjusted = ortho.local(microfacet_normal); + return adjusted; + } + }; diff --git a/vec3.cc b/vec3.cc index 31d10e9..cf5d6c6 100644 --- a/vec3.cc +++ b/vec3.cc @@ -89,7 +89,7 @@ vec3 cross(const vec3 &u, const vec3 &v) { } vec3 mix(vec3 x, vec3 y, float a) { - x * (1 - a) + y * a; + return x * (1 - a) + y * a; } std::ostream& operator<<(std::ostream &out, const vec3 &v) { From 52663371803ad9e4acc44ba2062c4cb3c8a3ced8 Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 2 Apr 2024 15:48:04 -0400 Subject: [PATCH 022/142] CA-95 remove CookTorrance unnecessary fields --- main.cc | 2 +- material.h | 28 +++++++++++----------------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/main.cc b/main.cc index 9f45a49..ecc41cb 100644 --- a/main.cc +++ b/main.cc @@ -927,7 +927,7 @@ void brdf_tests() { color test = color(1.0, 1.0, 1.0); auto mt = make_shared(test); auto mt2 = make_shared(test, 0.0); - auto mt3 = make_shared(color(1.0, 1.0, 1.0), 0.01, 0.0, 0.0); + auto mt3 = make_shared(color(1.0, 0.05, 0.05), 0.05); //auto mt = make_shared(1.5); auto sphere1 = make_shared(point3(0, 2, 2), mt, 2, device); diff --git a/material.h b/material.h index 4a76e10..a768d70 100644 --- a/material.h +++ b/material.h @@ -192,12 +192,17 @@ class OrenNayar : public material { float roughness; }; - +/** + * @class CookTorrance + * @brief Implements the Cook-Torrance BRDF model for simulating the specular reflection of a conductor. + * This is a more complex model of the original `metal` material. + * This implementation uses the GGX (Trowbridge-Reitz) microfacet distribution to simulate the roughness. +*/ class CookTorrance : public material { public: - CookTorrance(color albedo, float roughness, float metallic, float reflectance) - : albedo{albedo}, roughness{roughness}, metallic{metallic}, reflectance{reflectance} {} + CookTorrance(color albedo, float roughness) + : albedo{albedo}, roughness{roughness} {} virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const override { vec3 microfacet_normal = random_GGX_microfacet(rec.normal); @@ -220,24 +225,15 @@ class CookTorrance : public material { float NoH = clamp(dot(N, H), 0.0, 1.0); float VoH = clamp(dot(V, H), 0.0, 1.0); - float amt = 0.16 * reflectance * reflectance; - vec3 f0 = vec3(amt, amt, amt); - f0 = mix(f0, albedo, metallic); - + vec3 f0 = albedo; vec3 F = fresnelSchlick(VoH, f0); + float D = D_GGX(NoH, roughness); float G = G_Smith(NoV, NoL, roughness); vec3 spec = (F * D * G) / (4.0 * fmax(NoV, 0.001) * fmax(NoL, 0.001)); - vec3 rhoD = albedo; - - // optionally - rhoD = rhoD * (vec3(1.0, 1.0, 1.0) + (-F)); - - rhoD *= (1.0 - metallic); - vec3 diff = rhoD / pi; - return diff + spec; + return spec; } virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { @@ -262,8 +258,6 @@ class CookTorrance : public material { private: color albedo; float roughness; // 0-1 - float metallic; // 0.0 or 1.0 - float reflectance; // 0-1 // F, G, D functions From 942fa941381be98a82e9e541f3a343a3e4ce38a6 Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 2 Apr 2024 17:00:01 -0400 Subject: [PATCH 023/142] CA-95 implemented complex CookTorrance variant with refraction and absorption values --- main.cc | 9 ++++++--- material.h | 39 ++++++++++++++++++++++++++++++++++----- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/main.cc b/main.cc index ecc41cb..1f7c13d 100644 --- a/main.cc +++ b/main.cc @@ -927,11 +927,14 @@ void brdf_tests() { color test = color(1.0, 1.0, 1.0); auto mt = make_shared(test); auto mt2 = make_shared(test, 0.0); - auto mt3 = make_shared(color(1.0, 0.05, 0.05), 0.05); + + // Complex example: + auto mt3 = make_shared(0.05, color(1.0, 1.0, 1.0), color(1.0, 1.0, 1.0)); + // Non-complex example: + auto mt4 = make_shared(color(1.0, 0.05, 0.05), 0.0); - //auto mt = make_shared(1.5); auto sphere1 = make_shared(point3(0, 2, 2), mt, 2, device); - auto sphere2 = make_shared(point3(0, 2, -2), mt3, 2, device); + auto sphere2 = make_shared(point3(0, 2, -2), mt4, 2, device); scene_ptr->add_primitive(sphere1); scene_ptr->add_primitive(sphere2); diff --git a/material.h b/material.h index a768d70..27b0fdc 100644 --- a/material.h +++ b/material.h @@ -6,6 +6,8 @@ #include "texture.h" #include "onb.h" +#include + class hit_record; class material { @@ -202,7 +204,10 @@ class CookTorrance : public material { public: CookTorrance(color albedo, float roughness) - : albedo{albedo}, roughness{roughness} {} + : complex{false}, albedo{albedo}, roughness{roughness} {} + + CookTorrance(float roughness, color absorption, color refraction) + : complex(true), roughness{roughness}, absorption_coefficient{absorption}, eta{refraction} {} virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const override { vec3 microfacet_normal = random_GGX_microfacet(rec.normal); @@ -213,7 +218,6 @@ class CookTorrance : public material { return (dot(scattered.direction(), rec.normal) > 0); } - virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { vec3 L = scattered.direction().unit_vector(); vec3 N = rec.normal; @@ -225,8 +229,9 @@ class CookTorrance : public material { float NoH = clamp(dot(N, H), 0.0, 1.0); float VoH = clamp(dot(V, H), 0.0, 1.0); - vec3 f0 = albedo; - vec3 F = fresnelSchlick(VoH, f0); + vec3 f0 = albedo; vec3 F; + if (complex) { F = FrComplex(fabs(dot(V,rec.microfacet_normal)), absorption_coefficient, eta); } + else { F = fresnelSchlick(VoH, f0); } float D = D_GGX(NoH, roughness); float G = G_Smith(NoV, NoL, roughness); @@ -256,9 +261,11 @@ class CookTorrance : public material { } private: + bool complex; color albedo; float roughness; // 0-1 - + color absorption_coefficient; // RGB value of how much to not absorb. The higher the color channel, the less that one is absorbed. + color eta; // RGB value of how much to refract (i.e NOT reflect). The higher the color channel, the less it shows. // F, G, D functions vec3 fresnelSchlick(float cosTheta, vec3 F0) const { @@ -303,6 +310,28 @@ class CookTorrance : public material { return adjusted; } + float FrComplex(float cosTheta_i, std::complex eta) const { + using Complex = std::complex; + cosTheta_i = clamp(cosTheta_i, 0, 1); + float sin2Theta_i = 1 - (cosTheta_i * cosTheta_i); + Complex sin2Theta_t = sin2Theta_i / (eta * eta); + Complex val(1, -2); + Complex cosTheta_t = std::sqrt(val - sin2Theta_t); + + Complex r_parl = (eta * cosTheta_i - cosTheta_t) / + (eta * cosTheta_i + cosTheta_t); + Complex r_perp = (cosTheta_i - eta * cosTheta_t) / + (cosTheta_i + eta * cosTheta_t); + return (std::norm(r_parl) + std::norm(r_perp)) / 2; + } + + vec3 FrComplex(float cosTheta_v, vec3 k, vec3 eta) const { + float x = FrComplex(cosTheta_v, std::complex(eta.x(), k.x())); + float y = FrComplex(cosTheta_v, std::complex(eta.y(), k.y())); + float z = FrComplex(cosTheta_v, std::complex(eta.z(), k.z())); + return vec3(x,y,z); + } + }; From 49775c7968fc1a1994796ffd82cde2278e5893d8 Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 2 Apr 2024 17:00:13 -0400 Subject: [PATCH 024/142] CA-95 minimum roughness value to avoid singularities --- material.h | 1 + 1 file changed, 1 insertion(+) diff --git a/material.h b/material.h index 27b0fdc..7bcf4ee 100644 --- a/material.h +++ b/material.h @@ -273,6 +273,7 @@ class CookTorrance : public material { } float D_GGX(float NoH, float roughness) const { + roughness = fmax(0.0001, roughness); float alpha = roughness * roughness; float alpha2 = alpha * alpha; float NoH2 = NoH * NoH; From 575d567f31845f4193b12ce131cfe92f27a0663f Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 2 Apr 2024 17:31:39 -0400 Subject: [PATCH 025/142] CA-95 fully modular ctrs for CookTorrance, + microfacet classes used --- include/microfacet.h | 68 +++++++++++++++++++++++++++++++++++++++++ main.cc | 2 +- material.h | 73 +++++++++++++++----------------------------- 3 files changed, 93 insertions(+), 50 deletions(-) create mode 100644 include/microfacet.h diff --git a/include/microfacet.h b/include/microfacet.h new file mode 100644 index 0000000..6f77a31 --- /dev/null +++ b/include/microfacet.h @@ -0,0 +1,68 @@ +#ifndef MICROFACET_H +#define MICROFACET_H + +#include "general.h" +#include "onb.h" + +/** + * @class MicrofacetDF + * @brief Classes containing definitions to sample and weight for + * microfacet distributions in BRDFs. +*/ +class Microfacet { + public: + float roughness; + + Microfacet(float roughness) : roughness{roughness} {} + + virtual float D(float cosTheta) const = 0; + virtual float G(float cosTheta_V, float cosTheta_L) const = 0; + virtual vec3 sample(vec3 normal) const = 0; +}; + +class GGX : public Microfacet { + public: + GGX(float roughness) : Microfacet(roughness) {} + + float D(float NoH) const override { + float r = fmax(0.0001, roughness); + float alpha = r * r; + float alpha2 = alpha * alpha; + float NoH2 = NoH * NoH; + float b = (NoH2 * (alpha2 - 1.0) + 1.0); + return (alpha2 / pi) / (b * b); + } + + float G(float NoV, float NoL) const override { + return G1_GGX_Schlick(NoL) * G1_GGX_Schlick(NoV); + } + + vec3 sample(vec3 N) const override { + onb ortho; + ortho.build_from_w(N); + + auto e1 = random_double(); + auto e2 = random_double(); + + float theta = atan(roughness * sqrt(e1 / (1 - e1))); + float phi = 2 * pi * e2; + + // Calculate normal with y as the up vector + float x = sin(theta) * cos(phi); + float y = sin(theta) * sin(phi); + float z = cos(theta); + vec3 microfacet_normal = vec3(x, y, z).unit_vector(); + + vec3 adjusted = ortho.local(microfacet_normal); + return adjusted; + } + + private: + float G1_GGX_Schlick(float NoV) const { + float alpha = roughness * roughness; + float k = alpha / 2.0; + return fmax(NoV, 0.001) / (NoV * (1.0 - k) + k); + } +}; + +#endif \ No newline at end of file diff --git a/main.cc b/main.cc index 1f7c13d..a26cabe 100644 --- a/main.cc +++ b/main.cc @@ -929,7 +929,7 @@ void brdf_tests() { auto mt2 = make_shared(test, 0.0); // Complex example: - auto mt3 = make_shared(0.05, color(1.0, 1.0, 1.0), color(1.0, 1.0, 1.0)); + auto mt3 = make_shared(color(1.0, 1.0, 1.0), color(1.0, 1.0, 1.0), 0.05); // Non-complex example: auto mt4 = make_shared(color(1.0, 0.05, 0.05), 0.0); diff --git a/material.h b/material.h index 7bcf4ee..5a364ed 100644 --- a/material.h +++ b/material.h @@ -7,6 +7,7 @@ #include "onb.h" #include +#include "microfacet.h" class hit_record; @@ -199,18 +200,31 @@ class OrenNayar : public material { * @brief Implements the Cook-Torrance BRDF model for simulating the specular reflection of a conductor. * This is a more complex model of the original `metal` material. * This implementation uses the GGX (Trowbridge-Reitz) microfacet distribution to simulate the roughness. + * + * @param albedo [OPTIONAL] Base colour. + * @param roughness In range [0-1] defines how rough the surface of the material becomes (less shiny). + * @param absorption [OPTIONAL] RGB value of how much to not absorb. The higher the color channel, the less that one is absorbed. + * @param refraction [OPTIONAL] RGB value of how much to refract (i.e NOT reflect). The higher the color channel, the less it shows. + * + * @note by default, if no MDF is specified in the constructor, GGX is used. */ class CookTorrance : public material { public: CookTorrance(color albedo, float roughness) - : complex{false}, albedo{albedo}, roughness{roughness} {} + : complex{false}, albedo{albedo}, MDF{std::make_shared(roughness)} {} + + CookTorrance(color albedo, std::shared_ptr mdf) + : complex(false), albedo{albedo}, MDF{mdf} {} + + CookTorrance(color absorption, color refraction, float roughness) + : complex(true), absorption_coefficient{absorption}, eta{refraction}, MDF{std::make_shared(roughness)} {} - CookTorrance(float roughness, color absorption, color refraction) - : complex(true), roughness{roughness}, absorption_coefficient{absorption}, eta{refraction} {} + CookTorrance(color absorption, color refraction, std::shared_ptr mdf) + : complex(true), absorption_coefficient{absorption}, eta{refraction}, MDF{mdf} {} virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const override { - vec3 microfacet_normal = random_GGX_microfacet(rec.normal); + vec3 microfacet_normal = MDF->sample(rec.normal); rec.microfacet_normal = microfacet_normal; vec3 scatter_direction = reflect(r_in.direction().unit_vector(), microfacet_normal); scattered = ray(rec.pos, scatter_direction, r_in.time()); @@ -233,8 +247,8 @@ class CookTorrance : public material { if (complex) { F = FrComplex(fabs(dot(V,rec.microfacet_normal)), absorption_coefficient, eta); } else { F = fresnelSchlick(VoH, f0); } - float D = D_GGX(NoH, roughness); - float G = G_Smith(NoV, NoL, roughness); + float D = MDF->D(NoH); + float G = MDF->G(NoV, NoL); vec3 spec = (F * D * G) / (4.0 * fmax(NoV, 0.001) * fmax(NoL, 0.001)); @@ -250,7 +264,7 @@ class CookTorrance : public material { float NoH = clamp(dot(N, H), 0.0, 1.0); float VoH = clamp(dot(V, H), 0.0, 1.0); - float D = D_GGX(NoH, roughness); + float D = MDF->D(NoH); // Convert D(N·H) to pdf based on the microfacet normal distribution. // The Jacobian of the half-vector reflection transformation is |4 * (V·H)|. // This accounts for the change in area density when mapping from H to L. @@ -263,54 +277,15 @@ class CookTorrance : public material { private: bool complex; color albedo; - float roughness; // 0-1 - color absorption_coefficient; // RGB value of how much to not absorb. The higher the color channel, the less that one is absorbed. - color eta; // RGB value of how much to refract (i.e NOT reflect). The higher the color channel, the less it shows. + color absorption_coefficient; + color eta; + std::shared_ptr MDF; // F, G, D functions vec3 fresnelSchlick(float cosTheta, vec3 F0) const { return F0 + (color(1.0, 1.0, 1.0) - F0) * pow(1.0 - cosTheta, 5.0); } - float D_GGX(float NoH, float roughness) const { - roughness = fmax(0.0001, roughness); - float alpha = roughness * roughness; - float alpha2 = alpha * alpha; - float NoH2 = NoH * NoH; - float b = (NoH2 * (alpha2 - 1.0) + 1.0); - return (alpha2 / pi) / (b * b); - } - - float G1_GGX_Schlick(float NoV, float roughness) const { - float alpha = roughness * roughness; - float k = alpha / 2.0; - return fmax(NoV, 0.001) / (NoV * (1.0 - k) + k); - } - - float G_Smith(float NoV, float NoL, float roughness) const { - return G1_GGX_Schlick(NoL, roughness) * G1_GGX_Schlick(NoV, roughness); - } - - vec3 random_GGX_microfacet(vec3 N) const { - onb ortho; - ortho.build_from_w(N); - - auto e1 = random_double(); - auto e2 = random_double(); - - float theta = atan(roughness * sqrt(e1 / (1 - e1))); - float phi = 2 * pi * e2; - - // Calculate normal with y as the up vector - float x = sin(theta) * cos(phi); - float y = sin(theta) * sin(phi); - float z = cos(theta); - vec3 microfacet_normal = vec3(x, y, z).unit_vector(); - - vec3 adjusted = ortho.local(microfacet_normal); - return adjusted; - } - float FrComplex(float cosTheta_i, std::complex eta) const { using Complex = std::complex; cosTheta_i = clamp(cosTheta_i, 0, 1); From e621da01c75eac413e1b3a1c3af785cd34cc05c0 Mon Sep 17 00:00:00 2001 From: connortbot Date: Wed, 3 Apr 2024 15:44:50 -0400 Subject: [PATCH 026/142] CA-95 move light sampling after weight calculation --- include/render.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/render.h b/include/render.h index 2a8258e..8055ee5 100644 --- a/include/render.h +++ b/include/render.h @@ -33,6 +33,12 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { return accumulated_color; } + color brdf_value = mat_ptr->generate(r_in, scattered, record); + double cos_theta = fabs(dot(record.normal, (scattered.direction().unit_vector()))); + double pdf_value = mat_ptr->pdf(r_in, scattered, record); + + weight = weight * (brdf_value * cos_theta / pdf_value); + bool direct = false; if (i == 0 && direct) { // Direct Light Sampling @@ -74,19 +80,13 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { } } } - - color brdf_value = mat_ptr->generate(r_in, scattered, record); - double cos_theta = fabs(dot(record.normal, (scattered.direction().unit_vector()))); - double pdf_value = mat_ptr->pdf(r_in, scattered, record); - - weight = weight * (brdf_value * cos_theta / pdf_value); } else { // Sky background (gradient blue-white) vec3 unit_direction = r_in.direction().unit_vector(); auto t = 0.5*(unit_direction.y() + 1.0); - //color sky = color(0,0,0); - color sky = (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0); // lerp formula (1.0-t)*start + t*endval + color sky = color(0,0,0); + // color sky = (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0); // lerp formula (1.0-t)*start + t*endval accumulated_color += weight * sky; return accumulated_color; } From 93579ae45b5d73c7f15fefe4347101557180a510 Mon Sep 17 00:00:00 2001 From: connortbot Date: Wed, 3 Apr 2024 18:46:58 -0400 Subject: [PATCH 027/142] CA-91 use BSDFSample objects for the rendering equation --- include/render.h | 15 ++++++--------- material.h | 28 ++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/include/render.h b/include/render.h index 8055ee5..5321b6b 100644 --- a/include/render.h +++ b/include/render.h @@ -29,15 +29,12 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { color color_from_emission = mat_ptr->emitted(record.u, record.v, record.pos); accumulated_color += weight * color_from_emission; - if (!mat_ptr->scatter(r_in, record, attenuation, scattered)) { // sets scattered to sampled BRDF + BSDFSample sample_data = mat_ptr->sample(r_in, record, scattered); + if (!sample_data.scatter) { return accumulated_color; } - - color brdf_value = mat_ptr->generate(r_in, scattered, record); - double cos_theta = fabs(dot(record.normal, (scattered.direction().unit_vector()))); - double pdf_value = mat_ptr->pdf(r_in, scattered, record); - - weight = weight * (brdf_value * cos_theta / pdf_value); + double cos_theta = fabs(dot(record.normal, (sample_data.scatter_direction))); + weight = weight * (sample_data.bsdf_value * cos_theta / sample_data.pdf_value); bool direct = false; if (i == 0 && direct) { @@ -85,8 +82,8 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { vec3 unit_direction = r_in.direction().unit_vector(); auto t = 0.5*(unit_direction.y() + 1.0); - color sky = color(0,0,0); - // color sky = (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0); // lerp formula (1.0-t)*start + t*endval + //color sky = color(0,0,0); + color sky = (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0); // lerp formula (1.0-t)*start + t*endval accumulated_color += weight * sky; return accumulated_color; } diff --git a/material.h b/material.h index 5a364ed..b4dea4c 100644 --- a/material.h +++ b/material.h @@ -11,6 +11,13 @@ class hit_record; +struct BSDFSample { + bool scatter; + vec3 scatter_direction; + color bsdf_value; + float pdf_value; +}; + class material { public: @@ -18,13 +25,30 @@ class material { return color(0,0,0); } - virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const = 0; + virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { + return true; + } virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { return color(0,0,0); } virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { return 1.0; }; + + virtual BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const { + BSDFSample sample_data; + // Sample the microfacet distribution to get the scatter direction. + color attenuation; // placeholder until it gets removed from the scatter function header + sample_data.scatter = scatter(r_in, rec, attenuation, scattered); + sample_data.scatter_direction = scattered.direction().unit_vector(); + + // Sample the BRDF for the value + sample_data.bsdf_value = generate(r_in, scattered, rec); + + // Find the PDF for the MDF + sample_data.pdf_value = pdf(r_in, scattered, rec); + return sample_data; + } }; class lambertian : public material { @@ -116,7 +140,7 @@ class dielectric : public material { virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { return 1.0; } - + public: From 755881f8c3f56f0bf49437461e2ac37ed99cc01f Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 5 Apr 2024 14:50:15 -0400 Subject: [PATCH 028/142] CA-95 not working dielectric --- main.cc | 5 +- material.h | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+), 1 deletion(-) diff --git a/main.cc b/main.cc index a26cabe..3198e22 100644 --- a/main.cc +++ b/main.cc @@ -932,8 +932,9 @@ void brdf_tests() { auto mt3 = make_shared(color(1.0, 1.0, 1.0), color(1.0, 1.0, 1.0), 0.05); // Non-complex example: auto mt4 = make_shared(color(1.0, 0.05, 0.05), 0.0); + auto mt5 = make_shared(0.01, 0.01); - auto sphere1 = make_shared(point3(0, 2, 2), mt, 2, device); + auto sphere1 = make_shared(point3(0, 2, 2), mt5, 2, device); auto sphere2 = make_shared(point3(0, 2, -2), mt4, 2, device); scene_ptr->add_primitive(sphere1); scene_ptr->add_primitive(sphere2); @@ -946,6 +947,8 @@ void brdf_tests() { rtcReleaseDevice(device); output(render_data, cam, scene_ptr); + + std::cout << mt5->FrDielectric(0.995037) << std::endl; } int main(int argc, char* argv[]) { diff --git a/material.h b/material.h index b4dea4c..605acbc 100644 --- a/material.h +++ b/material.h @@ -335,4 +335,156 @@ class CookTorrance : public material { }; +class CookTorranceDielectric : public material { + + public: + CookTorranceDielectric(float eta, float roughness) : eta{eta}, MDF{std::make_shared(roughness)} {} + + BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const override { + BSDFSample sample_data; + + vec3 microfacet_normal = MDF->sample(rec.normal); + rec.microfacet_normal = microfacet_normal; + + float cosTheta_i = dot(-r_in.direction().unit_vector(), microfacet_normal); + float R = FrDielectric(cosTheta_i); + float T = 1 - R; + + float u = random_double(); + + if (u < (R / (R + T))) { // reflectance + + vec3 scatter_direction = reflect(r_in.direction().unit_vector(), microfacet_normal); + scattered = ray(rec.pos, scatter_direction, r_in.time()); + sample_data.scatter_direction = scatter_direction; + sample_data.scatter = (dot(scattered.direction(), rec.normal) > 0); + + sample_data.bsdf_value = f_r(r_in, rec, scattered, R); + sample_data.pdf_value = pdf_r(r_in, rec, scattered, R); + + + } else { // transmission + double refraction_ratio = rec.front_face ? (1.0/eta) : eta; + vec3 scatter_direction = refract(r_in.direction().unit_vector(), microfacet_normal, refraction_ratio); + scattered = ray(rec.pos, scatter_direction, r_in.time()); + sample_data.scatter = (dot(scattered.direction(), rec.normal) < 0); + + sample_data.bsdf_value = f_t(r_in, rec, scattered, T); + sample_data.pdf_value = pdf_t(r_in, rec, scattered, T); + } + + return sample_data; + } + + //private: + float eta; + std::shared_ptr MDF; + + float FrDielectric(float cosTheta_i) const { + float temp_eta = eta; + cosTheta_i = clamp(cosTheta_i, -1.0, 1.0); + if (cosTheta_i < 0) { + temp_eta = 1 / eta; + cosTheta_i = -cosTheta_i; + } + + float sin2Theta_i = 1 - (cosTheta_i * cosTheta_i); + float sin2Theta_t = sin2Theta_i / (temp_eta * temp_eta); + if (sin2Theta_t >= 1.0) { + return 1.0; + } + float cosTheta_t = sqrt(1 - sin2Theta_t); + float r_parallel = (temp_eta * cosTheta_i - cosTheta_t) / (temp_eta * cosTheta_i + cosTheta_t); + float r_perp = (cosTheta_i - (temp_eta * cosTheta_t)) / (cosTheta_i + (eta * cosTheta_t)); + return ((r_parallel * r_parallel) + (r_perp * r_perp)) / 2; + } + private: + color f_r(const ray& r_in, HitInfo& rec, ray& scattered, float R) const { + vec3 V = -r_in.direction().unit_vector(); + vec3 L = scattered.direction().unit_vector(); + vec3 H = (V + L).unit_vector(); + vec3 N = rec.normal; + + float NoH = clamp(dot(N, H), 0.0, 1.0); + float NoV = clamp(dot(N, V), 0.0, 1.0); + float NoL = clamp(dot(N, L), 0.0, 1.0); + + float D = MDF->D(NoH); + float G = MDF->G(NoV, NoL); + + color R_col = color(R, R, R); + + color num = D * G * R_col; + float denom = (4.0 * fmax(NoV, 0.001) * fmax(NoL, 0.001)); + + return num / denom; + } + + float pdf_r(const ray& r_in, HitInfo& rec, ray& scattered, float R) const { + vec3 V = -r_in.direction().unit_vector(); + vec3 L = scattered.direction().unit_vector(); + vec3 H = (V + L).unit_vector(); + vec3 N = rec.normal; + + float NoH = clamp(dot(N, H), 0.0, 1.0); + float VoH = clamp(dot(V, H), 0.0, 1.0); + + float D = MDF->D(NoH); + // Convert D(N·H) to pdf based on the microfacet normal distribution. + // The Jacobian of the half-vector reflection transformation is |4 * (V·H)|. + // This accounts for the change in area density when mapping from H to L. + float jacobian = 4.0 * abs(dot(V, H)); + if (jacobian < 0.0001) return 0; + + return (D * R) / jacobian; + } + + float pdf_t(const ray& r_in, HitInfo& rec, ray& scattered, float T) const { + double etap = rec.front_face ? (1.0/eta) : eta; + + vec3 V = -r_in.direction().unit_vector(); + vec3 L = scattered.direction().unit_vector(); + vec3 H = (V + L).unit_vector(); + vec3 N = rec.normal; + + float NoH = clamp(dot(N, H), 0.0, 1.0); + float VoH = clamp(dot(V, H), 0.0, 1.0); + float LoM = dot(L, rec.microfacet_normal); + + float num = MDF->D(NoH) * fabs(LoM) * T; + float dw = dot(V, rec.microfacet_normal) / etap; + float denom = (LoM + dw) * (LoM + dw); + + return num / denom; + } + + color f_t(const ray& r_in, HitInfo& rec, ray& scattered, float T) const { + double etap = rec.front_face ? (1.0/eta) : eta; + + vec3 V = -r_in.direction().unit_vector(); + vec3 L = scattered.direction().unit_vector(); + vec3 H = (V + L).unit_vector(); + vec3 N = rec.normal; + + float NoH = clamp(dot(N, H), 0.0, 1.0); + float NoV = clamp(dot(N, V), 0.0, 1.0); + float NoL = clamp(dot(N, L), 0.0, 1.0); + float VoM = dot(V, rec.microfacet_normal); + float LoM = dot(L, rec.microfacet_normal); + + color T_col = color(T, T, T); + + float D = MDF->D(NoH); + float G = MDF->G(NoV, NoL); + float dotabs = fabs(VoM * LoM); + color num = D * G * dotabs * T_col; + + float dw = dot(V, rec.microfacet_normal) / etap; + float dw2 = (LoM + dw) * (LoM + dw); + float denom = fmax(NoV, 0.001) * fmax(NoL, 0.001) * dw2; + + return num / denom; + } +}; + #endif From 46d4b9701a57e943a1e404d21098600397aac02b Mon Sep 17 00:00:00 2001 From: connortbot Date: Sun, 7 Apr 2024 02:16:35 -0400 Subject: [PATCH 029/142] CA-95 --- material.h | 96 ++++++++++++++++++++++++++---------------------------- vec3.h | 4 +++ 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/material.h b/material.h index 605acbc..899bdb1 100644 --- a/material.h +++ b/material.h @@ -341,33 +341,36 @@ class CookTorranceDielectric : public material { CookTorranceDielectric(float eta, float roughness) : eta{eta}, MDF{std::make_shared(roughness)} {} BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const override { - BSDFSample sample_data; + // Vectors wo and wi are the outgoing and incident directions respectively. + vec3 wo = -r_in.direction().unit_vector(); - vec3 microfacet_normal = MDF->sample(rec.normal); - rec.microfacet_normal = microfacet_normal; + BSDFSample sample_data; + vec3 N = rec.normal; + vec3 wm = MDF->sample(N); // outward microfacet normal. + rec.microfacet_normal = wm; - float cosTheta_i = dot(-r_in.direction().unit_vector(), microfacet_normal); + float cosTheta_i = dot(wo, wm); float R = FrDielectric(cosTheta_i); float T = 1 - R; float u = random_double(); + double refraction_ratio = rec.front_face ? (1.0/eta) : eta; + double sinTheta_i = sqrt(1.0 - cosTheta_i*cosTheta_i); - if (u < (R / (R + T))) { // reflectance - - vec3 scatter_direction = reflect(r_in.direction().unit_vector(), microfacet_normal); - scattered = ray(rec.pos, scatter_direction, r_in.time()); - sample_data.scatter_direction = scatter_direction; - sample_data.scatter = (dot(scattered.direction(), rec.normal) > 0); + if (u < (R / (R + T)) || refraction_ratio * sinTheta_i > 1.0) { // reflectance + vec3 wi = reflect(-wo, wm); + scattered = ray(rec.pos, wi, r_in.time()); + sample_data.scatter_direction = wi; + sample_data.scatter = (dot(wi, N) > 0); sample_data.bsdf_value = f_r(r_in, rec, scattered, R); sample_data.pdf_value = pdf_r(r_in, rec, scattered, R); - } else { // transmission - double refraction_ratio = rec.front_face ? (1.0/eta) : eta; - vec3 scatter_direction = refract(r_in.direction().unit_vector(), microfacet_normal, refraction_ratio); - scattered = ray(rec.pos, scatter_direction, r_in.time()); - sample_data.scatter = (dot(scattered.direction(), rec.normal) < 0); + vec3 wi = refract(-wo, wm, refraction_ratio); + scattered = ray(rec.pos, wi, r_in.time()); + sample_data.scatter_direction = wi; + sample_data.scatter = (dot(wi, N) < 0); sample_data.bsdf_value = f_t(r_in, rec, scattered, T); sample_data.pdf_value = pdf_t(r_in, rec, scattered, T); @@ -442,48 +445,41 @@ class CookTorranceDielectric : public material { float pdf_t(const ray& r_in, HitInfo& rec, ray& scattered, float T) const { double etap = rec.front_face ? (1.0/eta) : eta; - vec3 V = -r_in.direction().unit_vector(); - vec3 L = scattered.direction().unit_vector(); - vec3 H = (V + L).unit_vector(); - vec3 N = rec.normal; - - float NoH = clamp(dot(N, H), 0.0, 1.0); - float VoH = clamp(dot(V, H), 0.0, 1.0); - float LoM = dot(L, rec.microfacet_normal); - - float num = MDF->D(NoH) * fabs(LoM) * T; - float dw = dot(V, rec.microfacet_normal) / etap; - float denom = (LoM + dw) * (LoM + dw); - - return num / denom; + vec3 wo = -r_in.direction().unit_vector(); + vec3 wi = scattered.direction().unit_vector(); + vec3 wn = rec.normal; + vec3 wm = rec.microfacet_normal; + vec3 h = (wo + wi).unit_vector(); + + float denom = (dot(wi, wm) + dot(wo, wm) / etap) * (dot(wi, wm) + dot(wo, wm) / etap); + float dwm_dwi = fabs(dot(wi, wm)) / denom; + float NoM = dot(wm, wn); + float D = MDF->D(NoM); + return D * dwm_dwi * T; } color f_t(const ray& r_in, HitInfo& rec, ray& scattered, float T) const { double etap = rec.front_face ? (1.0/eta) : eta; - - vec3 V = -r_in.direction().unit_vector(); - vec3 L = scattered.direction().unit_vector(); - vec3 H = (V + L).unit_vector(); - vec3 N = rec.normal; - - float NoH = clamp(dot(N, H), 0.0, 1.0); - float NoV = clamp(dot(N, V), 0.0, 1.0); - float NoL = clamp(dot(N, L), 0.0, 1.0); - float VoM = dot(V, rec.microfacet_normal); - float LoM = dot(L, rec.microfacet_normal); + vec3 wo = -r_in.direction().unit_vector(); + vec3 wi = scattered.direction().unit_vector(); + vec3 wn = rec.normal; + vec3 wm = rec.microfacet_normal; + vec3 h = (wo + wi).unit_vector(); + + float NoM = dot(wm, wn); + float NoO = dot(wn, wo); + float NoI = dot(wn, wi); + float D = MDF->D(NoM); + float G = MDF->G(fabs(NoO), fabs(NoI)); color T_col = color(T, T, T); + color num = D * G * T_col; - float D = MDF->D(NoH); - float G = MDF->G(NoV, NoL); - float dotabs = fabs(VoM * LoM); - color num = D * G * dotabs * T_col; - - float dw = dot(V, rec.microfacet_normal) / etap; - float dw2 = (LoM + dw) * (LoM + dw); - float denom = fmax(NoV, 0.001) * fmax(NoL, 0.001) * dw2; - - return num / denom; + float IoM = dot(wi, wm); + float OoM = dot(wo, wm); + float denom = (IoM + OoM / etap) * (IoM + OoM / etap); + float dotabs = fabs(IoM * OoM / (dot(wi, wn) * dot(wo, wn) * denom)); // 1: e+14, 2: inf + return num * dotabs; } }; diff --git a/vec3.h b/vec3.h index 25e4b51..ca32144 100644 --- a/vec3.h +++ b/vec3.h @@ -84,7 +84,11 @@ vec3 random_in_unit_disk(); vec3 random_cosine_direction(); // reflection and refraction + +// reflects the incoming vector v across the normal n. v is not outward. vec3 reflect(const vec3& v, const vec3& n); + +// refracts the incoming vector v across the normal n. v is not outward. vec3 refract(const vec3& uv, const vec3& n, float etai_over_etat); #endif From 718b0dba458be229a167b3d30ba3b1af8b99c58e Mon Sep 17 00:00:00 2001 From: connortbot Date: Sun, 7 Apr 2024 15:47:01 -0400 Subject: [PATCH 030/142] CA-95 add albedo to cook torrance dielectric --- main.cc | 15 ++++++++------- material.h | 8 ++++---- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/main.cc b/main.cc index 3198e22..db7fa8c 100644 --- a/main.cc +++ b/main.cc @@ -909,7 +909,7 @@ void two_perlin_spheres(){ void brdf_tests() { RenderData render_data; const auto aspect_ratio = 16.0 / 9.0; - setRenderData(render_data, aspect_ratio, 1200, 20, 10); + setRenderData(render_data, aspect_ratio, 1200, 50, 20); point3 lookfrom(10, 3, 0); point3 lookat(0, 2, 0); @@ -931,24 +931,25 @@ void brdf_tests() { // Complex example: auto mt3 = make_shared(color(1.0, 1.0, 1.0), color(1.0, 1.0, 1.0), 0.05); // Non-complex example: - auto mt4 = make_shared(color(1.0, 0.05, 0.05), 0.0); - auto mt5 = make_shared(0.01, 0.01); + auto mt4 = make_shared(color(1.0, 1.0, 1.0), 0.0); + + // Dielectric comparison + auto mt5 = make_shared(color(0.6, 1.0, 0.6), 1.5, 0.0001); // model glass + auto mt7 = make_shared(color(0.6, 1.0, 0.6), 0.0, 0.0001); // model mirror auto sphere1 = make_shared(point3(0, 2, 2), mt5, 2, device); - auto sphere2 = make_shared(point3(0, 2, -2), mt4, 2, device); + auto sphere2 = make_shared(point3(0, 2, -2), mt7, 2, device); scene_ptr->add_primitive(sphere1); scene_ptr->add_primitive(sphere2); auto red = make_shared(color(1.0, 0.2, 0.2)); - auto ground = make_shared(point3(0,-1000,0), red, 1000, device); + auto ground = make_shared(point3(0,-10000,0), red, 10000, device); scene_ptr->add_primitive(ground); scene_ptr->commitScene(); rtcReleaseDevice(device); output(render_data, cam, scene_ptr); - - std::cout << mt5->FrDielectric(0.995037) << std::endl; } int main(int argc, char* argv[]) { diff --git a/material.h b/material.h index 899bdb1..639246d 100644 --- a/material.h +++ b/material.h @@ -334,11 +334,10 @@ class CookTorrance : public material { }; - class CookTorranceDielectric : public material { public: - CookTorranceDielectric(float eta, float roughness) : eta{eta}, MDF{std::make_shared(roughness)} {} + CookTorranceDielectric(color albedo, float eta, float roughness) : albedo{albedo}, eta{(eta == 0.0f) ? 0.0f : fmax(eta, 1.0001)}, MDF{std::make_shared(roughness)} {} BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const override { // Vectors wo and wi are the outgoing and incident directions respectively. @@ -380,6 +379,7 @@ class CookTorranceDielectric : public material { } //private: + color albedo; float eta; std::shared_ptr MDF; @@ -415,7 +415,7 @@ class CookTorranceDielectric : public material { float D = MDF->D(NoH); float G = MDF->G(NoV, NoL); - color R_col = color(R, R, R); + color R_col = color(R, R, R) * albedo; color num = D * G * R_col; float denom = (4.0 * fmax(NoV, 0.001) * fmax(NoL, 0.001)); @@ -472,7 +472,7 @@ class CookTorranceDielectric : public material { float NoI = dot(wn, wi); float D = MDF->D(NoM); float G = MDF->G(fabs(NoO), fabs(NoI)); - color T_col = color(T, T, T); + color T_col = color(T, T, T) * albedo; color num = D * G * T_col; float IoM = dot(wi, wm); From 752419d8c4d835193e0a64bac23a7324ab3d65b1 Mon Sep 17 00:00:00 2001 From: connortbot Date: Sun, 7 Apr 2024 17:17:32 -0400 Subject: [PATCH 031/142] CA-95 remove render warning --- material.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/material.h b/material.h index 639246d..4538b2b 100644 --- a/material.h +++ b/material.h @@ -337,7 +337,7 @@ class CookTorrance : public material { class CookTorranceDielectric : public material { public: - CookTorranceDielectric(color albedo, float eta, float roughness) : albedo{albedo}, eta{(eta == 0.0f) ? 0.0f : fmax(eta, 1.0001)}, MDF{std::make_shared(roughness)} {} + CookTorranceDielectric(color albedo, float eta, float roughness) : albedo{albedo}, eta{(eta == 0.0f) ? 0.0f : (float)fmax(eta, 1.0001f)}, MDF{std::make_shared(roughness)} {} BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const override { // Vectors wo and wi are the outgoing and incident directions respectively. From e88d65e82d857847805be45361225660714105bc Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 9 Apr 2024 11:21:13 -0400 Subject: [PATCH 032/142] Update main.cc --- main.cc | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/main.cc b/main.cc index 349bc16..606a2d0 100644 --- a/main.cc +++ b/main.cc @@ -129,20 +129,16 @@ color colorize_ray(const ray& r, std::shared_ptr scene, int depth) { return color_from_emission; } - double scattering_pdf = mat_ptr->scattering_pdf(r, record, scattered); - double pdf = scattering_pdf; - - color color_from_scatter = (attenuation * scattering_pdf * colorize_ray(scattered, scene, depth-1)) / pdf; + color color_from_scatter = (attenuation * colorize_ray(scattered, scene, depth-1)); return color_from_emission + color_from_scatter; } - return color(0,0,0); // Sky background (gradient blue-white) - // vec3 unit_direction = r.direction().unit_vector(); - // auto t = 0.5*(unit_direction.y() + 1.0); + vec3 unit_direction = r.direction().unit_vector(); + auto t = 0.5*(unit_direction.y() + 1.0); - // return (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0); // lerp formula (1.0-t)*start + t*endval + return (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0); // lerp formula (1.0-t)*start + t*endval } struct RenderData { From 1f1409b82bd90418e93c5064bfe519f15ba2e91c Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 9 Apr 2024 11:21:42 -0400 Subject: [PATCH 033/142] Update main.cc --- main.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.cc b/main.cc index 606a2d0..db7fa8c 100644 --- a/main.cc +++ b/main.cc @@ -127,9 +127,9 @@ color colorize_ray(const ray& r, std::shared_ptr scene, int depth) { color color_from_emission = mat_ptr->emitted(record.u, record.v, record.pos); if (!mat_ptr->scatter(r, record, attenuation, scattered)) { return color_from_emission; - } + } - color color_from_scatter = (attenuation * colorize_ray(scattered, scene, depth-1)); + color color_from_scatter = attenuation * colorize_ray(scattered, scene, depth-1); return color_from_emission + color_from_scatter; } From 86f0803cd7ee648df33c0e7b7d043f9db67e0ec5 Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 9 Apr 2024 11:27:09 -0400 Subject: [PATCH 034/142] CA-89 uncaught wrong scatter signature in emissive --- light.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/light.h b/light.h index 68efa72..f2ee644 100644 --- a/light.h +++ b/light.h @@ -10,7 +10,7 @@ class emissive : public material { public: emissive(shared_ptr a) : emit(a) {} emissive(color emission_color) : emit(make_shared(emission_color)) {} - bool scatter(const ray& r_in, const HitInfo& rec, color& attenuation, ray& scattered) const override { + bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { return false; } color emitted(double u, double v, const point3& p) const override { From 9114f8cc2ef74eb97a2491d57b66cad9df596014 Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 9 Apr 2024 11:37:04 -0400 Subject: [PATCH 035/142] CA-89 update switch codes --- main.cc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/main.cc b/main.cc index db7fa8c..8fa30db 100644 --- a/main.cc +++ b/main.cc @@ -954,16 +954,16 @@ void brdf_tests() { int main(int argc, char* argv[]) { Config config = parseArguments(argc, argv); - switch (9) { - case 1: random_spheres(); break; - case 2: two_spheres(); break; - case 3: earth(); break; - case 4: quads(); break; - case 5: load_example(config); break; - case 6: simple_light(); break; - case 7: cornell_box(); break; - case 8: two_perlin_spheres(); break; - case 9: brdf_tests(); break; + switch (5) { + case 30: random_spheres(); break; + case 48: two_spheres(); break; + case 48.1: earth(); break; + case 50: quads(); break; + case 80: load_example(config); break; + case 51: simple_light(); break; + case 51.1: cornell_box(); break; + case 49: two_perlin_spheres(); break; + case 89: brdf_tests(); break; } } From 8101bf84224418af046b19a3e0b757c919eb5d37 Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 9 Apr 2024 11:55:45 -0400 Subject: [PATCH 036/142] CA-89 fix switch codes --- main.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main.cc b/main.cc index 8fa30db..7ba6ac2 100644 --- a/main.cc +++ b/main.cc @@ -954,14 +954,14 @@ void brdf_tests() { int main(int argc, char* argv[]) { Config config = parseArguments(argc, argv); - switch (5) { + switch (80) { case 30: random_spheres(); break; case 48: two_spheres(); break; - case 48.1: earth(); break; + case 481: earth(); break; case 50: quads(); break; case 80: load_example(config); break; case 51: simple_light(); break; - case 51.1: cornell_box(); break; + case 511: cornell_box(); break; case 49: two_perlin_spheres(); break; case 89: brdf_tests(); break; } From 32bac35700c9cb0742f043607ec083a977d58d38 Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 9 Apr 2024 14:31:05 -0400 Subject: [PATCH 037/142] CA-89 change default render func --- include/render.h | 2 +- main.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/render.h b/include/render.h index cd2a709..5ea9e0e 100644 --- a/include/render.h +++ b/include/render.h @@ -175,7 +175,7 @@ void render_scanlines(int lines, int start_line, std::shared_ptr scene_pt auto u = (i + random_double()) / (image_width-1); auto v = (j + random_double()) / (image_height-1); ray r = cam.get_ray(u, v); - pixel_color += colorize_ray(r, scene_ptr, max_depth); + pixel_color += trace_ray(r, scene_ptr, max_depth); } int buffer_index = j * image_width + i; diff --git a/main.cc b/main.cc index ee646a9..8d0ba9a 100644 --- a/main.cc +++ b/main.cc @@ -622,7 +622,7 @@ void brdf_tests() { int main(int argc, char* argv[]) { Config config = parseArguments(argc, argv); - switch (80) { + switch (89) { case 30: random_spheres(); break; case 48: two_spheres(); break; case 481: earth(); break; From db9c0d6d15872b25a78ad38c1b009d7518cad8c8 Mon Sep 17 00:00:00 2001 From: connortbot Date: Thu, 18 Apr 2024 11:35:21 -0400 Subject: [PATCH 038/142] CA-89 merge errors missing end } --- include/CSRParser.h | 1 + scene.cc | 1 + 2 files changed, 2 insertions(+) diff --git a/include/CSRParser.h b/include/CSRParser.h index 9572594..f2f22ff 100644 --- a/include/CSRParser.h +++ b/include/CSRParser.h @@ -132,6 +132,7 @@ class CSRParser { // see above warning in Sphere block if (materials[readStringProperty(material)]->emitted(0, 0, point3(0,0,0)).length() > 0) { scene_ptr->add_physical_light(quad); + } } else if (startsWith(line, "Instance")) { auto idStart = line.find('[') + 1; auto idEnd = line.find(']'); diff --git a/scene.cc b/scene.cc index be016ee..fec076b 100644 --- a/scene.cc +++ b/scene.cc @@ -20,6 +20,7 @@ unsigned int Scene::add_primitive(std::shared_ptr prim) { void Scene::add_physical_light(std::shared_ptr geom_ptr) { physical_lights.push_back(geom_ptr); +} unsigned int Scene::add_primitive_instance(std::shared_ptr pi_ptr, RTCDevice device) { RTCGeometry instance_geom = rtcNewGeometry(device, RTC_GEOMETRY_TYPE_INSTANCE); From ed7470adf85581a9bab7063ad91e7ca8f9727e1c Mon Sep 17 00:00:00 2001 From: connortbot Date: Thu, 18 Apr 2024 11:42:08 -0400 Subject: [PATCH 039/142] CA-89 updated trace_ray function supporting instancing IDs --- include/render.h | 122 +++++++++++++++++++++++++---------------------- 1 file changed, 66 insertions(+), 56 deletions(-) diff --git a/include/render.h b/include/render.h index 3e4a5d2..2d43357 100644 --- a/include/render.h +++ b/include/render.h @@ -21,62 +21,11 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { setupRayHit1(rayhit, r_in); rtcIntersect1(scene->rtc_scene, &rayhit); - if (rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID) { - std::shared_ptr geomhit = scene->geom_map[rayhit.hit.geomID]; - std::shared_ptr mat_ptr = geomhit->materialById(rayhit.hit.geomID); - record = geomhit->getHitInfo(r_in, r_in.at(rayhit.ray.tfar), rayhit.ray.tfar, rayhit.hit.geomID); - - color color_from_emission = mat_ptr->emitted(record.u, record.v, record.pos); - accumulated_color += weight * color_from_emission; - - BSDFSample sample_data = mat_ptr->sample(r_in, record, scattered); - if (!sample_data.scatter) { - return accumulated_color; - } - double cos_theta = fabs(dot(record.normal, (sample_data.scatter_direction))); - weight = weight * (sample_data.bsdf_value * cos_theta / sample_data.pdf_value); - - bool direct = false; - if (i == 0 && direct) { - // Direct Light Sampling - for (auto& light_ptr : scene->physical_lights) { // only accounts for physical lights currently - point3 sampled_point = light_ptr->sample(record); - vec3 light_dir = (sampled_point - record.pos); - ray sampled_ray = ray(record.pos, light_dir, 0.0); - - // Trace a ray from here to the light - struct RTCRayHit light_rayhit; - setupRayHit1(light_rayhit, sampled_ray); - - rtcIntersect1(scene->rtc_scene, &light_rayhit); - if (light_rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID) { // we hit something - std::shared_ptr light_geomhit = scene->geom_map[light_rayhit.hit.geomID]; - if (light_geomhit == light_ptr) { // if it is the light, we are not obscured from the light - // Store hit data of tracing the ray from here to the light - HitInfo light_record; - light_record = light_geomhit->getHitInfo(sampled_ray, sampled_ray.at(light_rayhit.ray.tfar), light_rayhit.ray.tfar, light_rayhit.hit.geomID); - - // Get the light's material - std::shared_ptr light_mat_ptr = light_geomhit->materialById(light_rayhit.hit.geomID); - - ray inverse = ray(sampled_point, -light_dir, light_rayhit.ray.tfar); - ray light_scatter; - mat_ptr->scatter(inverse, record, attenuation, light_scatter); - - // Calculate direct light contribution - color light_color = light_mat_ptr->emitted(light_record.u, light_record.v, light_record.pos); - // BRDF of light as the incoming source - color light_brdf = mat_ptr->generate(inverse, light_scatter, record); - // Calculate cos using the surface normal of the hit object and the direction from the object to the light. - double light_cos_theta = fmax(0.0, dot(record.normal, light_dir.unit_vector())); - // Evaluate the PDF considering the probability of sampling the point on the light source from the objects POV. - double light_pdf_value = light_ptr->pdf(light_record, sampled_ray); - color direct_contribution = (light_brdf * light_color * light_cos_theta) / light_pdf_value; - accumulated_color += weight * direct_contribution; - } - } - } - } + int targetID; + if (rayhit.hit.instID[0] != RTC_INVALID_GEOMETRY_ID) { + targetID = rayhit.hit.instID[0]; + } else if (rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID) { + targetID = rayhit.hit.geomID; } else { // Sky background (gradient blue-white) vec3 unit_direction = r_in.direction().unit_vector(); @@ -88,6 +37,67 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { return accumulated_color; } + std::shared_ptr geomhit = scene->geom_map[targetID]; + std::shared_ptr mat_ptr = geomhit->materialById(targetID); + record = geomhit->getHitInfo(r_in, r_in.at(rayhit.ray.tfar), rayhit.ray.tfar, targetID); + + color color_from_emission = mat_ptr->emitted(record.u, record.v, record.pos); + accumulated_color += weight * color_from_emission; + + BSDFSample sample_data = mat_ptr->sample(r_in, record, scattered); + if (!sample_data.scatter) { + return accumulated_color; + } + double cos_theta = fabs(dot(record.normal, (sample_data.scatter_direction))); + weight = weight * (sample_data.bsdf_value * cos_theta / sample_data.pdf_value); + + bool direct = false; + if (i == 0 && direct) { + // Direct Light Sampling + for (auto& light_ptr : scene->physical_lights) { // only accounts for physical lights currently + point3 sampled_point = light_ptr->sample(record); + vec3 light_dir = (sampled_point - record.pos); + ray sampled_ray = ray(record.pos, light_dir, 0.0); + + // Trace a ray from here to the light + struct RTCRayHit light_rayhit; + setupRayHit1(light_rayhit, sampled_ray); + + rtcIntersect1(scene->rtc_scene, &light_rayhit); + int light_targetID; + if (light_rayhit.hit.instID[0] != RTC_INVALID_GEOMETRY_ID) { + light_targetID = light_rayhit.hit.instID[0]; + } else if (light_rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID) { + light_targetID = light_rayhit.hit.geomID; + } + + std::shared_ptr light_geomhit = scene->geom_map[light_targetID]; + if (light_geomhit == light_ptr) { // if it is the light, we are not obscured from the light + // Store hit data of tracing the ray from here to the light + HitInfo light_record; + light_record = light_geomhit->getHitInfo(sampled_ray, sampled_ray.at(light_rayhit.ray.tfar), light_rayhit.ray.tfar, light_targetID); + + // Get the light's material + std::shared_ptr light_mat_ptr = light_geomhit->materialById(light_targetID); + + ray inverse = ray(sampled_point, -light_dir, light_rayhit.ray.tfar); + ray light_scatter; + mat_ptr->scatter(inverse, record, attenuation, light_scatter); + + // Calculate direct light contribution + color light_color = light_mat_ptr->emitted(light_record.u, light_record.v, light_record.pos); + // BRDF of light as the incoming source + color light_brdf = mat_ptr->generate(inverse, light_scatter, record); + // Calculate cos using the surface normal of the hit object and the direction from the object to the light. + double light_cos_theta = fmax(0.0, dot(record.normal, light_dir.unit_vector())); + // Evaluate the PDF considering the probability of sampling the point on the light source from the objects POV. + double light_pdf_value = light_ptr->pdf(light_record, sampled_ray); + color direct_contribution = (light_brdf * light_color * light_cos_theta) / light_pdf_value; + accumulated_color += weight * direct_contribution; + } + } + } + r_in = scattered; } From 988f2d62d9f1432db1ae9034ef6679846c4dd3f4 Mon Sep 17 00:00:00 2001 From: connortbot Date: Thu, 18 Apr 2024 14:56:52 -0400 Subject: [PATCH 040/142] CA-89 change default image format output --- include/CLIParser.h | 2 +- main.cc | 23 +++++++++++------------ 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/include/CLIParser.h b/include/CLIParser.h index 599604c..eec53e8 100644 --- a/include/CLIParser.h +++ b/include/CLIParser.h @@ -18,7 +18,7 @@ struct Config { std::string outputPath = "image.ppm"; int image_width = 1200; int image_height = 675; - std::string outputType = "ppm"; // [jpg|png|ppm] + std::string outputType = "png"; // [jpg|png|ppm] // Output flags bool showVersion = false; diff --git a/main.cc b/main.cc index 93e609f..cfcca10 100644 --- a/main.cc +++ b/main.cc @@ -54,18 +54,17 @@ void brdf_tests() { } int main(int argc, char* argv[]) { - brdf_tests(); - // Config config = parseArguments(argc, argv); + Config config = parseArguments(argc, argv); - // RenderData render_data; - // const auto aspect_ratio = static_cast(config.image_width) / config.image_height; - // setRenderData(render_data, aspect_ratio, config.image_width, config.samples_per_pixel, config.max_depth); - // std::string filePath = config.inputFile; - // RTCDevice device = initializeDevice(); - // CSRParser parser; - // auto scene_ptr = parser.parseCSR(filePath, device); - // scene_ptr->commitScene(); - // rtcReleaseDevice(device); + RenderData render_data; + const auto aspect_ratio = static_cast(config.image_width) / config.image_height; + setRenderData(render_data, aspect_ratio, config.image_width, config.samples_per_pixel, config.max_depth); + std::string filePath = config.inputFile; + RTCDevice device = initializeDevice(); + CSRParser parser; + auto scene_ptr = parser.parseCSR(filePath, device); + scene_ptr->commitScene(); + rtcReleaseDevice(device); - // output(render_data, scene_ptr->cam, scene_ptr, config); + output(render_data, scene_ptr->cam, scene_ptr, config); } From 7e5fd0895a145a17a7892fd65cc58e922535affa Mon Sep 17 00:00:00 2001 From: connortbot Date: Thu, 18 Apr 2024 15:11:25 -0400 Subject: [PATCH 041/142] CA-89 divide into src and includes --- CMakeLists.txt | 2 ++ camera.h => include/camera.h | 0 color.h => include/color.h | 0 device.h => include/device.h | 0 general.h => include/general.h | 0 geometry.h => include/geometry.h | 0 instances.h => include/instances.h | 0 intersects.h => include/intersects.h | 0 interval.h => include/interval.h | 0 light.h => include/light.h | 0 material.h => include/material.h | 0 perlin.h => include/perlin.h | 0 png_output.h => include/png_output.h | 0 primitive.h => include/primitive.h | 0 quad_primitive.h => include/quad_primitive.h | 0 ray.h => include/ray.h | 0 rtw_stb_image.h => include/rtw_stb_image.h | 0 sphere_primitive.h => include/sphere_primitive.h | 0 texture.h => include/texture.h | 0 vec3.h => include/vec3.h | 0 visual.h => include/visual.h | 0 quad_primitive.cc => src/quad_primitive.cc | 0 rtw_stb_image.cc => src/rtw_stb_image.cc | 0 scene.cc => src/scene.cc | 0 vec3.cc => src/vec3.cc | 0 25 files changed, 2 insertions(+) rename camera.h => include/camera.h (100%) rename color.h => include/color.h (100%) rename device.h => include/device.h (100%) rename general.h => include/general.h (100%) rename geometry.h => include/geometry.h (100%) rename instances.h => include/instances.h (100%) rename intersects.h => include/intersects.h (100%) rename interval.h => include/interval.h (100%) rename light.h => include/light.h (100%) rename material.h => include/material.h (100%) rename perlin.h => include/perlin.h (100%) rename png_output.h => include/png_output.h (100%) rename primitive.h => include/primitive.h (100%) rename quad_primitive.h => include/quad_primitive.h (100%) rename ray.h => include/ray.h (100%) rename rtw_stb_image.h => include/rtw_stb_image.h (100%) rename sphere_primitive.h => include/sphere_primitive.h (100%) rename texture.h => include/texture.h (100%) rename vec3.h => include/vec3.h (100%) rename visual.h => include/visual.h (100%) rename quad_primitive.cc => src/quad_primitive.cc (100%) rename rtw_stb_image.cc => src/rtw_stb_image.cc (100%) rename scene.cc => src/scene.cc (100%) rename vec3.cc => src/vec3.cc (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 46ccb1d..4609430 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,8 @@ FIND_PACKAGE(embree 4 REQUIRED) find_package(PNG REQUIRED) file(GLOB_RECURSE SOURCES "*.cc") +file(GLOB_RECURSE SRC_FOLDER "src/*.cc") +list(APPEND SOURCES ${SRC_FOLDER}) add_executable(caitlyn ${SOURCES}) # Required # EMBREE CONFIG diff --git a/camera.h b/include/camera.h similarity index 100% rename from camera.h rename to include/camera.h diff --git a/color.h b/include/color.h similarity index 100% rename from color.h rename to include/color.h diff --git a/device.h b/include/device.h similarity index 100% rename from device.h rename to include/device.h diff --git a/general.h b/include/general.h similarity index 100% rename from general.h rename to include/general.h diff --git a/geometry.h b/include/geometry.h similarity index 100% rename from geometry.h rename to include/geometry.h diff --git a/instances.h b/include/instances.h similarity index 100% rename from instances.h rename to include/instances.h diff --git a/intersects.h b/include/intersects.h similarity index 100% rename from intersects.h rename to include/intersects.h diff --git a/interval.h b/include/interval.h similarity index 100% rename from interval.h rename to include/interval.h diff --git a/light.h b/include/light.h similarity index 100% rename from light.h rename to include/light.h diff --git a/material.h b/include/material.h similarity index 100% rename from material.h rename to include/material.h diff --git a/perlin.h b/include/perlin.h similarity index 100% rename from perlin.h rename to include/perlin.h diff --git a/png_output.h b/include/png_output.h similarity index 100% rename from png_output.h rename to include/png_output.h diff --git a/primitive.h b/include/primitive.h similarity index 100% rename from primitive.h rename to include/primitive.h diff --git a/quad_primitive.h b/include/quad_primitive.h similarity index 100% rename from quad_primitive.h rename to include/quad_primitive.h diff --git a/ray.h b/include/ray.h similarity index 100% rename from ray.h rename to include/ray.h diff --git a/rtw_stb_image.h b/include/rtw_stb_image.h similarity index 100% rename from rtw_stb_image.h rename to include/rtw_stb_image.h diff --git a/sphere_primitive.h b/include/sphere_primitive.h similarity index 100% rename from sphere_primitive.h rename to include/sphere_primitive.h diff --git a/texture.h b/include/texture.h similarity index 100% rename from texture.h rename to include/texture.h diff --git a/vec3.h b/include/vec3.h similarity index 100% rename from vec3.h rename to include/vec3.h diff --git a/visual.h b/include/visual.h similarity index 100% rename from visual.h rename to include/visual.h diff --git a/quad_primitive.cc b/src/quad_primitive.cc similarity index 100% rename from quad_primitive.cc rename to src/quad_primitive.cc diff --git a/rtw_stb_image.cc b/src/rtw_stb_image.cc similarity index 100% rename from rtw_stb_image.cc rename to src/rtw_stb_image.cc diff --git a/scene.cc b/src/scene.cc similarity index 100% rename from scene.cc rename to src/scene.cc diff --git a/vec3.cc b/src/vec3.cc similarity index 100% rename from vec3.cc rename to src/vec3.cc From 4671c35f9763e0dc047c92edee812a0bc5d9e1a9 Mon Sep 17 00:00:00 2001 From: connortbot Date: Wed, 24 Apr 2024 14:37:36 -0400 Subject: [PATCH 042/142] CA-89 split sample and pdf into cc file --- include/objects/primitives/sphere_primitive.h | 4 +++ src/objects/primitives/sphere_primitive.cc | 29 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/include/objects/primitives/sphere_primitive.h b/include/objects/primitives/sphere_primitive.h index 4ad8448..2861e01 100644 --- a/include/objects/primitives/sphere_primitive.h +++ b/include/objects/primitives/sphere_primitive.h @@ -16,8 +16,12 @@ class SpherePrimitive : public Primitive { HitInfo getHitInfo(const ray& r, const vec3& p, const float t, unsigned int geomID) const override; + point3 sample(const HitInfo& rec) const override; + double pdf(const HitInfo& rec, ray sample_ray) const override; + private: static void get_sphere_uv(const point3& p, double& u, double& v); + static vec3 random_to_sphere(double radius, double distance_squared); }; #endif \ No newline at end of file diff --git a/src/objects/primitives/sphere_primitive.cc b/src/objects/primitives/sphere_primitive.cc index c6090c3..4a18b7f 100644 --- a/src/objects/primitives/sphere_primitive.cc +++ b/src/objects/primitives/sphere_primitive.cc @@ -33,6 +33,23 @@ HitInfo SpherePrimitive::getHitInfo(const ray& r, const vec3& p, const float t, return record; } +point3 SpherePrimitive::sample(const HitInfo& rec) const { + vec3 direction = position - rec.pos; + vec3 to_sphere = random_in_hemisphere(direction.unit_vector()); + return position + (to_sphere * radius); + // auto distance_squared = direction.length_squared(); + // onb uvw; + // uvw.build_from_w(direction); + // return uvw.local(random_to_sphere(radius, distance_squared)); +} + +double SpherePrimitive::pdf(const HitInfo& rec, ray sample_ray) const { + auto cos_theta_max = sqrt(1 - radius*radius/(position - sample_ray.origin()).length_squared()); + auto solid_angle = 2*pi*(1-cos_theta_max); + return 1 / solid_angle; + //return (4 * pi); +} + void SpherePrimitive::get_sphere_uv(const point3& p, double& u, double& v) { // p: a given point on the sphere of radius one, centered at the origin. // u: returned value [0,1] of angle around the Y axis from X=-1. @@ -46,4 +63,16 @@ void SpherePrimitive::get_sphere_uv(const point3& p, double& u, double& v) { u = phi / (2*pi); v = theta / pi; +} + +vec3 SpherePrimitive::random_to_sphere(double radius, double distance_squared) { + auto r1 = random_double(); + auto r2 = random_double(); + auto z = 1 + r2*(sqrt(1-radius*radius/distance_squared) - 1); + + auto phi = 2*pi*r1; + auto x = cos(phi)*sqrt(1-z*z); + auto y = sin(phi)*sqrt(1-z*z); + + return vec3(x, y, z); } \ No newline at end of file From 50cf507582299dc5b4d301195f46125a67d22b68 Mon Sep 17 00:00:00 2001 From: connortbot Date: Wed, 24 Apr 2024 17:31:39 -0400 Subject: [PATCH 043/142] CA-89 merge from 0.1.3 header splits --- include/objects/light.h | 2 +- main.cc | 2 +- src/objects/light.cc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/objects/light.h b/include/objects/light.h index fe0cbe3..f3227db 100644 --- a/include/objects/light.h +++ b/include/objects/light.h @@ -10,7 +10,7 @@ class emissive : public material { public: color emission_color; emissive(color emission_color); - bool scatter(const ray& r_in, const HitInfo& rec, color& attenuation, ray& scattered) const override; + bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const override; color emitted(double u, double v, const point3& p) const override; }; diff --git a/main.cc b/main.cc index cfcca10..b70625c 100644 --- a/main.cc +++ b/main.cc @@ -18,7 +18,7 @@ void brdf_tests() { double aperture = 0.0001; double dist_to_focus = 10.0; - Camera cam(lookfrom, lookat, vup, vfov, aspect_ratio, aperture, dist_to_focus, 0.0, 1.0); + Camera cam(lookfrom, lookat, vup, vfov, aspect_ratio, aperture, dist_to_focus); RTCDevice device = initializeDevice(); auto scene_ptr = make_shared(device, cam); diff --git a/src/objects/light.cc b/src/objects/light.cc index 84a0dda..35d2cd1 100644 --- a/src/objects/light.cc +++ b/src/objects/light.cc @@ -2,7 +2,7 @@ emissive::emissive(color emission_color) : emission_color{emission_color} {} -bool emissive::scatter(const ray& r_in, const HitInfo& rec, color& attenuation, ray& scattered) const { +bool emissive::scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { return false; } From d4adac7ff9731a590ba1ce3e5e7af0d912daa8cb Mon Sep 17 00:00:00 2001 From: connortbot Date: Mon, 29 Apr 2024 14:42:08 -0400 Subject: [PATCH 044/142] CA-89 updated BRDF hardcoded test function --- main.cc | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/main.cc b/main.cc index b70625c..deab41c 100644 --- a/main.cc +++ b/main.cc @@ -9,7 +9,7 @@ void brdf_tests() { RenderData render_data; const auto aspect_ratio = 16.0 / 9.0; - setRenderData(render_data, aspect_ratio, 1200, 50, 20); + setRenderData(render_data, aspect_ratio, 1200, 25, 20); point3 lookfrom(10, 3, 0); point3 lookat(0, 2, 0); @@ -23,24 +23,32 @@ void brdf_tests() { RTCDevice device = initializeDevice(); auto scene_ptr = make_shared(device, cam); - //auto mt = make_shared(color(0.7, 0.6, 0.77), 0.1); - color test = color(1.0, 1.0, 1.0); - auto mt = make_shared(test); - auto mt2 = make_shared(test, 0.0); - + // ALL MATERIALS + // DEFAULTS V0.1.X + auto emit = make_shared(color(15.0, 15.0, 15.0)); + auto mt1 = make_shared(color(0.7, 0.6, 0.77), 0.1); + auto mt2 = make_shared(color(1.0, 1.0, 1.0)); + + // Oren-Nayar + auto mt3 = make_shared(color(1.0, 1.0, 1.0), 0.0); + + // Cook-Torrance // Complex example: - auto mt3 = make_shared(color(1.0, 1.0, 1.0), color(1.0, 1.0, 1.0), 0.05); + auto mt4 = make_shared(color(1.0, 1.0, 1.0), color(1.0, 1.0, 1.0), 0.05); // Non-complex example: - auto mt4 = make_shared(color(1.0, 1.0, 1.0), 0.0); + auto mt5 = make_shared(color(1.0, 1.0, 1.0), 0.0); // Dielectric comparison - auto mt5 = make_shared(color(0.6, 1.0, 0.6), 1.5, 0.0001); // model glass - auto mt7 = make_shared(color(0.6, 1.0, 0.6), 0.0, 0.0001); // model mirror + auto mt6 = make_shared(color(1.0, 1.0, 1.0), 1.5, 0.0001); // model glass + auto mt7 = make_shared(color(1.0, 1.0, 1.0), 0.0, 0.0001); // model mirror auto sphere1 = make_shared(point3(0, 2, 2), mt5, 2, device); - auto sphere2 = make_shared(point3(0, 2, -2), mt7, 2, device); + auto sphere2 = make_shared(point3(1, 2, -2), emit, 0.5, device); + auto sphere3 = make_shared(point3(-4, 2, -1), mt6, 2, device); scene_ptr->add_primitive(sphere1); scene_ptr->add_primitive(sphere2); + scene_ptr->add_primitive(sphere3); + scene_ptr->add_physical_light(sphere2); auto red = make_shared(color(1.0, 0.2, 0.2)); auto ground = make_shared(point3(0,-10000,0), red, 10000, device); From 72f24398b447fdd2b4f5b050a1bcbc2941a75643 Mon Sep 17 00:00:00 2001 From: connortbot Date: Mon, 29 Apr 2024 16:15:24 -0400 Subject: [PATCH 045/142] CA-89 updatre --- main.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/main.cc b/main.cc index deab41c..1d4329b 100644 --- a/main.cc +++ b/main.cc @@ -26,7 +26,8 @@ void brdf_tests() { // ALL MATERIALS // DEFAULTS V0.1.X auto emit = make_shared(color(15.0, 15.0, 15.0)); - auto mt1 = make_shared(color(0.7, 0.6, 0.77), 0.1); + auto mt0 = make_shared(1.5); + auto mt1 = make_shared(color(1.0, 1.0, 1.0), 0.1); auto mt2 = make_shared(color(1.0, 1.0, 1.0)); // Oren-Nayar @@ -36,7 +37,7 @@ void brdf_tests() { // Complex example: auto mt4 = make_shared(color(1.0, 1.0, 1.0), color(1.0, 1.0, 1.0), 0.05); // Non-complex example: - auto mt5 = make_shared(color(1.0, 1.0, 1.0), 0.0); + auto mt5 = make_shared(color(1.0, 1.0, 1.0), 0.1); // Dielectric comparison auto mt6 = make_shared(color(1.0, 1.0, 1.0), 1.5, 0.0001); // model glass From 73d655c9022b7120d2547a8b10bf4121a841d616 Mon Sep 17 00:00:00 2001 From: connortbot Date: Mon, 29 Apr 2024 16:15:44 -0400 Subject: [PATCH 046/142] CA-89 add BSDF types to BSDFSample, update information in sample material functions --- include/materials/material.h | 70 +++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/include/materials/material.h b/include/materials/material.h index 4538b2b..82927a3 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -11,11 +11,22 @@ class hit_record; +// CONSTANTS +const float SPECULAR_ROUGHNESS_SAMPLING_CUTOFF = 0.1; + +enum BSDF_TYPE { + DIFFUSE, + GLOSSY, + SPECULAR, + TRANSMISSION +}; struct BSDFSample { + bool scatter; vec3 scatter_direction; color bsdf_value; float pdf_value; + BSDF_TYPE type = BSDF_TYPE::DIFFUSE; }; class material { @@ -101,6 +112,25 @@ class metal : public material { return 1.0; } + virtual BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const override { + BSDFSample sample_data; + // Sample the microfacet distribution to get the scatter direction. + color attenuation; // placeholder until it gets removed from the scatter function header + sample_data.scatter = scatter(r_in, rec, attenuation, scattered); + sample_data.scatter_direction = scattered.direction().unit_vector(); + + // Sample the BRDF for the value + sample_data.bsdf_value = generate(r_in, scattered, rec); + + // Find the PDF for the MDF + sample_data.pdf_value = pdf(r_in, scattered, rec); + + // Provide type for sample + if (fuzz < SPECULAR_ROUGHNESS_SAMPLING_CUTOFF) { sample_data.type = BSDF_TYPE::SPECULAR; } // 0.05 was picked arbitrarily, should experiment + else { sample_data.type = BSDF_TYPE::GLOSSY; } + return sample_data; + } + public: color albedo; @@ -141,6 +171,25 @@ class dielectric : public material { return 1.0; } + virtual BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const override { + BSDFSample sample_data; + // Sample the microfacet distribution to get the scatter direction. + color attenuation; // placeholder until it gets removed from the scatter function header + sample_data.scatter = scatter(r_in, rec, attenuation, scattered); + sample_data.scatter_direction = scattered.direction().unit_vector(); + + // Sample the BRDF for the value + sample_data.bsdf_value = generate(r_in, scattered, rec); + + // Find the PDF for the MDF + sample_data.pdf_value = pdf(r_in, scattered, rec); + + // Provide type for sample + sample_data.type = BSDF_TYPE::SPECULAR; + // is incorrect. only works because render function uses direct light sampling + bsdf sampling the same way for specular and transmission. + + return sample_data; + } public: @@ -298,6 +347,23 @@ class CookTorrance : public material { return D / jacobian; } + virtual BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const override { + BSDFSample sample_data; + // Sample the microfacet distribution to get the scatter direction. + color attenuation; // placeholder until it gets removed from the scatter function header + sample_data.scatter = scatter(r_in, rec, attenuation, scattered); + sample_data.scatter_direction = scattered.direction().unit_vector(); + + // Sample the BRDF for the value + sample_data.bsdf_value = generate(r_in, scattered, rec); + + // Find the PDF for the MDF + sample_data.pdf_value = pdf(r_in, scattered, rec); + if (MDF->roughness < SPECULAR_ROUGHNESS_SAMPLING_CUTOFF) { sample_data.type = BSDF_TYPE::SPECULAR; } // 0.05 was picked arbitrarily, should experiment + else { sample_data.type = BSDF_TYPE::GLOSSY; } + return sample_data; + } + private: bool complex; color albedo; @@ -364,7 +430,8 @@ class CookTorranceDielectric : public material { sample_data.bsdf_value = f_r(r_in, rec, scattered, R); sample_data.pdf_value = pdf_r(r_in, rec, scattered, R); - + if (MDF->roughness < SPECULAR_ROUGHNESS_SAMPLING_CUTOFF) { sample_data.type = BSDF_TYPE::SPECULAR; } // 0.05 was picked arbitrarily, should experiment + else { sample_data.type = BSDF_TYPE::GLOSSY; } } else { // transmission vec3 wi = refract(-wo, wm, refraction_ratio); scattered = ray(rec.pos, wi, r_in.time()); @@ -373,6 +440,7 @@ class CookTorranceDielectric : public material { sample_data.bsdf_value = f_t(r_in, rec, scattered, T); sample_data.pdf_value = pdf_t(r_in, rec, scattered, T); + sample_data.type = BSDF_TYPE::TRANSMISSION; } return sample_data; From 0062f58bc5c81388ff06827b8739aa6f65efee84 Mon Sep 17 00:00:00 2001 From: connortbot Date: Mon, 29 Apr 2024 16:29:58 -0400 Subject: [PATCH 047/142] CA-89 power heuristic implementation for MIS --- include/util/sampling.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 include/util/sampling.h diff --git a/include/util/sampling.h b/include/util/sampling.h new file mode 100644 index 0000000..83de147 --- /dev/null +++ b/include/util/sampling.h @@ -0,0 +1,22 @@ +#ifndef SAMPLING_H +#define SAMPLING_H + +// ATTENTION: +// credit: OSL testrender © +// NOT OUR CODE, IN RELEASE, THIS MUST BE REPLACED WITH A PROPER CREDIT!!! + + +struct MIS { + // for the function below, enumerate the cases for: + // the sampled function being a weight or eval, + // the "other" function being a weight or eval + enum MISMode { WEIGHT_WEIGHT, WEIGHT_EVAL, EVAL_WEIGHT }; + + template static inline float power_heuristic(float pdf1, float pdf2) { + float num = pow(pdf1, mode); + float denom = pow(pdf1, mode) + pow(pdf2, mode); + return num / denom; + } +}; + +#endif \ No newline at end of file From f4fba34880ef722156ae47953e4bd1ac181f9ade Mon Sep 17 00:00:00 2001 From: connortbot Date: Mon, 29 Apr 2024 16:30:21 -0400 Subject: [PATCH 048/142] CA-89 working direct light sampling with transmission, glossy, diffuse, specular --- include/render.h | 81 +++++++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/include/render.h b/include/render.h index af5df5c..fa30ce1 100644 --- a/include/render.h +++ b/include/render.h @@ -6,6 +6,8 @@ #include "scene.h" #include "vec3.h" +#include "sampling.h" + color trace_ray(const ray& r, std::shared_ptr scene, int depth) { HitInfo record; @@ -13,6 +15,7 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { color accumulated_color = color(0,0,0); ray r_in = r; + BSDF_TYPE incoming_type = BSDF_TYPE::DIFFUSE; for (int i=0; i scene, int depth) { vec3 unit_direction = r_in.direction().unit_vector(); auto t = 0.5*(unit_direction.y() + 1.0); - //color sky = color(0,0,0); + // color sky = color(0,0,0); color sky = (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0); // lerp formula (1.0-t)*start + t*endval accumulated_color += weight * sky; return accumulated_color; @@ -41,27 +44,34 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { std::shared_ptr mat_ptr = geomhit->materialById(targetID); record = geomhit->getHitInfo(r_in, r_in.at(rayhit.ray.tfar), rayhit.ray.tfar, targetID); + // Enable of disable direct light sampling (debug only, should always be enabled) + bool direct = true; + + // Get emission contribution color color_from_emission = mat_ptr->emitted(record.u, record.v, record.pos); - accumulated_color += weight * color_from_emission; BSDFSample sample_data = mat_ptr->sample(r_in, record, scattered); - if (!sample_data.scatter) { - return accumulated_color; + if (sample_data.type == BSDF_TYPE::SPECULAR || sample_data.type == BSDF_TYPE::TRANSMISSION) { direct = false; } + if (incoming_type == BSDF_TYPE::SPECULAR || sample_data.type == BSDF_TYPE::TRANSMISSION) { + accumulated_color += weight * color_from_emission; + } else { + // To prevent double contribution of emission, only directly add if and only if: + // => we are directly hitting the light, i.e (i==0) + if (i == 0) { accumulated_color += weight * color_from_emission; } } - double cos_theta = fabs(dot(record.normal, (sample_data.scatter_direction))); - weight = weight * (sample_data.bsdf_value * cos_theta / sample_data.pdf_value); - bool direct = false; - if (i == 0 && direct) { - // Direct Light Sampling + // Direct Light Sampling + if (direct && color_from_emission.length() == 0.0) { + int N = (int)scene->physical_lights.size(); // amount of lights for (auto& light_ptr : scene->physical_lights) { // only accounts for physical lights currently + // Create ray from hit point to the light point3 sampled_point = light_ptr->sample(record); - vec3 light_dir = (sampled_point - record.pos); - ray sampled_ray = ray(record.pos, light_dir, 0.0); + vec3 light_dir = (sampled_point - record.pos).unit_vector(); // direction from hit point to the light + ray light_ray = ray(record.pos, light_dir, 0.0); // Trace a ray from here to the light struct RTCRayHit light_rayhit; - setupRayHit1(light_rayhit, sampled_ray); + setupRayHit1(light_rayhit, light_ray); rtcIntersect1(scene->rtc_scene, &light_rayhit); int light_targetID; @@ -75,30 +85,45 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { if (light_geomhit == light_ptr) { // if it is the light, we are not obscured from the light // Store hit data of tracing the ray from here to the light HitInfo light_record; - light_record = light_geomhit->getHitInfo(sampled_ray, sampled_ray.at(light_rayhit.ray.tfar), light_rayhit.ray.tfar, light_targetID); - + light_record = light_geomhit->getHitInfo(light_ray, light_ray.at(light_rayhit.ray.tfar), light_rayhit.ray.tfar, light_targetID); + // Get the light's material std::shared_ptr light_mat_ptr = light_geomhit->materialById(light_targetID); - ray inverse = ray(sampled_point, -light_dir, light_rayhit.ray.tfar); - ray light_scatter; - mat_ptr->scatter(inverse, record, attenuation, light_scatter); - - // Calculate direct light contribution - color light_color = light_mat_ptr->emitted(light_record.u, light_record.v, light_record.pos); - // BRDF of light as the incoming source - color light_brdf = mat_ptr->generate(inverse, light_scatter, record); - // Calculate cos using the surface normal of the hit object and the direction from the object to the light. - double light_cos_theta = fmax(0.0, dot(record.normal, light_dir.unit_vector())); - // Evaluate the PDF considering the probability of sampling the point on the light source from the objects POV. - double light_pdf_value = light_ptr->pdf(light_record, sampled_ray); - color direct_contribution = (light_brdf * light_color * light_cos_theta) / light_pdf_value; - accumulated_color += weight * direct_contribution; + // Sample BSDF of hit point with incoming light + ray light_scattered; + ray inverse_light_ray = ray(light_record.pos, -light_dir, 0.0); // ray from light to the hit point + + BSDFSample light_sample_data; + color att; + light_sample_data.scatter = mat_ptr->scatter(r_in, record, att, light_scattered); + light_sample_data.bsdf_value = mat_ptr->generate(r_in, light_ray, record); + light_sample_data.pdf_value = mat_ptr->pdf(r_in, light_ray, record); + + // Find pdf for the light hit point + double light_pdf_value = light_ptr->pdf(light_record, light_ray); + + // Find contribution of light using MIS power heuristic of light_pdf and sample pdf + double light_cos_theta = fabs(dot(record.normal, light_dir)); + color light_contribution = weight * light_sample_data.bsdf_value * light_cos_theta + * MIS::power_heuristic(light_pdf_value, light_sample_data.pdf_value) / light_pdf_value; + + // Get emission of light + color light_Le = light_mat_ptr->emitted(light_record.u, light_record.v, light_record.pos); + accumulated_color += light_contribution * light_Le / N; } } } + // Indirect ray contribution + if (!sample_data.scatter) { + return accumulated_color; + } + double cos_theta = fabs(dot(record.normal, (sample_data.scatter_direction))); + weight = weight * (sample_data.bsdf_value * cos_theta / sample_data.pdf_value); + r_in = scattered; + incoming_type = sample_data.type; } return accumulated_color; From 9abb04fc93e301ec1894133d660a3197f5040455 Mon Sep 17 00:00:00 2001 From: connortbot Date: Mon, 29 Apr 2024 17:04:22 -0400 Subject: [PATCH 049/142] CA-90 stratified samples in render_scanlines only --- include/render.h | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/include/render.h b/include/render.h index fa30ce1..909970c 100644 --- a/include/render.h +++ b/include/render.h @@ -205,18 +205,30 @@ void render_scanlines(int lines, int start_line, std::shared_ptr scene_pt int samples_per_pixel = data.samples_per_pixel; int max_depth = data.max_depth; + int sqrt_samples = int(sqrt(samples_per_pixel)); // Assume samples_per_pixel is a perfect square + for (int j=start_line; j>=start_line - (lines - 1); --j) { for (int i=0; i Date: Tue, 30 Apr 2024 11:13:29 -0400 Subject: [PATCH 050/142] CA-120 isotropic phase function as material --- include/materials/material.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/materials/material.h b/include/materials/material.h index 82927a3..ba2399c 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -551,4 +551,20 @@ class CookTorranceDielectric : public material { } }; +class isotropic : public material { + isotropic(const color& albedo) : tex(make_shared(albedo)) {} + isotropic(shared_ptr tex) : tex(tex) {} + + virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { + scattered = ray(rec.p, random_unit_vector(), r_in.time()); + return true; + } + virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { + return 1 / (4 * pi); + } + virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { + return 1 / (4 * pi); + }; +} + #endif From 0ff9334fa00004bbb9443b4d40b2537d09e6fbd2 Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 30 Apr 2024 12:24:41 -0400 Subject: [PATCH 051/142] CA-89 only apply direct light sampling to diffuse surfaces --- include/render.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/render.h b/include/render.h index 909970c..2133b87 100644 --- a/include/render.h +++ b/include/render.h @@ -51,8 +51,8 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { color color_from_emission = mat_ptr->emitted(record.u, record.v, record.pos); BSDFSample sample_data = mat_ptr->sample(r_in, record, scattered); - if (sample_data.type == BSDF_TYPE::SPECULAR || sample_data.type == BSDF_TYPE::TRANSMISSION) { direct = false; } - if (incoming_type == BSDF_TYPE::SPECULAR || sample_data.type == BSDF_TYPE::TRANSMISSION) { + if (sample_data.type != BSDF_TYPE::DIFFUSE) { direct = false; } + if (incoming_type != BSDF_TYPE::DIFFUSE || sample_data.type == BSDF_TYPE::TRANSMISSION) { accumulated_color += weight * color_from_emission; } else { // To prevent double contribution of emission, only directly add if and only if: @@ -92,11 +92,13 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { // Sample BSDF of hit point with incoming light ray light_scattered; - ray inverse_light_ray = ray(light_record.pos, -light_dir, 0.0); // ray from light to the hit point BSDFSample light_sample_data; color att; - light_sample_data.scatter = mat_ptr->scatter(r_in, record, att, light_scattered); + // We do NOT call the above line because it would sample a possibly different microfacet normal + // than what is already sampled previous to the Direct Light Sampling (for complex BSDFs that use microfacets) + // Both generate and pdf assume that, if a microfacet normal is needed, it is already defined. Thus, we use the previous + // and pass in the same HitInfo. light_sample_data.bsdf_value = mat_ptr->generate(r_in, light_ray, record); light_sample_data.pdf_value = mat_ptr->pdf(r_in, light_ray, record); From a48945ab21814a67836cce6801e19eda4adcad93 Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 30 Apr 2024 12:24:54 -0400 Subject: [PATCH 052/142] CA-89 individual scatter, generate, pdf functions for CookTorranceDielectric --- include/materials/material.h | 60 +++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/include/materials/material.h b/include/materials/material.h index 82927a3..25c3031 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -405,6 +405,58 @@ class CookTorranceDielectric : public material { public: CookTorranceDielectric(color albedo, float eta, float roughness) : albedo{albedo}, eta{(eta == 0.0f) ? 0.0f : (float)fmax(eta, 1.0001f)}, MDF{std::make_shared(roughness)} {} + virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { + vec3 wo = -r_in.direction().unit_vector(); + vec3 N = rec.normal; + vec3 wm = MDF->sample(N); // outward microfacet normal. + rec.microfacet_normal = wm; + + float cosTheta_i = dot(wo, wm); + float R = FrDielectric(cosTheta_i); + float T = 1 - R; + + float u = random_double(); + double refraction_ratio = rec.front_face ? (1.0/eta) : eta; + double sinTheta_i = sqrt(1.0 - cosTheta_i*cosTheta_i); + + if (u < (R / (R + T)) || refraction_ratio * sinTheta_i > 1.0) { // reflectance + vec3 wi = reflect(-wo, wm); + scattered = ray(rec.pos, wi, r_in.time()); + } else { + vec3 wi = refract(-wo, wm, refraction_ratio); + scattered = ray(rec.pos, wi, r_in.time()); + } + return true; + } + virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { // assumes wm has been defined in rec + vec3 wo = -r_in.direction().unit_vector(); + vec3 N = rec.normal; + vec3 wi = scattered.direction(); + vec3 wm = rec.microfacet_normal; + float cosTheta_i = dot(wo, wm); + float R = FrDielectric(cosTheta_i); + float T = 1 - R; + if (cosTheta_i > 0) { // reflectance + return f_r(r_in, rec, scattered, R); + } else { // refractance + return f_t(r_in, rec, scattered, T); + } + } + virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { // assumes wm has been defined in rec + vec3 wo = -r_in.direction().unit_vector(); + vec3 N = rec.normal; + vec3 wi = scattered.direction(); + vec3 wm = rec.microfacet_normal; + float cosTheta_i = dot(wo, wm); + float R = FrDielectric(cosTheta_i); + float T = 1 - R; + if (cosTheta_i > 0) { // reflectance + return pdf_r(r_in, rec, scattered, R); + } else { // refractance + return pdf_t(r_in, rec, scattered, T); + } + }; + BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const override { // Vectors wo and wi are the outgoing and incident directions respectively. vec3 wo = -r_in.direction().unit_vector(); @@ -470,7 +522,7 @@ class CookTorranceDielectric : public material { return ((r_parallel * r_parallel) + (r_perp * r_perp)) / 2; } private: - color f_r(const ray& r_in, HitInfo& rec, ray& scattered, float R) const { + color f_r(const ray& r_in, const HitInfo& rec, const ray& scattered, float R) const { vec3 V = -r_in.direction().unit_vector(); vec3 L = scattered.direction().unit_vector(); vec3 H = (V + L).unit_vector(); @@ -491,7 +543,7 @@ class CookTorranceDielectric : public material { return num / denom; } - float pdf_r(const ray& r_in, HitInfo& rec, ray& scattered, float R) const { + float pdf_r(const ray& r_in, const HitInfo& rec, const ray& scattered, float R) const { vec3 V = -r_in.direction().unit_vector(); vec3 L = scattered.direction().unit_vector(); vec3 H = (V + L).unit_vector(); @@ -510,7 +562,7 @@ class CookTorranceDielectric : public material { return (D * R) / jacobian; } - float pdf_t(const ray& r_in, HitInfo& rec, ray& scattered, float T) const { + float pdf_t(const ray& r_in, const HitInfo& rec, const ray& scattered, float T) const { double etap = rec.front_face ? (1.0/eta) : eta; vec3 wo = -r_in.direction().unit_vector(); @@ -526,7 +578,7 @@ class CookTorranceDielectric : public material { return D * dwm_dwi * T; } - color f_t(const ray& r_in, HitInfo& rec, ray& scattered, float T) const { + color f_t(const ray& r_in, const HitInfo& rec, const ray& scattered, float T) const { double etap = rec.front_face ? (1.0/eta) : eta; vec3 wo = -r_in.direction().unit_vector(); From 4e8dbf1c01b9523684cc0b6385048c06c491dc4d Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 30 Apr 2024 12:37:26 -0400 Subject: [PATCH 053/142] CA-120 compil fixes isotropic --- include/materials/material.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/include/materials/material.h b/include/materials/material.h index 8904dcc..977c081 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -604,19 +604,20 @@ class CookTorranceDielectric : public material { }; class isotropic : public material { - isotropic(const color& albedo) : tex(make_shared(albedo)) {} - isotropic(shared_ptr tex) : tex(tex) {} + color albedo; + + isotropic(const color& albedo) : albedo{albedo} {} virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { - scattered = ray(rec.p, random_unit_vector(), r_in.time()); + scattered = ray(rec.pos, random_unit_vector(), r_in.time()); return true; } virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { - return 1 / (4 * pi); + return albedo / (4 * pi); } virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { return 1 / (4 * pi); }; -} +}; #endif From cac685669304e2c9e3a9dff922b83860fcc3a999 Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 3 May 2024 12:15:29 -0400 Subject: [PATCH 054/142] CA-120 new constructor to copy over a Geometry object that only retains the RTCGeometry object --- include/objects/geometry.h | 3 +++ src/objects/geometry.cc | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/include/objects/geometry.h b/include/objects/geometry.h index d142e91..398b63a 100644 --- a/include/objects/geometry.h +++ b/include/objects/geometry.h @@ -15,6 +15,9 @@ class Geometry : public Visual { RTCGeometry geom; Geometry(vec3 position, RTCGeometry geom); + // Copy constructor, only copies over RTCGeometry field + Geometry(shared_ptr geom); + /** @brief given a geomID (referring to ID given by scene attachment), find the material pointer. Usually called by renderer. */ virtual shared_ptr materialById(unsigned int geomID) const = 0; diff --git a/src/objects/geometry.cc b/src/objects/geometry.cc index 5db1d15..0429c03 100644 --- a/src/objects/geometry.cc +++ b/src/objects/geometry.cc @@ -1,3 +1,8 @@ #include "geometry.h" -Geometry::Geometry(vec3 position, RTCGeometry geom) : geom{geom}, Visual(position) {} \ No newline at end of file +Geometry::Geometry(vec3 position, RTCGeometry geom) : geom{geom}, Visual(position) {} + +Geometry::Geometry(shared_ptr geom_ptr) : Visual(geom_ptr->position) { + geom = geom_ptr->geom; + rtcRetainGeometry(geom); +} \ No newline at end of file From f4203892443dfa02d59fc0a42747875fbb0088b7 Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 3 May 2024 12:16:02 -0400 Subject: [PATCH 055/142] CA-120 track if the hit object is a medium or not, defaults to false --- include/intersection/hitinfo.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/intersection/hitinfo.h b/include/intersection/hitinfo.h index 1dcbf79..9aed43c 100644 --- a/include/intersection/hitinfo.h +++ b/include/intersection/hitinfo.h @@ -12,6 +12,8 @@ struct HitInfo { double u; double v; + bool medium = false; + /** @brief Given a face's outward normal and the initial ray, sets front_face to represent if collision hits it from the front or not. */ inline void set_face_normal(const ray& r, const vec3& outward_normal) { From 4034e22557f2fdc7e94a0c9a6140cfedc263b81c Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 3 May 2024 12:16:09 -0400 Subject: [PATCH 056/142] Update material.h --- include/materials/material.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/materials/material.h b/include/materials/material.h index 977c081..05dc7f6 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -604,6 +604,7 @@ class CookTorranceDielectric : public material { }; class isotropic : public material { + public: color albedo; isotropic(const color& albedo) : albedo{albedo} {} From 1f411fd89af4baead417ef11d2719eac9983f4e6 Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 3 May 2024 12:16:36 -0400 Subject: [PATCH 057/142] CA-120 basic base medium class that is a constant medium, and an interface for getting particleDistance --- include/materials/medium.h | 17 +++++++++++++++++ src/materials/medium.cc | 10 ++++++++++ 2 files changed, 27 insertions(+) create mode 100644 include/materials/medium.h create mode 100644 src/materials/medium.cc diff --git a/include/materials/medium.h b/include/materials/medium.h new file mode 100644 index 0000000..cfef08a --- /dev/null +++ b/include/materials/medium.h @@ -0,0 +1,17 @@ +#ifndef MEDIUM_H +#define MEDIUM_H + +#include "material.h" + +class Medium { + public: + Medium(float density, shared_ptr phase_function); + + virtual float particleDistance() const; + + float density; + shared_ptr phase; +}; + + +#endif \ No newline at end of file diff --git a/src/materials/medium.cc b/src/materials/medium.cc new file mode 100644 index 0000000..9baf2ca --- /dev/null +++ b/src/materials/medium.cc @@ -0,0 +1,10 @@ +#include "medium.h" + +Medium::Medium(float density, shared_ptr phase_function) + : density{density}, phase{phase_function} {} + +float Medium::particleDistance() const { + float neg_inv_density = -1 / density; + auto hit_distance = neg_inv_density * log(random_double()); + return hit_distance; +} \ No newline at end of file From 21b451f6f67e1c9aff628f7f4b277e9d571a9fe4 Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 3 May 2024 12:22:49 -0400 Subject: [PATCH 058/142] CA-120 volume object built from a medium and a geometry object to copy the geometry of --- include/objects/volume.h | 23 +++++++++++++++++++++++ src/objects/volume.cc | 13 +++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 include/objects/volume.h create mode 100644 src/objects/volume.cc diff --git a/include/objects/volume.h b/include/objects/volume.h new file mode 100644 index 0000000..91c4579 --- /dev/null +++ b/include/objects/volume.h @@ -0,0 +1,23 @@ +#ifndef VOLUME_H +#define VOLUME_H + +#include "geometry.h" +#include "medium.h" + +class Volume : public Geometry { + public: + // Releases geometry pointer of the geom ptr, and keeps for itself. Thus, the used geometry cannot be added itself. + Volume(shared_ptr medium, shared_ptr geom); + + virtual shared_ptr materialById(unsigned int geomID) const override; + + virtual HitInfo getHitInfo(const ray& r, const vec3& p, const float t, unsigned int geomID) const override; + + // sample and pdf are not implemented as volumes should not be sampled for direct light sampling + // however, reconsider with emissive volumes + + shared_ptr medium; + shared_ptr geom_ptr; +}; + +#endif \ No newline at end of file diff --git a/src/objects/volume.cc b/src/objects/volume.cc new file mode 100644 index 0000000..6604b0b --- /dev/null +++ b/src/objects/volume.cc @@ -0,0 +1,13 @@ +#include "volume.h" + +Volume::Volume(shared_ptr medium, shared_ptr geom_ptr) : medium{medium}, geom_ptr{geom_ptr}, Geometry(geom_ptr) {} + +shared_ptr Volume::materialById(unsigned int geomID) const { + return medium->phase; +} + +HitInfo Volume::getHitInfo(const ray& r, const vec3& p, const float t, unsigned int geomID) const { + HitInfo rec = geom_ptr->getHitInfo(r, p, t, geomID); + rec.medium = true; + return rec; +} \ No newline at end of file From 67bce6f5a2a40b2327be78695a7565e5f62b3a99 Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 3 May 2024 12:23:17 -0400 Subject: [PATCH 059/142] CA-120 add volume --- include/scene.h | 2 ++ src/scene.cc | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/include/scene.h b/include/scene.h index a2ee97b..0725676 100644 --- a/include/scene.h +++ b/include/scene.h @@ -9,6 +9,7 @@ #include "sphere_primitive.h" #include "instances.h" #include "hitinfo.h" +#include "volume.h" // SCENE INTERFACE // The scene class object covers all relevant objects in a scene: @@ -41,6 +42,7 @@ class Scene { void commitScene(); void releaseScene(); unsigned int add_primitive(std::shared_ptr prim); + unsigned int add_volume(std::shared_ptr vol); void add_physical_light(std::shared_ptr geom_ptr); unsigned int add_primitive_instance(std::shared_ptr pi_ptr, RTCDevice device); diff --git a/src/scene.cc b/src/scene.cc index fec076b..b62c9e5 100644 --- a/src/scene.cc +++ b/src/scene.cc @@ -18,6 +18,13 @@ unsigned int Scene::add_primitive(std::shared_ptr prim) { return primID; } +unsigned int Scene::add_volume(std::shared_ptr vol) { + unsigned int primID = rtcAttachGeometry(rtc_scene, vol->geom); + rtcReleaseGeometry(vol->geom); + geom_map[primID] = vol; + return primID; +} + void Scene::add_physical_light(std::shared_ptr geom_ptr) { physical_lights.push_back(geom_ptr); } From e80e33cccc25b654b824c920b0de6f0ee1a6faaa Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 3 May 2024 12:25:21 -0400 Subject: [PATCH 060/142] CA-120 updated render function to track mediums --- include/render.h | 48 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/include/render.h b/include/render.h index 2133b87..f315d94 100644 --- a/include/render.h +++ b/include/render.h @@ -8,6 +8,12 @@ #include "sampling.h" +/** + * @brief Most updated integrator for path tracing through scenes + * @note Is INACCURATE WHEN BEGINNING WITHIN VOLUMES. Tracing relies are intersection with + * volume boundary to know if it "enters" or not. If ray begins within the volume, we "enter" and never exit other than + * doubling back. +*/ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { HitInfo record; @@ -17,13 +23,35 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { ray r_in = r; BSDF_TYPE incoming_type = BSDF_TYPE::DIFFUSE; + shared_ptr current_volume = nullptr; + shared_ptr current_medium = nullptr; + for (int i=0; irtc_scene, &rayhit); + + // Check for volume intersections + if (current_medium) { + float hitDist = current_medium->particleDistance(); // sample dist + float istDist = rayhit.ray.tfar * r_in.direction().length(); + if (hitDist < istDist) { // volume intersection + // Update record + record.pos = r_in.at(hitDist / r_in.direction().length()); + std::shared_ptr mat_ptr = current_medium->phase; + BSDFSample sample_data = mat_ptr->sample(r_in, record, scattered); + weight = weight * (sample_data.bsdf_value / sample_data.pdf_value); + r_in = scattered; + incoming_type = BSDF_TYPE::TRANSMISSION; + continue; + } + } + int targetID; if (rayhit.hit.instID[0] != RTC_INVALID_GEOMETRY_ID) { targetID = rayhit.hit.instID[0]; @@ -42,10 +70,23 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { std::shared_ptr geomhit = scene->geom_map[targetID]; std::shared_ptr mat_ptr = geomhit->materialById(targetID); + record.medium = false; // reset to default, next line updates. hardcoded for now, getHitInfo implementations should already do this. record = geomhit->getHitInfo(r_in, r_in.at(rayhit.ray.tfar), rayhit.ray.tfar, targetID); - - // Enable of disable direct light sampling (debug only, should always be enabled) - bool direct = true; + if (record.medium) { + std::shared_ptr volhit = std::dynamic_pointer_cast(geomhit); + if (volhit) { + if (current_volume == volhit) { // exiting the volume + current_medium = nullptr; + current_volume = nullptr; + } else { + current_medium = volhit->medium; + current_volume = volhit; + } + r_in = ray(r_in.at(rayhit.ray.tfar), r_in.direction(), 0.0); + incoming_type = BSDF_TYPE::TRANSMISSION; + continue; // ignore edges of volumes? move to next bounce + } + } // Get emission contribution color color_from_emission = mat_ptr->emitted(record.u, record.v, record.pos); @@ -127,7 +168,6 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { r_in = scattered; incoming_type = sample_data.type; } - return accumulated_color; } From e46c6e84a1eb02f21a43265d41e37f134546dcc3 Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 3 May 2024 12:25:53 -0400 Subject: [PATCH 061/142] CA-120 updated test scene with volumes --- main.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/main.cc b/main.cc index 1d4329b..bdb2285 100644 --- a/main.cc +++ b/main.cc @@ -9,7 +9,7 @@ void brdf_tests() { RenderData render_data; const auto aspect_ratio = 16.0 / 9.0; - setRenderData(render_data, aspect_ratio, 1200, 25, 20); + setRenderData(render_data, aspect_ratio, 1200, 25, 200); point3 lookfrom(10, 3, 0); point3 lookat(0, 2, 0); @@ -46,11 +46,17 @@ void brdf_tests() { auto sphere1 = make_shared(point3(0, 2, 2), mt5, 2, device); auto sphere2 = make_shared(point3(1, 2, -2), emit, 0.5, device); auto sphere3 = make_shared(point3(-4, 2, -1), mt6, 2, device); - scene_ptr->add_primitive(sphere1); + // scene_ptr->add_primitive(sphere1); scene_ptr->add_primitive(sphere2); scene_ptr->add_primitive(sphere3); scene_ptr->add_physical_light(sphere2); + // Create volume out of sphere3 + auto iso = make_shared(color(1,1,1)); + auto medium = make_shared(1, iso); + auto volume1 = make_shared(medium, sphere1); + scene_ptr->add_volume(volume1); + auto red = make_shared(color(1.0, 0.2, 0.2)); auto ground = make_shared(point3(0,-10000,0), red, 10000, device); scene_ptr->add_primitive(ground); From 19ba34593a141a6c45ccdaa1146f4ee76c4cf98c Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 7 May 2024 22:24:09 -0400 Subject: [PATCH 062/142] CA-120 Intersect function that finds up to a given max intersections along a ray in a given scene --- include/intersection/intersects.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/include/intersection/intersects.h b/include/intersection/intersects.h index 33f5886..e1071ea 100644 --- a/include/intersection/intersects.h +++ b/include/intersection/intersects.h @@ -80,4 +80,30 @@ void setupRayHit16(struct RTCRayHit16& rayhit, std::vector& rays) { } } +/** + * @brief In a given scene, fires a continuous ray and fills information for multiple hits. +*/ +void MultiIntersect(int max, ray r_in, RTCScene& rtc_scene, std::vector& ids, std::vector& tfars) { + struct RTCRayHit rayhit; + ray r = r_in; + setupRayHit1(rayhit, r); + for (int i=0; i Date: Tue, 7 May 2024 22:25:31 -0400 Subject: [PATCH 063/142] CA-120 euler for transmittance calculation --- include/util/general.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/util/general.h b/include/util/general.h index f5ef460..19ac942 100644 --- a/include/util/general.h +++ b/include/util/general.h @@ -13,6 +13,7 @@ using std::make_shared; using std::sqrt; const double infinity = std::numeric_limits::infinity(); +const double euler = std::exp(1.0); const double pi = 3.1415926535897932385; inline double degrees_to_radians(double degrees) { return degrees * pi / 180.0; } From 5df4d47d60200d0c38f1d167cf1d920b393fa9e4 Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 7 May 2024 22:31:43 -0400 Subject: [PATCH 064/142] CA-120 basic transmittance for Mediums --- include/materials/medium.h | 9 +++++++++ src/materials/medium.cc | 12 ++++++++++++ 2 files changed, 21 insertions(+) diff --git a/include/materials/medium.h b/include/materials/medium.h index cfef08a..a7bab14 100644 --- a/include/materials/medium.h +++ b/include/materials/medium.h @@ -1,6 +1,7 @@ #ifndef MEDIUM_H #define MEDIUM_H +#include "general.h" #include "material.h" class Medium { @@ -9,6 +10,14 @@ class Medium { virtual float particleDistance() const; + /** + * @brief calculates transmittance coefficient between point x and y, assuming a constant medium. + * @note Assumes density as equivalent to extinction coefficient. + * May not be accurate! + */ + virtual float transmittance(const vec3& x, const vec3& y) const; + virtual float transmittance(float dist) const; + float density; shared_ptr phase; }; diff --git a/src/materials/medium.cc b/src/materials/medium.cc index 9baf2ca..049d424 100644 --- a/src/materials/medium.cc +++ b/src/materials/medium.cc @@ -7,4 +7,16 @@ float Medium::particleDistance() const { float neg_inv_density = -1 / density; auto hit_distance = neg_inv_density * log(random_double()); return hit_distance; +} + +float Medium::transmittance(const vec3& x, const vec3& y) const { + float exponent = -(density * fabs((x - y).length())); + return pow(euler, exponent); +} + +float Medium::transmittance(float dist) const { + float exponent = -(density * dist); + return pow(euler, exponent); +} + } \ No newline at end of file From 9e4b3d624c39a8948c9a9ec07cc1c55908d1ede5 Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 10 May 2024 12:28:42 -0400 Subject: [PATCH 065/142] Update csr-schema --- csr-schema | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csr-schema b/csr-schema index c9ddc73..c7feab0 160000 --- a/csr-schema +++ b/csr-schema @@ -1 +1 @@ -Subproject commit c9ddc7357f35001510b54d42c783a39916ddeaca +Subproject commit c7feab0264d734b60b1f72095bd6a2e26fa5f97d From d52d09258298e77c9e2fb8dcc8c8c2ed8bc51049 Mon Sep 17 00:00:00 2001 From: connortbot Date: Sun, 12 May 2024 13:37:48 -0400 Subject: [PATCH 066/142] CA-120 fix MultiIntersect to rely on original ray length but all rays should be normalized anyways --- include/intersection/intersects.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/intersection/intersects.h b/include/intersection/intersects.h index e1071ea..fbb4e1d 100644 --- a/include/intersection/intersects.h +++ b/include/intersection/intersects.h @@ -99,7 +99,7 @@ void MultiIntersect(int max, ray r_in, RTCScene& rtc_scene, std::vector& id } ids.push_back(targetID); // Calculate tfar for the original ray - float time = (r.at(rayhit.ray.tfar) - r_in.origin()).length() / (r.direction().length()); + float time = (r.at(rayhit.ray.tfar) - r_in.origin()).length() / (r_in.direction().length()); tfars.push_back(time); r = ray(r.at(rayhit.ray.tfar), r.direction(), 0.0); setupRayHit1(rayhit, r); From db8fa9ee0a5e29d5a7038b406db6ca10fa370963 Mon Sep 17 00:00:00 2001 From: connortbot Date: Sun, 12 May 2024 13:38:17 -0400 Subject: [PATCH 067/142] CA-120 MediumRecord class for maintaining residing mediums and transmittance in traced ray paths --- include/materials/medium.h | 47 ++++++++++++++++++++++++++++++++ src/materials/medium.cc | 55 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/include/materials/medium.h b/include/materials/medium.h index a7bab14..3f43fa9 100644 --- a/include/materials/medium.h +++ b/include/materials/medium.h @@ -1,6 +1,8 @@ #ifndef MEDIUM_H #define MEDIUM_H +#include +#include #include "general.h" #include "material.h" @@ -23,4 +25,49 @@ class Medium { }; +/** + * @class MediumRecord + * @brief Used for two purposes: tracking of mediums when ray tracing/marching and for calculating transmittance in DLS. +*/ +class MediumRecord { + public: + std::vector> mediums; + + // Transmittance calculation variables + float transmittance = 1.0; + point3 recent_position; + shared_ptr highest_density_volume = nullptr; + + + + MediumRecord(point3 initial_position); + + bool contains(std::shared_ptr vol) const; + + /** + * @brief Used to update the mediums list and update transmittance. + * Called whenever a new volume is hit in which we travel through the participating mediums. + * + * 1. Let D be the distance between new point and previous point. This is distance traveled. + * 2. Multiply transmittance by the transmittance applied by the highest_density_volume in D distance. + * 3A. If volume is in mediums, then we are exiting - remove from mediums. + * 3B. If volume is NOT in mediums, then we are entering. Check if vol->density > highest_density_volume->density. + */ + void hitVolume(shared_ptr vol, point3 hitPoint); + + /** + * @brief Only used for direct light sampling. Given + */ + + /** + * @brief Iterates through mediums and calls particleDistance to simulate the nearest particle of all + * participating mediums. + * @returns ptr to the particle's corresponding medium and updates passed in dist float. + */ + shared_ptr particleDistance(float& dist) const; + + +}; + + #endif \ No newline at end of file diff --git a/src/materials/medium.cc b/src/materials/medium.cc index 049d424..9970d1b 100644 --- a/src/materials/medium.cc +++ b/src/materials/medium.cc @@ -19,4 +19,59 @@ float Medium::transmittance(float dist) const { return pow(euler, exponent); } + +// MediumStack + +MediumRecord::MediumRecord(point3 initial_position) : recent_position{initial_position} {} + +bool MediumRecord::contains(std::shared_ptr vol) const { + return std::find(mediums.begin(), mediums.end(), vol) != mediums.end(); +} + +void MediumRecord::hitVolume(shared_ptr vol, point3 hitPoint) { + float distance_traveled = (recent_position - hitPoint).length(); + if (!mediums.empty()) { + transmittance *= highest_density_volume->transmittance(distance_traveled); + if (!vol) { return; } + if (!contains(vol)) { // entering + mediums.push_back(vol); + if (vol->density > highest_density_volume->density) { highest_density_volume = vol; } + } else { // exiting + mediums.erase(std::remove(mediums.begin(), mediums.end(), vol), mediums.end()); + + float highestDensity = -1.0f; + if (vol == highest_density_volume) { // reset highest density volume by finding it + for (const auto& ptr : mediums) { + if (ptr->density > highestDensity) { + highestDensity = ptr->density; + highest_density_volume = ptr; + } + } + } + } + } else { // mediums empty + if (!vol) { return; } + highest_density_volume = vol; + mediums.push_back(vol); + recent_position = hitPoint; + } +} + +shared_ptr MediumRecord::particleDistance(float& dist) const { + float min_dist; + shared_ptr p = nullptr; + for (size_t i = 0; i < mediums.size(); ++i) { + float ndist = mediums[i]->particleDistance(); + if (i == 0) { + min_dist = ndist; + p = mediums[i]; + } else { + if (ndist < min_dist) { + min_dist = ndist; + p = mediums[i]; + } + } + } + dist = min_dist; + return p; } \ No newline at end of file From 79df51ee6668783ea7875576d9a1a0d0c6644c5b Mon Sep 17 00:00:00 2001 From: connortbot Date: Sun, 12 May 2024 13:38:43 -0400 Subject: [PATCH 068/142] CA-120 make internal scene for Volumes for isolated checks if we are inside of them --- include/objects/volume.h | 3 ++- src/objects/volume.cc | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/objects/volume.h b/include/objects/volume.h index 91c4579..a541cb1 100644 --- a/include/objects/volume.h +++ b/include/objects/volume.h @@ -7,7 +7,7 @@ class Volume : public Geometry { public: // Releases geometry pointer of the geom ptr, and keeps for itself. Thus, the used geometry cannot be added itself. - Volume(shared_ptr medium, shared_ptr geom); + Volume(shared_ptr medium, shared_ptr geom, RTCDevice device); virtual shared_ptr materialById(unsigned int geomID) const override; @@ -16,6 +16,7 @@ class Volume : public Geometry { // sample and pdf are not implemented as volumes should not be sampled for direct light sampling // however, reconsider with emissive volumes + RTCScene volume_scene; // object scene only containing the volume shared_ptr medium; shared_ptr geom_ptr; }; diff --git a/src/objects/volume.cc b/src/objects/volume.cc index 6604b0b..98e9e10 100644 --- a/src/objects/volume.cc +++ b/src/objects/volume.cc @@ -1,6 +1,11 @@ #include "volume.h" -Volume::Volume(shared_ptr medium, shared_ptr geom_ptr) : medium{medium}, geom_ptr{geom_ptr}, Geometry(geom_ptr) {} +Volume::Volume(shared_ptr medium, shared_ptr geom_ptr, RTCDevice device) : medium{medium}, geom_ptr{geom_ptr}, Geometry(geom_ptr) { + volume_scene = rtcNewScene(device); + unsigned int geomID = rtcAttachGeometry(volume_scene, geom_ptr->geom); + rtcReleaseGeometry(geom_ptr->geom); + rtcCommitScene(volume_scene); +} shared_ptr Volume::materialById(unsigned int geomID) const { return medium->phase; From c2f49769b0aef22741900ea8831a5a34e4f48b3f Mon Sep 17 00:00:00 2001 From: connortbot Date: Sun, 12 May 2024 13:39:04 -0400 Subject: [PATCH 069/142] CA-120 vector of volumes to iterate through for initial volumes we are inside of --- include/scene.h | 4 ++++ src/scene.cc | 1 + 2 files changed, 5 insertions(+) diff --git a/include/scene.h b/include/scene.h index 0725676..3b81878 100644 --- a/include/scene.h +++ b/include/scene.h @@ -32,9 +32,13 @@ class Scene { std::map> geom_map; RTCScene rtc_scene; + // Lights std::vector> physical_lights; std::vector> lights; + // Relevant storage + std::vector> volumes; // used to check initial mediums + // Default Constructor // requires a device to initialize RTCScene Scene(RTCDevice device, Camera cam); diff --git a/src/scene.cc b/src/scene.cc index b62c9e5..b3191ee 100644 --- a/src/scene.cc +++ b/src/scene.cc @@ -19,6 +19,7 @@ unsigned int Scene::add_primitive(std::shared_ptr prim) { } unsigned int Scene::add_volume(std::shared_ptr vol) { + volumes.push_back(vol); unsigned int primID = rtcAttachGeometry(rtc_scene, vol->geom); rtcReleaseGeometry(vol->geom); geom_map[primID] = vol; From 2f368c2e5d6edd0418fa8d84894ed0b047d877c3 Mon Sep 17 00:00:00 2001 From: connortbot Date: Sun, 12 May 2024 13:39:46 -0400 Subject: [PATCH 070/142] CA-120 tested and updated trace ray function for volumes (ray marching, transmittance direct light sampling) --- include/render.h | 168 ++++++++++++++++++++++++++++++----------------- 1 file changed, 106 insertions(+), 62 deletions(-) diff --git a/include/render.h b/include/render.h index f315d94..f8e4602 100644 --- a/include/render.h +++ b/include/render.h @@ -23,12 +23,37 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { ray r_in = r; BSDF_TYPE incoming_type = BSDF_TYPE::DIFFUSE; - shared_ptr current_volume = nullptr; - shared_ptr current_medium = nullptr; + MediumRecord med_rec(r.origin()); + // Trace rays in all volume scenes to check which ones we reside in + struct RTCRayHit vol_rayhit; + HitInfo vol_record; + for (const auto& ptr : scene->volumes) { + setupRayHit1(vol_rayhit, r_in); + rtcIntersect1(ptr->volume_scene, &vol_rayhit); + int targetID; + if (vol_rayhit.hit.instID[0] != RTC_INVALID_GEOMETRY_ID) { + targetID = vol_rayhit.hit.instID[0]; + } else if (vol_rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID) { + targetID = vol_rayhit.hit.geomID; + } else { + continue; + } + vol_record = ptr->getHitInfo(r_in, r_in.at(vol_rayhit.ray.tfar), vol_rayhit.ray.tfar, targetID); + if (!vol_record.front_face) { // inside the volume! + record.medium = false; + record = vol_record; + if (record.medium) { + med_rec.hitVolume(ptr->medium, r_in.origin()); + incoming_type = BSDF_TYPE::SPECULAR; + } + } + } for (int i=0; i mat_ptr = nullptr; ray scattered; color attenuation; struct RTCRayHit rayhit; @@ -37,57 +62,50 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { rtcIntersect1(scene->rtc_scene, &rayhit); // Check for volume intersections - if (current_medium) { - float hitDist = current_medium->particleDistance(); // sample dist + float particleDist; + shared_ptr m_ptr = med_rec.particleDistance(particleDist); + if (m_ptr) { // we are in a medium float istDist = rayhit.ray.tfar * r_in.direction().length(); - if (hitDist < istDist) { // volume intersection + if (particleDist < istDist) { // volume intersection // Update record - record.pos = r_in.at(hitDist / r_in.direction().length()); - std::shared_ptr mat_ptr = current_medium->phase; - BSDFSample sample_data = mat_ptr->sample(r_in, record, scattered); - weight = weight * (sample_data.bsdf_value / sample_data.pdf_value); - r_in = scattered; - incoming_type = BSDF_TYPE::TRANSMISSION; - continue; + record.pos = r_in.at(particleDist / r_in.direction().length()); + raymarched = true; } } - int targetID; - if (rayhit.hit.instID[0] != RTC_INVALID_GEOMETRY_ID) { - targetID = rayhit.hit.instID[0]; - } else if (rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID) { - targetID = rayhit.hit.geomID; - } else { - // Sky background (gradient blue-white) - vec3 unit_direction = r_in.direction().unit_vector(); - auto t = 0.5*(unit_direction.y() + 1.0); - - // color sky = color(0,0,0); - color sky = (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0); // lerp formula (1.0-t)*start + t*endval - accumulated_color += weight * sky; - return accumulated_color; - } + if (!raymarched) { // process information of next geometry hit + int targetID; + if (rayhit.hit.instID[0] != RTC_INVALID_GEOMETRY_ID) { + targetID = rayhit.hit.instID[0]; + } else if (rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID) { + targetID = rayhit.hit.geomID; + } else { + // Sky background (gradient blue-white) + vec3 unit_direction = r_in.direction().unit_vector(); + auto t = 0.5*(unit_direction.y() + 1.0); + + // color sky = color(0,0,0); + color sky = (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0); // lerp formula (1.0-t)*start + t*endval + accumulated_color += weight * sky; + return accumulated_color; + } - std::shared_ptr geomhit = scene->geom_map[targetID]; - std::shared_ptr mat_ptr = geomhit->materialById(targetID); - record.medium = false; // reset to default, next line updates. hardcoded for now, getHitInfo implementations should already do this. - record = geomhit->getHitInfo(r_in, r_in.at(rayhit.ray.tfar), rayhit.ray.tfar, targetID); - if (record.medium) { - std::shared_ptr volhit = std::dynamic_pointer_cast(geomhit); - if (volhit) { - if (current_volume == volhit) { // exiting the volume - current_medium = nullptr; - current_volume = nullptr; - } else { - current_medium = volhit->medium; - current_volume = volhit; + std::shared_ptr geomhit = scene->geom_map[targetID]; + mat_ptr = geomhit->materialById(targetID); + record.medium = false; + record = geomhit->getHitInfo(r_in, r_in.at(rayhit.ray.tfar), rayhit.ray.tfar, targetID); + if (record.medium) { + std::shared_ptr volhit = std::dynamic_pointer_cast(geomhit); + if (volhit) { + med_rec.hitVolume(volhit->medium, r_in.at(rayhit.ray.tfar)); + r_in = ray(r_in.at(rayhit.ray.tfar), r_in.direction(), 0.0); + incoming_type = BSDF_TYPE::SPECULAR; + continue; // ignore edges of volumes? move to next bounce } - r_in = ray(r_in.at(rayhit.ray.tfar), r_in.direction(), 0.0); - incoming_type = BSDF_TYPE::TRANSMISSION; - continue; // ignore edges of volumes? move to next bounce } + } else { + mat_ptr = m_ptr->phase; } - // Get emission contribution color color_from_emission = mat_ptr->emitted(record.u, record.v, record.pos); @@ -110,26 +128,48 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { vec3 light_dir = (sampled_point - record.pos).unit_vector(); // direction from hit point to the light ray light_ray = ray(record.pos, light_dir, 0.0); - // Trace a ray from here to the light - struct RTCRayHit light_rayhit; - setupRayHit1(light_rayhit, light_ray); - - rtcIntersect1(scene->rtc_scene, &light_rayhit); - int light_targetID; - if (light_rayhit.hit.instID[0] != RTC_INVALID_GEOMETRY_ID) { - light_targetID = light_rayhit.hit.instID[0]; - } else if (light_rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID) { - light_targetID = light_rayhit.hit.geomID; + float distWithinMedium = (sampled_point - record.pos).length(); // distance to light + + // MultiIntersect to light to capture medium boundaries + MediumRecord light_med_rec(record.pos); + light_med_rec.mediums = med_rec.mediums; + light_med_rec.highest_density_volume = med_rec.highest_density_volume; + std::vector ids; + std::vector tfars; + // errors warning: overflow in conversion from 'float' to 'int' changes value from '+Inff' to '2147483647' [-Woverflow] + // MultiIntersect(std::numeric_limits::infinity(), light_ray, scene->rtc_scene, ids, tfars); + MultiIntersect(5, light_ray, scene->rtc_scene, ids, tfars); + + bool non_medium_encountered = false; + std::shared_ptr light_geomhit; + int light_id; + int light_tfar; + for (size_t j = 0; j < ids.size(); j++) { + int id = ids[j]; + float tfar = tfars[j]; + light_geomhit = scene->geom_map[id]; + std::shared_ptr possible_volume_hit = std::dynamic_pointer_cast(light_geomhit); + if (!possible_volume_hit) { // non volume encountered + if (light_geomhit == light_ptr) { // hit the light + light_id = id; + light_tfar = tfar; + light_med_rec.hitVolume(nullptr, light_ray.at(tfar)); + break; + } + non_medium_encountered = true; + break; + } else { // one of the intersections was a volume + light_med_rec.hitVolume(possible_volume_hit->medium, light_ray.at(tfar)); + } } - - std::shared_ptr light_geomhit = scene->geom_map[light_targetID]; - if (light_geomhit == light_ptr) { // if it is the light, we are not obscured from the light + + if (!non_medium_encountered) { // if it is the light, we are not obscured from the light // Store hit data of tracing the ray from here to the light HitInfo light_record; - light_record = light_geomhit->getHitInfo(light_ray, light_ray.at(light_rayhit.ray.tfar), light_rayhit.ray.tfar, light_targetID); + light_record = light_geomhit->getHitInfo(light_ray, light_ray.at(light_tfar), light_tfar, light_id); // Get the light's material - std::shared_ptr light_mat_ptr = light_geomhit->materialById(light_targetID); + std::shared_ptr light_mat_ptr = light_geomhit->materialById(light_id); // Sample BSDF of hit point with incoming light ray light_scattered; @@ -150,7 +190,8 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { double light_cos_theta = fabs(dot(record.normal, light_dir)); color light_contribution = weight * light_sample_data.bsdf_value * light_cos_theta * MIS::power_heuristic(light_pdf_value, light_sample_data.pdf_value) / light_pdf_value; - + float transmittance_coeff = light_med_rec.transmittance; + light_contribution *= transmittance_coeff; // Get emission of light color light_Le = light_mat_ptr->emitted(light_record.u, light_record.v, light_record.pos); accumulated_color += light_contribution * light_Le / N; @@ -163,8 +204,11 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { return accumulated_color; } double cos_theta = fabs(dot(record.normal, (sample_data.scatter_direction))); - weight = weight * (sample_data.bsdf_value * cos_theta / sample_data.pdf_value); - + if (!raymarched) { + weight = weight * (sample_data.bsdf_value * cos_theta / sample_data.pdf_value); + } else { + weight = weight * (sample_data.bsdf_value / sample_data.pdf_value); + } r_in = scattered; incoming_type = sample_data.type; } From 9f5bd022d9396eca5fa32b0ac050f53998f6f177 Mon Sep 17 00:00:00 2001 From: connortbot Date: Sun, 12 May 2024 13:41:28 -0400 Subject: [PATCH 071/142] Update main.cc --- main.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.cc b/main.cc index bdb2285..9e7a7b5 100644 --- a/main.cc +++ b/main.cc @@ -54,7 +54,7 @@ void brdf_tests() { // Create volume out of sphere3 auto iso = make_shared(color(1,1,1)); auto medium = make_shared(1, iso); - auto volume1 = make_shared(medium, sphere1); + auto volume1 = make_shared(medium, sphere1), device; scene_ptr->add_volume(volume1); auto red = make_shared(color(1.0, 0.2, 0.2)); From 9ecde81054861438a737f8f1a183eae9f8512180 Mon Sep 17 00:00:00 2001 From: connortbot Date: Sun, 12 May 2024 13:43:49 -0400 Subject: [PATCH 072/142] Update main.cc --- main.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.cc b/main.cc index 9e7a7b5..2243564 100644 --- a/main.cc +++ b/main.cc @@ -54,7 +54,7 @@ void brdf_tests() { // Create volume out of sphere3 auto iso = make_shared(color(1,1,1)); auto medium = make_shared(1, iso); - auto volume1 = make_shared(medium, sphere1), device; + auto volume1 = make_shared(medium, sphere1, device); scene_ptr->add_volume(volume1); auto red = make_shared(color(1.0, 0.2, 0.2)); From 6dd470663afb309c893a5da67dde3bd865dbedb0 Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 14 May 2024 01:36:38 -0400 Subject: [PATCH 073/142] Update render.cc --- src/render.cc | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/render.cc b/src/render.cc index 6d705b3..f70a804 100644 --- a/src/render.cc +++ b/src/render.cc @@ -62,17 +62,23 @@ void render_scanlines(int lines, int start_line, std::shared_ptr scene_pt int samples_per_pixel = data.samples_per_pixel; int max_depth = data.max_depth; + int sqrt_samples = int(sqrt(samples_per_pixel)); + + for (int j=start_line; j>=start_line - (lines - 1); --j) { for (int i=0; i Date: Tue, 14 May 2024 01:42:28 -0400 Subject: [PATCH 074/142] split headers --- include/intersection/intersects.h | 23 +--- include/render.h | 201 +---------------------------- src/intersection/intersects.cc | 23 ++++ src/render.cc | 203 +++++++++++++++++++++++++++++- 4 files changed, 227 insertions(+), 223 deletions(-) diff --git a/include/intersection/intersects.h b/include/intersection/intersects.h index 2bc3c81..b825577 100644 --- a/include/intersection/intersects.h +++ b/include/intersection/intersects.h @@ -22,27 +22,6 @@ void setupRayHit16(struct RTCRayHit16& rayhit, std::vector& rays); /** * @brief In a given scene, fires a continuous ray and fills information for multiple hits. */ -void MultiIntersect(int max, ray r_in, RTCScene& rtc_scene, std::vector& ids, std::vector& tfars) { - struct RTCRayHit rayhit; - ray r = r_in; - setupRayHit1(rayhit, r); - for (int i=0; i& ids, std::vector& tfars); #endif \ No newline at end of file diff --git a/include/render.h b/include/render.h index 5959d85..6cd02f8 100644 --- a/include/render.h +++ b/include/render.h @@ -14,206 +14,7 @@ * volume boundary to know if it "enters" or not. If ray begins within the volume, we "enter" and never exit other than * doubling back. */ -color trace_ray(const ray& r, std::shared_ptr scene, int depth) { - HitInfo record; - - color weight = color(1.0, 1.0, 1.0); - color accumulated_color = color(0,0,0); - - ray r_in = r; - BSDF_TYPE incoming_type = BSDF_TYPE::DIFFUSE; - - MediumRecord med_rec(r.origin()); - // Trace rays in all volume scenes to check which ones we reside in - struct RTCRayHit vol_rayhit; - HitInfo vol_record; - for (const auto& ptr : scene->volumes) { - setupRayHit1(vol_rayhit, r_in); - rtcIntersect1(ptr->volume_scene, &vol_rayhit); - int targetID; - if (vol_rayhit.hit.instID[0] != RTC_INVALID_GEOMETRY_ID) { - targetID = vol_rayhit.hit.instID[0]; - } else if (vol_rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID) { - targetID = vol_rayhit.hit.geomID; - } else { - continue; - } - vol_record = ptr->getHitInfo(r_in, r_in.at(vol_rayhit.ray.tfar), vol_rayhit.ray.tfar, targetID); - if (!vol_record.front_face) { // inside the volume! - record.medium = false; - record = vol_record; - if (record.medium) { - med_rec.hitVolume(ptr->medium, r_in.origin()); - incoming_type = BSDF_TYPE::SPECULAR; - } - } - } - - for (int i=0; i mat_ptr = nullptr; - ray scattered; - color attenuation; - struct RTCRayHit rayhit; - setupRayHit1(rayhit, r_in); - - rtcIntersect1(scene->rtc_scene, &rayhit); - - // Check for volume intersections - float particleDist; - shared_ptr m_ptr = med_rec.particleDistance(particleDist); - if (m_ptr) { // we are in a medium - float istDist = rayhit.ray.tfar * r_in.direction().length(); - if (particleDist < istDist) { // volume intersection - // Update record - record.pos = r_in.at(particleDist / r_in.direction().length()); - raymarched = true; - } - } - - if (!raymarched) { // process information of next geometry hit - int targetID; - if (rayhit.hit.instID[0] != RTC_INVALID_GEOMETRY_ID) { - targetID = rayhit.hit.instID[0]; - } else if (rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID) { - targetID = rayhit.hit.geomID; - } else { - // Sky background (gradient blue-white) - vec3 unit_direction = r_in.direction().unit_vector(); - auto t = 0.5*(unit_direction.y() + 1.0); - - // color sky = color(0,0,0); - color sky = (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0); // lerp formula (1.0-t)*start + t*endval - accumulated_color += weight * sky; - return accumulated_color; - } - - std::shared_ptr geomhit = scene->geom_map[targetID]; - mat_ptr = geomhit->materialById(targetID); - record.medium = false; - record = geomhit->getHitInfo(r_in, r_in.at(rayhit.ray.tfar), rayhit.ray.tfar, targetID); - if (record.medium) { - std::shared_ptr volhit = std::dynamic_pointer_cast(geomhit); - if (volhit) { - med_rec.hitVolume(volhit->medium, r_in.at(rayhit.ray.tfar)); - r_in = ray(r_in.at(rayhit.ray.tfar), r_in.direction(), 0.0); - incoming_type = BSDF_TYPE::SPECULAR; - continue; // ignore edges of volumes? move to next bounce - } - } - } else { - mat_ptr = m_ptr->phase; - } - // Get emission contribution - color color_from_emission = mat_ptr->emitted(record.u, record.v, record.pos); - - BSDFSample sample_data = mat_ptr->sample(r_in, record, scattered); - if (sample_data.type != BSDF_TYPE::DIFFUSE) { direct = false; } - if (incoming_type != BSDF_TYPE::DIFFUSE || sample_data.type == BSDF_TYPE::TRANSMISSION) { - accumulated_color += weight * color_from_emission; - } else { - // To prevent double contribution of emission, only directly add if and only if: - // => we are directly hitting the light, i.e (i==0) - if (i == 0) { accumulated_color += weight * color_from_emission; } - } - - // Direct Light Sampling - if (direct && color_from_emission.length() == 0.0) { - int N = (int)scene->physical_lights.size(); // amount of lights - for (auto& light_ptr : scene->physical_lights) { // only accounts for physical lights currently - // Create ray from hit point to the light - point3 sampled_point = light_ptr->sample(record); - vec3 light_dir = (sampled_point - record.pos).unit_vector(); // direction from hit point to the light - ray light_ray = ray(record.pos, light_dir, 0.0); - - float distWithinMedium = (sampled_point - record.pos).length(); // distance to light - - // MultiIntersect to light to capture medium boundaries - MediumRecord light_med_rec(record.pos); - light_med_rec.mediums = med_rec.mediums; - light_med_rec.highest_density_volume = med_rec.highest_density_volume; - std::vector ids; - std::vector tfars; - // errors warning: overflow in conversion from 'float' to 'int' changes value from '+Inff' to '2147483647' [-Woverflow] - // MultiIntersect(std::numeric_limits::infinity(), light_ray, scene->rtc_scene, ids, tfars); - MultiIntersect(5, light_ray, scene->rtc_scene, ids, tfars); - - bool non_medium_encountered = false; - std::shared_ptr light_geomhit; - int light_id; - int light_tfar; - for (size_t j = 0; j < ids.size(); j++) { - int id = ids[j]; - float tfar = tfars[j]; - light_geomhit = scene->geom_map[id]; - std::shared_ptr possible_volume_hit = std::dynamic_pointer_cast(light_geomhit); - if (!possible_volume_hit) { // non volume encountered - if (light_geomhit == light_ptr) { // hit the light - light_id = id; - light_tfar = tfar; - light_med_rec.hitVolume(nullptr, light_ray.at(tfar)); - break; - } - non_medium_encountered = true; - break; - } else { // one of the intersections was a volume - light_med_rec.hitVolume(possible_volume_hit->medium, light_ray.at(tfar)); - } - } - - if (!non_medium_encountered) { // if it is the light, we are not obscured from the light - // Store hit data of tracing the ray from here to the light - HitInfo light_record; - light_record = light_geomhit->getHitInfo(light_ray, light_ray.at(light_tfar), light_tfar, light_id); - - // Get the light's material - std::shared_ptr light_mat_ptr = light_geomhit->materialById(light_id); - - // Sample BSDF of hit point with incoming light - ray light_scattered; - - BSDFSample light_sample_data; - color att; - // We do NOT call the above line because it would sample a possibly different microfacet normal - // than what is already sampled previous to the Direct Light Sampling (for complex BSDFs that use microfacets) - // Both generate and pdf assume that, if a microfacet normal is needed, it is already defined. Thus, we use the previous - // and pass in the same HitInfo. - light_sample_data.bsdf_value = mat_ptr->generate(r_in, light_ray, record); - light_sample_data.pdf_value = mat_ptr->pdf(r_in, light_ray, record); - - // Find pdf for the light hit point - double light_pdf_value = light_ptr->pdf(light_record, light_ray); - - // Find contribution of light using MIS power heuristic of light_pdf and sample pdf - double light_cos_theta = fabs(dot(record.normal, light_dir)); - color light_contribution = weight * light_sample_data.bsdf_value * light_cos_theta - * MIS::power_heuristic(light_pdf_value, light_sample_data.pdf_value) / light_pdf_value; - float transmittance_coeff = light_med_rec.transmittance; - light_contribution *= transmittance_coeff; - // Get emission of light - color light_Le = light_mat_ptr->emitted(light_record.u, light_record.v, light_record.pos); - accumulated_color += light_contribution * light_Le / N; - } - } - } - - // Indirect ray contribution - if (!sample_data.scatter) { - return accumulated_color; - } - double cos_theta = fabs(dot(record.normal, (sample_data.scatter_direction))); - if (!raymarched) { - weight = weight * (sample_data.bsdf_value * cos_theta / sample_data.pdf_value); - } else { - weight = weight * (sample_data.bsdf_value / sample_data.pdf_value); - } - r_in = scattered; - incoming_type = sample_data.type; - } - return accumulated_color; -} +color trace_ray(const ray& r, std::shared_ptr scene, int depth); struct RenderData { int image_width; diff --git a/src/intersection/intersects.cc b/src/intersection/intersects.cc index 364f9eb..0dc3b6f 100644 --- a/src/intersection/intersects.cc +++ b/src/intersection/intersects.cc @@ -71,3 +71,26 @@ void setupRayHit16(struct RTCRayHit16& rayhit, std::vector& rays) { ix += 1; } } + +void MultiIntersect(int max, ray r_in, RTCScene& rtc_scene, std::vector& ids, std::vector& tfars) { + struct RTCRayHit rayhit; + ray r = r_in; + setupRayHit1(rayhit, r); + for (int i=0; i scene, int depth) { + HitInfo record; + + color weight = color(1.0, 1.0, 1.0); + color accumulated_color = color(0,0,0); + + ray r_in = r; + BSDF_TYPE incoming_type = BSDF_TYPE::DIFFUSE; + + MediumRecord med_rec(r.origin()); + // Trace rays in all volume scenes to check which ones we reside in + struct RTCRayHit vol_rayhit; + HitInfo vol_record; + for (const auto& ptr : scene->volumes) { + setupRayHit1(vol_rayhit, r_in); + rtcIntersect1(ptr->volume_scene, &vol_rayhit); + int targetID; + if (vol_rayhit.hit.instID[0] != RTC_INVALID_GEOMETRY_ID) { + targetID = vol_rayhit.hit.instID[0]; + } else if (vol_rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID) { + targetID = vol_rayhit.hit.geomID; + } else { + continue; + } + vol_record = ptr->getHitInfo(r_in, r_in.at(vol_rayhit.ray.tfar), vol_rayhit.ray.tfar, targetID); + if (!vol_record.front_face) { // inside the volume! + record.medium = false; + record = vol_record; + if (record.medium) { + med_rec.hitVolume(ptr->medium, r_in.origin()); + incoming_type = BSDF_TYPE::SPECULAR; + } + } + } + + for (int i=0; i mat_ptr = nullptr; + ray scattered; + color attenuation; + struct RTCRayHit rayhit; + setupRayHit1(rayhit, r_in); + + rtcIntersect1(scene->rtc_scene, &rayhit); + + // Check for volume intersections + float particleDist; + shared_ptr m_ptr = med_rec.particleDistance(particleDist); + if (m_ptr) { // we are in a medium + float istDist = rayhit.ray.tfar * r_in.direction().length(); + if (particleDist < istDist) { // volume intersection + // Update record + record.pos = r_in.at(particleDist / r_in.direction().length()); + raymarched = true; + } + } + + if (!raymarched) { // process information of next geometry hit + int targetID; + if (rayhit.hit.instID[0] != RTC_INVALID_GEOMETRY_ID) { + targetID = rayhit.hit.instID[0]; + } else if (rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID) { + targetID = rayhit.hit.geomID; + } else { + // Sky background (gradient blue-white) + vec3 unit_direction = r_in.direction().unit_vector(); + auto t = 0.5*(unit_direction.y() + 1.0); + + // color sky = color(0,0,0); + color sky = (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0); // lerp formula (1.0-t)*start + t*endval + accumulated_color += weight * sky; + return accumulated_color; + } + + std::shared_ptr geomhit = scene->geom_map[targetID]; + mat_ptr = geomhit->materialById(targetID); + record.medium = false; + record = geomhit->getHitInfo(r_in, r_in.at(rayhit.ray.tfar), rayhit.ray.tfar, targetID); + if (record.medium) { + std::shared_ptr volhit = std::dynamic_pointer_cast(geomhit); + if (volhit) { + med_rec.hitVolume(volhit->medium, r_in.at(rayhit.ray.tfar)); + r_in = ray(r_in.at(rayhit.ray.tfar), r_in.direction(), 0.0); + incoming_type = BSDF_TYPE::SPECULAR; + continue; // ignore edges of volumes? move to next bounce + } + } + } else { + mat_ptr = m_ptr->phase; + } + // Get emission contribution + color color_from_emission = mat_ptr->emitted(record.u, record.v, record.pos); + + BSDFSample sample_data = mat_ptr->sample(r_in, record, scattered); + if (sample_data.type != BSDF_TYPE::DIFFUSE) { direct = false; } + if (incoming_type != BSDF_TYPE::DIFFUSE || sample_data.type == BSDF_TYPE::TRANSMISSION) { + accumulated_color += weight * color_from_emission; + } else { + // To prevent double contribution of emission, only directly add if and only if: + // => we are directly hitting the light, i.e (i==0) + if (i == 0) { accumulated_color += weight * color_from_emission; } + } + + // Direct Light Sampling + if (direct && color_from_emission.length() == 0.0) { + int N = (int)scene->physical_lights.size(); // amount of lights + for (auto& light_ptr : scene->physical_lights) { // only accounts for physical lights currently + // Create ray from hit point to the light + point3 sampled_point = light_ptr->sample(record); + vec3 light_dir = (sampled_point - record.pos).unit_vector(); // direction from hit point to the light + ray light_ray = ray(record.pos, light_dir, 0.0); + + float distWithinMedium = (sampled_point - record.pos).length(); // distance to light + + // MultiIntersect to light to capture medium boundaries + MediumRecord light_med_rec(record.pos); + light_med_rec.mediums = med_rec.mediums; + light_med_rec.highest_density_volume = med_rec.highest_density_volume; + std::vector ids; + std::vector tfars; + // errors warning: overflow in conversion from 'float' to 'int' changes value from '+Inff' to '2147483647' [-Woverflow] + // MultiIntersect(std::numeric_limits::infinity(), light_ray, scene->rtc_scene, ids, tfars); + MultiIntersect(5, light_ray, scene->rtc_scene, ids, tfars); + + bool non_medium_encountered = false; + std::shared_ptr light_geomhit; + int light_id; + int light_tfar; + for (size_t j = 0; j < ids.size(); j++) { + int id = ids[j]; + float tfar = tfars[j]; + light_geomhit = scene->geom_map[id]; + std::shared_ptr possible_volume_hit = std::dynamic_pointer_cast(light_geomhit); + if (!possible_volume_hit) { // non volume encountered + if (light_geomhit == light_ptr) { // hit the light + light_id = id; + light_tfar = tfar; + light_med_rec.hitVolume(nullptr, light_ray.at(tfar)); + break; + } + non_medium_encountered = true; + break; + } else { // one of the intersections was a volume + light_med_rec.hitVolume(possible_volume_hit->medium, light_ray.at(tfar)); + } + } + + if (!non_medium_encountered) { // if it is the light, we are not obscured from the light + // Store hit data of tracing the ray from here to the light + HitInfo light_record; + light_record = light_geomhit->getHitInfo(light_ray, light_ray.at(light_tfar), light_tfar, light_id); + + // Get the light's material + std::shared_ptr light_mat_ptr = light_geomhit->materialById(light_id); + + // Sample BSDF of hit point with incoming light + ray light_scattered; + + BSDFSample light_sample_data; + color att; + // We do NOT call the above line because it would sample a possibly different microfacet normal + // than what is already sampled previous to the Direct Light Sampling (for complex BSDFs that use microfacets) + // Both generate and pdf assume that, if a microfacet normal is needed, it is already defined. Thus, we use the previous + // and pass in the same HitInfo. + light_sample_data.bsdf_value = mat_ptr->generate(r_in, light_ray, record); + light_sample_data.pdf_value = mat_ptr->pdf(r_in, light_ray, record); + + // Find pdf for the light hit point + double light_pdf_value = light_ptr->pdf(light_record, light_ray); + + // Find contribution of light using MIS power heuristic of light_pdf and sample pdf + double light_cos_theta = fabs(dot(record.normal, light_dir)); + color light_contribution = weight * light_sample_data.bsdf_value * light_cos_theta + * MIS::power_heuristic(light_pdf_value, light_sample_data.pdf_value) / light_pdf_value; + float transmittance_coeff = light_med_rec.transmittance; + light_contribution *= transmittance_coeff; + // Get emission of light + color light_Le = light_mat_ptr->emitted(light_record.u, light_record.v, light_record.pos); + accumulated_color += light_contribution * light_Le / N; + } + } + } + + // Indirect ray contribution + if (!sample_data.scatter) { + return accumulated_color; + } + double cos_theta = fabs(dot(record.normal, (sample_data.scatter_direction))); + if (!raymarched) { + weight = weight * (sample_data.bsdf_value * cos_theta / sample_data.pdf_value); + } else { + weight = weight * (sample_data.bsdf_value / sample_data.pdf_value); + } + r_in = scattered; + incoming_type = sample_data.type; + } + return accumulated_color; +} + void setRenderData(RenderData& render_data, const float aspect_ratio, const int image_width, const int samples_per_pixel, const int max_depth) { const int image_height = static_cast(image_width / aspect_ratio); render_data.image_width = image_width; @@ -64,7 +265,7 @@ void render_scanlines(int lines, int start_line, std::shared_ptr scene_pt int sqrt_samples = int(sqrt(samples_per_pixel)); - + for (int j=start_line; j>=start_line - (lines - 1); --j) { for (int i=0; i Date: Wed, 15 May 2024 22:51:32 -0400 Subject: [PATCH 075/142] Update hit_info.hh --- include/intersection/hit_info.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/include/intersection/hit_info.hh b/include/intersection/hit_info.hh index a7d0eda..0bad8dc 100644 --- a/include/intersection/hit_info.hh +++ b/include/intersection/hit_info.hh @@ -11,6 +11,7 @@ struct HitInfo { float t; double u; double v; + bool transparent = false; bool medium = false; From b4bc4b9e6d25fd6865e17bfe86e5c0294e3e309b Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 17 May 2024 19:41:28 -0400 Subject: [PATCH 076/142] Update csr-schema --- csr-schema | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csr-schema b/csr-schema index c7feab0..fd69650 160000 --- a/csr-schema +++ b/csr-schema @@ -1 +1 @@ -Subproject commit c7feab0264d734b60b1f72095bd6a2e26fa5f97d +Subproject commit fd69650a7bf69b08228135054263a5c8345906df From abdcd930458e6409c3c9b0ce4043a4650a0a7382 Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 17 May 2024 21:47:24 -0400 Subject: [PATCH 077/142] Update material.h --- include/materials/material.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/materials/material.h b/include/materials/material.h index 673e387..1035470 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -647,7 +647,7 @@ class pixel_lambertian : public material { virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { if (!rec.transparent) { - return albedo->value(rec.u, rec.v, rec.pos) / pi; + return albedo->value(rec.u, rec.v).RGB / pi; } else { return color(1.0, 1.0, 1.0) / pi; } @@ -655,7 +655,8 @@ class pixel_lambertian : public material { virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { auto cos_theta = dot(rec.normal, scattered.direction().unit_vector()); - return fmax(0.0, cos_theta / pi); + if (!rec.transparent) { return fmax(0.0, cos_theta / pi); } + else { return fabs(cos_theta) / pi; } } virtual BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const { From 0d1c428dad4f8a035266e09771a6e58cc8e05846 Mon Sep 17 00:00:00 2001 From: connortbot Date: Sun, 4 Aug 2024 15:15:53 -0400 Subject: [PATCH 078/142] CA-95 MixtureBSDFs --- include/intersection/hit_info.hh | 2 + include/materials/material.h | 66 ++++++++++++++++++++++++++++++++ main.cc | 14 +++++-- src/output/output.cc | 3 +- 4 files changed, 81 insertions(+), 4 deletions(-) diff --git a/include/intersection/hit_info.hh b/include/intersection/hit_info.hh index 0bad8dc..ae4a62b 100644 --- a/include/intersection/hit_info.hh +++ b/include/intersection/hit_info.hh @@ -15,6 +15,8 @@ struct HitInfo { bool medium = false; + float rand = 0.5; // used in MixtureBSDF + /** @brief Given a face's outward normal and the initial ray, sets front_face to represent if collision hits it from the front or not. */ void set_face_normal(const ray& r, const vec3& outward_normal); diff --git a/include/materials/material.h b/include/materials/material.h index 1035470..d18347e 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -679,4 +679,70 @@ class pixel_lambertian : public material { shared_ptr albedo; }; +/** + * @class MixtureBSDF + * @brief A linearly interpolated mixture of N materials, using a vector of weights and materials. + * The mixture is done by simple randomization, where the weights decide the frequency that a certain mixed + * material is used. This means that there is no confusing interpolation between outgoing directions or sampling. + * + * @param weights Vector of floats representing weights of each material. Should sum to 1 to retain energy conservation. + * @param mats Vector of materials. + * + * @note It is assumed that the given vectors are of the same length and that the weights sum to 1. +*/ +class MixtureBSDF : public material { + public: + MixtureBSDF(std::vector weights, std::vector> mats) : weights{weights}, mats{mats} {} + + virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { + rec.rand = random_double(); + int mat_ix = chooseSampleMaterial(rec.rand); + if (mat_ix >= 0) { // weights is valid + return mats[mat_ix]->scatter(r_in, rec, attenuation, scattered); + } else { + return false; + } + } + + // assumes that scatter has already been called or sample has already been called, and thus rand is already generated. + virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { + int mat_ix = chooseSampleMaterial(rec.rand); + if (mat_ix >= 0) { return mats[mat_ix]->generate(r_in, scattered, rec); + } else { return color(1,1,1); } + } + + virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { + int mat_ix = chooseSampleMaterial(rec.rand); + if (mat_ix >= 0) { return mats[mat_ix]->pdf(r_in, scattered, rec); + } else { return 1.0; } + } + + // NOTE: ASSUMES WEIGHTS IS AT LEAST OF SIZE 1 OTHERWISE BEHAVIOUR IS UNDEFINED + BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const override { + rec.rand = random_double(); + int mat_ix = chooseSampleMaterial(rec.rand); + if (mat_ix >= 0) { return mats[mat_ix]->sample(r_in, rec, scattered); + } else { + BSDFSample sample_data; + return sample_data; + } + } + + private: + std::vector weights; + std::vector> mats; + + // Returns negative if weights vector has nothing. + int chooseSampleMaterial(float rand) const { + float cumulative_weight = 0.0f; + for (size_t i = 0; i < weights.size(); i++) { + cumulative_weight += weights[i]; + if (rand < cumulative_weight) { + return i; + } + } + return (int)weights.size() - 1; + } +}; + #endif diff --git a/main.cc b/main.cc index d0d843e..b6c8845 100644 --- a/main.cc +++ b/main.cc @@ -42,15 +42,23 @@ void brdf_tests() { auto mt6 = make_shared(color(1.0, 1.0, 1.0), 1.5, 0.0001); // model glass auto mt7 = make_shared(color(1.0, 1.0, 1.0), 0.0, 0.0001); // model mirror + // Example of MixtureBSDF + std::vector weights = {0.333f, 0.334f, 0.333f}; + std::vector> mats; + mats.push_back(mt3); + mats.push_back(mt6); + mats.push_back(mt7); + auto mt8 = make_shared(weights, mats); + + // Adding 3 spheres auto sphere1 = make_shared(point3(0, 2, 2), mt5, 2, device); auto sphere2 = make_shared(point3(1, 2, -2), emit, 0.5, device); - auto sphere3 = make_shared(point3(-4, 2, -1), mt6, 2, device); - // scene_ptr->add_primitive(sphere1); + auto sphere3 = make_shared(point3(-4, 2, -1), mt8, 2, device); scene_ptr->add_primitive(sphere2); scene_ptr->add_primitive(sphere3); scene_ptr->add_physical_light(sphere2); - // Create volume out of sphere3 + // Create volume out of sphere1 auto iso = make_shared(color(1,1,1)); auto medium = make_shared(1, iso); auto volume1 = make_shared(medium, sphere1, device); diff --git a/src/output/output.cc b/src/output/output.cc index fc3af68..b166b55 100644 --- a/src/output/output.cc +++ b/src/output/output.cc @@ -91,7 +91,8 @@ void output(RenderData& render_data, Camera& cam, std::shared_ptr scene_p } } - if (config.verbose) { + if (true) { + // if (config.verbose) { auto current_time = std::chrono::high_resolution_clock::now(); auto elapsed_time = std::chrono::duration_cast(current_time - start_time).count(); double time_seconds = elapsed_time / 1000.0; From 2a5a1256d1840dfc9de054690efe7c6f9bba42f1 Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 27 Aug 2024 02:17:51 -0400 Subject: [PATCH 079/142] CA-124 generate and scatter samplers for LayeredBSDF --- include/materials/material.h | 90 ++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/include/materials/material.h b/include/materials/material.h index d18347e..2cc686c 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -745,4 +745,94 @@ class MixtureBSDF : public material { } }; +/** + * @class LayeredBSDF + * @brief A representation of a top BSDF layered over a bottom BSDF with no medium inside. All sampling is done by tracing ray stochastically + * through the layers and sampling each time. + * + * @param top Top Layer BSDF. + * @param bottom Bottom Layer BSDF. + * @param termination Russian Roulette termination condition for number of bounces. If exceeded, pretends light is absorbed. + * + * @note Generate creates a copy of rec, scattered for simulation. Possibly better way...? +*/ +class LayeredBSDF : public material { + public: + LayeredBSDF(std::shared_ptr top, std::shared_ptr bottom, int termination = 10) : top{top}, bottom{bottom}, termination{termination} {} + + virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { + int bounce_count = 0; + bool on_top = true; + + ray b = r_in; + + while (bounce_count <= termination) { + bool layer_scatter; + if (on_top) { layer_scatter = top->scatter(b, rec, attenuation, scattered); } + else { layer_scatter = bottom->scatter(b, rec, attenuation, scattered); } + + if (!layer_scatter) { return false; } + + if (dot(rec.normal, scattered.direction()) <= 0) { + if (on_top) { // refract top + b = ray(b.origin(), scattered.direction(), b.time()); // change direction to scattered + bounce_count++; + continue; + } else { return true; } // full transmission + } else { + if (on_top) { return true; } // reflect top + else { // reflect bottom + b = ray(b.origin(), scattered.direction(), b.time()); + bounce_count++; + continue; + } + } + } + } + + virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { + int bounce_count = 0; + bool on_top = true; + + ray b = r_in; + + // Variables to run layered simulation + color attenuation; // placeholder for scatter functions. attenuation is not used and should eventually be removed + // from the scatter function signature. + HitInfo rec_manip = rec; + ray scattered_manip = scattered; + color f = color(1, 1, 1); + + while (bounce_count <= termination) { + bool layer_scatter; + if (on_top) { + layer_scatter = top->scatter(b, rec_manip, attenuation, scattered_manip); + f = f * top->generate(b, scattered_manip, rec_manip); + } else { + layer_scatter = bottom->scatter(b, rec_manip, attenuation, scattered_manip); + f = f * bottom->generate(b, scattered_manip, rec_manip); + } + + if (!layer_scatter) { return color(0.0, 0.0, 0.0); } // no scattering, black + + if (dot(rec_manip.normal, scattered_manip.direction()) <= 0) { + if (on_top) { // refract top + b = ray(b.origin(), scattered_manip.direction(), b.time()); // change direction to scattered + bounce_count++; + continue; + } else { return f; } // full transmission + } else { + if (on_top) { return f; } // reflect top + else { // reflect bottom + b = ray(b.origin(), scattered_manip.direction(), b.time()); + bounce_count++; + continue; + } + } + } + } + + private: + int termination; + std::shared_ptr top; #endif From ffe122ee328d15ca7a9eda5190091e1bc6da3626 Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 27 Aug 2024 02:18:34 -0400 Subject: [PATCH 080/142] Update material.h --- include/materials/material.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/materials/material.h b/include/materials/material.h index 2cc686c..cb53f7a 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -835,4 +835,7 @@ class LayeredBSDF : public material { private: int termination; std::shared_ptr top; + std::shared_ptr bottom; +}; + #endif From 8646a0804c4f0236b26a7f762a685fede1b4d726 Mon Sep 17 00:00:00 2001 From: connortbot Date: Thu, 29 Aug 2024 10:59:10 -0400 Subject: [PATCH 081/142] CA-124 over absorbing sampler for layeredbsdf --- include/materials/material.h | 111 +++++++++++++++++++++++++++++++++++ main.cc | 16 ++++- 2 files changed, 126 insertions(+), 1 deletion(-) diff --git a/include/materials/material.h b/include/materials/material.h index cb53f7a..9c4fa6a 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -777,6 +777,7 @@ class LayeredBSDF : public material { if (on_top) { // refract top b = ray(b.origin(), scattered.direction(), b.time()); // change direction to scattered bounce_count++; + on_top = false; continue; } else { return true; } // full transmission } else { @@ -784,10 +785,12 @@ class LayeredBSDF : public material { else { // reflect bottom b = ray(b.origin(), scattered.direction(), b.time()); bounce_count++; + on_top = true; continue; } } } + return false; // if termination, absorb light } virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { @@ -819,6 +822,7 @@ class LayeredBSDF : public material { if (on_top) { // refract top b = ray(b.origin(), scattered_manip.direction(), b.time()); // change direction to scattered bounce_count++; + on_top = false; continue; } else { return f; } // full transmission } else { @@ -826,10 +830,117 @@ class LayeredBSDF : public material { else { // reflect bottom b = ray(b.origin(), scattered_manip.direction(), b.time()); bounce_count++; + on_top = true; continue; } } } + return color(0, 0, 0); // if termination, absorb light. + } + + virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { + int bounce_count = 0; + bool on_top = true; + + ray b = r_in; + + // Variables to run layered simulation + color attenuation; // placeholder for scatter functions. attenuation is not used and should eventually be removed + // from the scatter function signature. + HitInfo rec_manip = rec; + ray scattered_manip = scattered; + double pdf = 1.0; + + while (bounce_count <= termination) { + bool layer_scatter; + if (on_top) { + layer_scatter = top->scatter(b, rec_manip, attenuation, scattered_manip); + pdf = pdf * top->pdf(b, scattered_manip, rec_manip); + } else { + layer_scatter = bottom->scatter(b, rec_manip, attenuation, scattered_manip); + pdf = pdf * bottom->pdf(b, scattered_manip, rec_manip); + } + + if (!layer_scatter) { return 1.0; } // no scattering, black + + if (dot(rec_manip.normal, scattered_manip.direction()) <= 0) { + if (on_top) { // refract top + b = ray(b.origin(), scattered_manip.direction(), b.time()); // change direction to scattered + bounce_count++; + on_top = false; + continue; + } else { return pdf; } // full transmission + } else { + if (on_top) { return pdf; } // reflect top + else { // reflect bottom + b = ray(b.origin(), scattered_manip.direction(), b.time()); + bounce_count++; + on_top = true; + continue; + } + } + } + return 0.0; // if termination, absorb light (more accurately, we return infinity?) + } + + // Structure courtesy of 14.3.2, pbrt + BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const override { + HitInfo rec_manip = rec; + BSDFSample absorbed; absorbed.scatter = false; + // Sample BSDF at entrance interface to get initial direction w + bool on_top = rec_manip.front_face; + vec3 outward_normal = rec_manip.front_face ? rec_manip.normal : -rec_manip.normal; + + BSDFSample bs = on_top ? top->sample(r_in, rec_manip, scattered) : bottom->sample(r_in, rec_manip, scattered); + if (!bs.scatter) { return absorbed; } + if (dot(rec_manip.normal, bs.scatter_direction) > 0) { return bs; } + vec3 w = bs.scatter_direction; + + color f = bs.bsdf_value * fabs(dot(rec_manip.normal, (bs.scatter_direction))); + float pdf = bs.pdf_value; + + for (int depth = 0; depth < termination; depth++) { + // Follow random walk through layers to sample layered BSDF + // Possibly terminate layered BSDF sampling with Russian Roulette + float rrBeta = fmax(fmax(f.x(), f.y()), f.z()) / bs.pdf_value; + if (depth > 3 && rrBeta < 0.25) { + float q = fmax(0, 1-rrBeta); + if (random_double() < q) { return absorbed; } // absorb light + // otherwise, account pdf for possibility of termination + pdf *= 1 - q; + } + + // Initialize new surface + std::shared_ptr layer = on_top ? bottom : top; + + // Sample layer BSDF for determine new path direction + ray r_new = ray(r_in.origin() - w, w, 0.0); + BSDFSample bs = layer->sample(r_new, rec_manip, scattered); + if (!bs.scatter) { return absorbed; } + f = f * bs.bsdf_value; + pdf = pdf * bs.pdf_value; + w = bs.scatter_direction; + + // Return sample if path has left the layers + if (bs.type == BSDF_TYPE::TRANSMISSION) { + BSDF_TYPE flag = dot(outward_normal, w) ? BSDF_TYPE::SPECULAR : BSDF_TYPE::TRANSMISSION; + BSDFSample out_sample; + out_sample.scatter = true; + out_sample.scatter_direction = w; + out_sample.bsdf_value = f; + out_sample.pdf_value = pdf; + out_sample.type = flag; + return out_sample; + } + + f = f * fabs(dot(rec_manip.normal, (bs.scatter_direction))); + + // Flip + on_top = !on_top; + rec_manip.front_face = !rec_manip.front_face; + rec_manip.normal = -rec_manip.normal; + } + return absorbed; } private: diff --git a/main.cc b/main.cc index b6c8845..0d96c1f 100644 --- a/main.cc +++ b/main.cc @@ -50,10 +50,24 @@ void brdf_tests() { mats.push_back(mt7); auto mt8 = make_shared(weights, mats); + // Example of LayeredBSDF: glass on diffuse + auto mt9 = make_shared(mt6, mt3, 200); + + // TEST ON LAYEREDBSDF + // HitInfo rec; + // rec.pos = point3(0,0,0); + // rec.normal = vec3(0,1,0); + // rec.front_face = true; + // rec.t = 0.0; + + // ray wi = ray(point3(1, 1, 0), (vec3(0,0,0) - vec3(1,1,0)).unit_vector(), 0.0); + // ray scattered; + // BSDFSample test_sample = mt9->sample(wi, rec, scattered); + // Adding 3 spheres auto sphere1 = make_shared(point3(0, 2, 2), mt5, 2, device); auto sphere2 = make_shared(point3(1, 2, -2), emit, 0.5, device); - auto sphere3 = make_shared(point3(-4, 2, -1), mt8, 2, device); + auto sphere3 = make_shared(point3(-4, 2, -1), mt9, 2, device); scene_ptr->add_primitive(sphere2); scene_ptr->add_primitive(sphere3); scene_ptr->add_physical_light(sphere2); From 34cecbbef66135d60ac85262b77480166fe365b2 Mon Sep 17 00:00:00 2001 From: connortbot Date: Wed, 18 Sep 2024 16:22:34 -0400 Subject: [PATCH 082/142] CA-124 functional LayeredBxDF (with black specks) --- include/materials/material.h | 241 +++++++++++++++-------------------- 1 file changed, 101 insertions(+), 140 deletions(-) diff --git a/include/materials/material.h b/include/materials/material.h index 9c4fa6a..608112d 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -761,183 +761,144 @@ class LayeredBSDF : public material { LayeredBSDF(std::shared_ptr top, std::shared_ptr bottom, int termination = 10) : top{top}, bottom{bottom}, termination{termination} {} virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { - int bounce_count = 0; - bool on_top = true; - - ray b = r_in; + ray r = r_in; + HitInfo rec_manip = rec; - while (bounce_count <= termination) { - bool layer_scatter; - if (on_top) { layer_scatter = top->scatter(b, rec, attenuation, scattered); } - else { layer_scatter = bottom->scatter(b, rec, attenuation, scattered); } + bool on_top = rec_manip.front_face; + rec_manip.front_face ? rec_manip.normal : -rec_manip.normal; - if (!layer_scatter) { return false; } - - if (dot(rec.normal, scattered.direction()) <= 0) { - if (on_top) { // refract top - b = ray(b.origin(), scattered.direction(), b.time()); // change direction to scattered - bounce_count++; - on_top = false; - continue; - } else { return true; } // full transmission - } else { - if (on_top) { return true; } // reflect top - else { // reflect bottom - b = ray(b.origin(), scattered.direction(), b.time()); - bounce_count++; - on_top = true; - continue; - } - } + std::shared_ptr current = on_top ? top : bottom; + BSDFSample bs = current->sample(r, rec_manip, scattered); + + if (!bs.scatter) { return false; } + if (bs.type != BSDF_TYPE::TRANSMISSION && bs.type != BSDF_TYPE::TRANSPARENT) { return true; } + for (int depth = 0; depth < termination; depth++) { + + on_top = !on_top; + current = on_top ? top : bottom; + r = ray(rec_manip.pos - bs.scatter_direction, bs.scatter_direction, r.time()); + + bs = current->sample(r, rec_manip, scattered); + + if (on_top && bs.type == BSDF_TYPE::TRANSMISSION) { return true; } + + // Flip since coming from the bottom! + rec_manip.front_face = false; + rec_manip.normal = -rec_manip.normal; } - return false; // if termination, absorb light + return false; } virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { - int bounce_count = 0; - bool on_top = true; + + ray r = r_in; + HitInfo rec_manip = rec; - ray b = r_in; + bool on_top = rec_manip.front_face; + rec_manip.front_face ? rec_manip.normal : -rec_manip.normal; - // Variables to run layered simulation - color attenuation; // placeholder for scatter functions. attenuation is not used and should eventually be removed - // from the scatter function signature. - HitInfo rec_manip = rec; - ray scattered_manip = scattered; - color f = color(1, 1, 1); - - while (bounce_count <= termination) { - bool layer_scatter; - if (on_top) { - layer_scatter = top->scatter(b, rec_manip, attenuation, scattered_manip); - f = f * top->generate(b, scattered_manip, rec_manip); - } else { - layer_scatter = bottom->scatter(b, rec_manip, attenuation, scattered_manip); - f = f * bottom->generate(b, scattered_manip, rec_manip); - } + ray scattered_copy = scattered; + std::shared_ptr current = on_top ? top : bottom; + BSDFSample bs = current->sample(r, rec_manip, scattered_copy); + color f = bs.bsdf_value; - if (!layer_scatter) { return color(0.0, 0.0, 0.0); } // no scattering, black + if (!bs.scatter) { return color(1.0, 1.0, 1.0); } + if (bs.type != BSDF_TYPE::TRANSMISSION && bs.type != BSDF_TYPE::TRANSPARENT) { return f; } + for (int depth = 0; depth < termination; depth++) { - if (dot(rec_manip.normal, scattered_manip.direction()) <= 0) { - if (on_top) { // refract top - b = ray(b.origin(), scattered_manip.direction(), b.time()); // change direction to scattered - bounce_count++; - on_top = false; - continue; - } else { return f; } // full transmission - } else { - if (on_top) { return f; } // reflect top - else { // reflect bottom - b = ray(b.origin(), scattered_manip.direction(), b.time()); - bounce_count++; - on_top = true; - continue; - } - } + on_top = !on_top; + current = on_top ? top : bottom; + f = f * fabs(dot(rec_manip.normal, (bs.scatter_direction))); + r = ray(rec_manip.pos - bs.scatter_direction, bs.scatter_direction, r.time()); + + bs = current->sample(r, rec_manip, scattered); + f = f * bs.bsdf_value; + + if (on_top && bs.type == BSDF_TYPE::TRANSMISSION) { return f; } + + // Flip since coming from the bottom! + rec_manip.front_face = false; + rec_manip.normal = -rec_manip.normal; } - return color(0, 0, 0); // if termination, absorb light. + return color(1.0, 1.0, 1.0); } virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { - int bounce_count = 0; - bool on_top = true; + ray r = r_in; + HitInfo rec_manip = rec; - ray b = r_in; + bool on_top = rec_manip.front_face; + rec_manip.front_face ? rec_manip.normal : -rec_manip.normal; - // Variables to run layered simulation - color attenuation; // placeholder for scatter functions. attenuation is not used and should eventually be removed - // from the scatter function signature. - HitInfo rec_manip = rec; - ray scattered_manip = scattered; - double pdf = 1.0; - - while (bounce_count <= termination) { - bool layer_scatter; - if (on_top) { - layer_scatter = top->scatter(b, rec_manip, attenuation, scattered_manip); - pdf = pdf * top->pdf(b, scattered_manip, rec_manip); - } else { - layer_scatter = bottom->scatter(b, rec_manip, attenuation, scattered_manip); - pdf = pdf * bottom->pdf(b, scattered_manip, rec_manip); - } + ray scattered_copy = scattered; + std::shared_ptr current = on_top ? top : bottom; + BSDFSample bs = current->sample(r, rec_manip, scattered_copy); + float pdf = bs.pdf_value; - if (!layer_scatter) { return 1.0; } // no scattering, black + if (!bs.scatter) { return 1.0; } + if (bs.type != BSDF_TYPE::TRANSMISSION && bs.type != BSDF_TYPE::TRANSPARENT) { return pdf; } + for (int depth = 0; depth < termination; depth++) { - if (dot(rec_manip.normal, scattered_manip.direction()) <= 0) { - if (on_top) { // refract top - b = ray(b.origin(), scattered_manip.direction(), b.time()); // change direction to scattered - bounce_count++; - on_top = false; - continue; - } else { return pdf; } // full transmission - } else { - if (on_top) { return pdf; } // reflect top - else { // reflect bottom - b = ray(b.origin(), scattered_manip.direction(), b.time()); - bounce_count++; - on_top = true; - continue; - } + on_top = !on_top; + current = on_top ? top : bottom; + r = ray(rec_manip.pos - bs.scatter_direction, bs.scatter_direction, r.time()); + + bs = current->sample(r, rec_manip, scattered_copy); + pdf = pdf * bs.pdf_value; + + if (on_top && bs.type == BSDF_TYPE::TRANSMISSION) { + return pdf; } + + // Flip since coming from the bottom! + rec_manip.front_face = false; + rec_manip.normal = -rec_manip.normal; } - return 0.0; // if termination, absorb light (more accurately, we return infinity?) + return 1.0; } // Structure courtesy of 14.3.2, pbrt BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const override { - HitInfo rec_manip = rec; + // Return in case calculating a full simulation becomes impossible or irrelevant BSDFSample absorbed; absorbed.scatter = false; - // Sample BSDF at entrance interface to get initial direction w + + ray r = r_in; + HitInfo rec_manip = rec; + bool on_top = rec_manip.front_face; - vec3 outward_normal = rec_manip.front_face ? rec_manip.normal : -rec_manip.normal; + rec_manip.front_face ? rec_manip.normal : -rec_manip.normal; - BSDFSample bs = on_top ? top->sample(r_in, rec_manip, scattered) : bottom->sample(r_in, rec_manip, scattered); - if (!bs.scatter) { return absorbed; } - if (dot(rec_manip.normal, bs.scatter_direction) > 0) { return bs; } - vec3 w = bs.scatter_direction; + std::shared_ptr current = on_top ? top : bottom; + BSDFSample bs = current->sample(r, rec_manip, scattered); - color f = bs.bsdf_value * fabs(dot(rec_manip.normal, (bs.scatter_direction))); + color f = bs.bsdf_value; float pdf = bs.pdf_value; + if (!bs.scatter) { return absorbed; } + if (bs.type != BSDF_TYPE::TRANSMISSION && bs.type != BSDF_TYPE::TRANSPARENT) { + bs.type = (dot(bs.scatter_direction, rec.normal) < 0) ? BSDF_TYPE::TRANSMISSION : BSDF_TYPE::SPECULAR; + return bs; + } for (int depth = 0; depth < termination; depth++) { - // Follow random walk through layers to sample layered BSDF - // Possibly terminate layered BSDF sampling with Russian Roulette - float rrBeta = fmax(fmax(f.x(), f.y()), f.z()) / bs.pdf_value; - if (depth > 3 && rrBeta < 0.25) { - float q = fmax(0, 1-rrBeta); - if (random_double() < q) { return absorbed; } // absorb light - // otherwise, account pdf for possibility of termination - pdf *= 1 - q; - } - // Initialize new surface - std::shared_ptr layer = on_top ? bottom : top; + on_top = !on_top; + current = on_top ? top : bottom; + f = f * fabs(dot(rec_manip.normal, (bs.scatter_direction))); + r = ray(rec_manip.pos - bs.scatter_direction, bs.scatter_direction, r.time()); - // Sample layer BSDF for determine new path direction - ray r_new = ray(r_in.origin() - w, w, 0.0); - BSDFSample bs = layer->sample(r_new, rec_manip, scattered); - if (!bs.scatter) { return absorbed; } + bs = current->sample(r, rec_manip, scattered); f = f * bs.bsdf_value; pdf = pdf * bs.pdf_value; - w = bs.scatter_direction; - - // Return sample if path has left the layers - if (bs.type == BSDF_TYPE::TRANSMISSION) { - BSDF_TYPE flag = dot(outward_normal, w) ? BSDF_TYPE::SPECULAR : BSDF_TYPE::TRANSMISSION; - BSDFSample out_sample; - out_sample.scatter = true; - out_sample.scatter_direction = w; - out_sample.bsdf_value = f; - out_sample.pdf_value = pdf; - out_sample.type = flag; - return out_sample; + bs.bsdf_value = f; + bs.pdf_value = pdf; + + if (on_top && bs.type == BSDF_TYPE::TRANSMISSION) { + bs.type = (dot(bs.scatter_direction, rec.normal) < 0) ? BSDF_TYPE::TRANSMISSION : BSDF_TYPE::SPECULAR; + return bs; } - - f = f * fabs(dot(rec_manip.normal, (bs.scatter_direction))); - // Flip - on_top = !on_top; - rec_manip.front_face = !rec_manip.front_face; + // Flip since coming from the bottom! + rec_manip.front_face = false; rec_manip.normal = -rec_manip.normal; } return absorbed; From 2d3fb9b97543b0137d6e3a2033a82c166b6b54a3 Mon Sep 17 00:00:00 2001 From: connortbot Date: Wed, 18 Sep 2024 20:37:16 -0400 Subject: [PATCH 083/142] CA-124 less scaled GGX D factor --- include/microfacet.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/microfacet.h b/include/microfacet.h index 6f77a31..bdb47d7 100644 --- a/include/microfacet.h +++ b/include/microfacet.h @@ -26,8 +26,9 @@ class GGX : public Microfacet { float D(float NoH) const override { float r = fmax(0.0001, roughness); - float alpha = r * r; - float alpha2 = alpha * alpha; + // float alpha = r * r; + // float alpha2 = alpha * alpha; + float alpha2 = r * r; float NoH2 = NoH * NoH; float b = (NoH2 * (alpha2 - 1.0) + 1.0); return (alpha2 / pi) / (b * b); From 50dd8920013c659035df491e47951d3296106c6a Mon Sep 17 00:00:00 2001 From: connortbot Date: Wed, 18 Sep 2024 21:48:29 -0400 Subject: [PATCH 084/142] CA-124 fixed black specs with russian roulette biasing, and notes on D overflowing --- include/materials/material.h | 34 +++++++++++++++++++++++++++++----- include/microfacet.h | 5 ++++- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/include/materials/material.h b/include/materials/material.h index 608112d..42c6463 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -754,7 +754,8 @@ class MixtureBSDF : public material { * @param bottom Bottom Layer BSDF. * @param termination Russian Roulette termination condition for number of bounces. If exceeded, pretends light is absorbed. * - * @note Generate creates a copy of rec, scattered for simulation. Possibly better way...? + * @note SCATTER, GENERATE, AND PDF DO NOT CONTAIN NECESSARY RUSSIAN ROULETTE OR LOGARITHMIC ACCUMULATION. THEY ARE NOT READY. + * Use sample instead. */ class LayeredBSDF : public material { public: @@ -811,7 +812,7 @@ class LayeredBSDF : public material { f = f * fabs(dot(rec_manip.normal, (bs.scatter_direction))); r = ray(rec_manip.pos - bs.scatter_direction, bs.scatter_direction, r.time()); - bs = current->sample(r, rec_manip, scattered); + bs = current->sample(r, rec_manip, scattered_copy); f = f * bs.bsdf_value; if (on_top && bs.type == BSDF_TYPE::TRANSMISSION) { return f; } @@ -857,7 +858,14 @@ class LayeredBSDF : public material { return 1.0; } - // Structure courtesy of 14.3.2, pbrt + // NOTES: + // - The D term in GGX is known to scale at ridiculous amounts to overflow to inf when multiple products, as seen in layering. + // For now, since we know that D exists in the f and pdf, it is safe to arbitrarily set it to 1 or omit it completely, but a better solution is needed. + // - Russian Roulette termination does not return black. This is to avoid black specks, but is PHYSICALLY IMPLAUSIBLE. + // There may be a better solution! + // We can check if the ray is bouncing back towards the INITIAL LAYER by if rec.front_face = on_top + // This means that it can never exit via the non-initial layer as a result of Russian Roulette. This is a sacrifice becasue + // there are currently no flags to check if a layer is transmissible or not to exit. However, we know that the initial layer must be. BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const override { // Return in case calculating a full simulation becomes impossible or irrelevant BSDFSample absorbed; absorbed.scatter = false; @@ -880,9 +888,24 @@ class LayeredBSDF : public material { return bs; } for (int depth = 0; depth < termination; depth++) { - + + // Follow random walk through layers to sample layered BSDF on_top = !on_top; current = on_top ? top : bottom; + + // Possibly terminate layered BSDF sampling with Russian Roulette + float rrBeta = fmax(fmax(f.x(), f.y()), f.z()) / bs.pdf_value; + if (depth > 3 && rrBeta < 0.25) { + float q = fmax(0, 1-rrBeta); + if (random_double() < q) { + if (on_top == rec.front_face) { + bs.type = (dot(bs.scatter_direction, rec.normal) < 0) ? BSDF_TYPE::TRANSMISSION : BSDF_TYPE::SPECULAR; + return bs; + } + } + pdf *= 1 - q; + } + f = f * fabs(dot(rec_manip.normal, (bs.scatter_direction))); r = ray(rec_manip.pos - bs.scatter_direction, bs.scatter_direction, r.time()); @@ -901,7 +924,8 @@ class LayeredBSDF : public material { rec_manip.front_face = false; rec_manip.normal = -rec_manip.normal; } - return absorbed; + bs.type = (dot(bs.scatter_direction, rec.normal) < 0) ? BSDF_TYPE::TRANSMISSION : BSDF_TYPE::SPECULAR; + return bs; } private: diff --git a/include/microfacet.h b/include/microfacet.h index bdb47d7..b67e24e 100644 --- a/include/microfacet.h +++ b/include/microfacet.h @@ -31,7 +31,10 @@ class GGX : public Microfacet { float alpha2 = r * r; float NoH2 = NoH * NoH; float b = (NoH2 * (alpha2 - 1.0) + 1.0); - return (alpha2 / pi) / (b * b); + // return (alpha2 / pi) / (b * b); + return 1; + // - The D term in GGX is known to scale at ridiculous amounts to overflow to inf when multiple products, as seen in layering. + // For now, since we know that D exists in the f and pdf, it is safe to arbitrarily set it to 1 or omit it completely, but a better solution is needed. } float G(float NoV, float NoL) const override { From 367afe27d36d4311c057f05a7ac24237efbf9690 Mon Sep 17 00:00:00 2001 From: connortbot Date: Wed, 18 Sep 2024 22:17:53 -0400 Subject: [PATCH 085/142] CA-124 schlick approximation to CookTorranceDielectric with exponent control! --- include/materials/material.h | 40 +++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/include/materials/material.h b/include/materials/material.h index 42c6463..409b104 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -401,10 +401,24 @@ class CookTorrance : public material { }; +/** + * @class CookTorranceDielectric + * @brief Implements the Cook-Torrance Dielectric BxDF model. + * This implementation uses the GGX (Trowbridge-Reitz) microfacet distribution to simulate the roughness. + * + * @param albedo + * @param eta Index of refraction (e.g 1.5 for glass) + * @param roughness In range [0-1] defines how rough the surface of the material becomes (less shiny). + * @param complexFresnel Indicates type of F term to calculate. 0 uses FrComplex, and any positive integer is used as the exponent to the + * Schlick approximation. + * @note by default, if no MDF is specified in the constructor, GGX is used. +*/ class CookTorranceDielectric : public material { public: - CookTorranceDielectric(color albedo, float eta, float roughness) : albedo{albedo}, eta{(eta == 0.0f) ? 0.0f : (float)fmax(eta, 1.0001f)}, MDF{std::make_shared(roughness)} {} + CookTorranceDielectric(color albedo, float eta, float roughness, int complexFresnel = 0) + : albedo{albedo}, eta{(eta == 0.0f) ? 0.0f : (float)fmax(eta, 1.0001f)}, + MDF{std::make_shared(roughness)}, complexFresnel{(int)fmax(complexFresnel, 0)} {} virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { vec3 wo = -r_in.direction().unit_vector(); @@ -413,7 +427,9 @@ class CookTorranceDielectric : public material { rec.microfacet_normal = wm; float cosTheta_i = dot(wo, wm); - float R = FrDielectric(cosTheta_i); + float R; + if (complexFresnel == 0) { R = FrDielectric(cosTheta_i); } + else { R = fresnelSchlick(cosTheta_i, complexFresnel); } float T = 1 - R; float u = random_double(); @@ -435,7 +451,9 @@ class CookTorranceDielectric : public material { vec3 wi = scattered.direction(); vec3 wm = rec.microfacet_normal; float cosTheta_i = dot(wo, wm); - float R = FrDielectric(cosTheta_i); + float R; + if (complexFresnel == 0) { R = FrDielectric(cosTheta_i); } + else { R = fresnelSchlick(cosTheta_i, complexFresnel); } float T = 1 - R; if (cosTheta_i > 0) { // reflectance return f_r(r_in, rec, scattered, R); @@ -449,7 +467,9 @@ class CookTorranceDielectric : public material { vec3 wi = scattered.direction(); vec3 wm = rec.microfacet_normal; float cosTheta_i = dot(wo, wm); - float R = FrDielectric(cosTheta_i); + float R; + if (complexFresnel == 0) { R = FrDielectric(cosTheta_i); } + else { R = fresnelSchlick(cosTheta_i, complexFresnel); } float T = 1 - R; if (cosTheta_i > 0) { // reflectance return pdf_r(r_in, rec, scattered, R); @@ -468,7 +488,9 @@ class CookTorranceDielectric : public material { rec.microfacet_normal = wm; float cosTheta_i = dot(wo, wm); - float R = FrDielectric(cosTheta_i); + float R; + if (complexFresnel == 0) { R = FrDielectric(cosTheta_i); } + else { R = fresnelSchlick(cosTheta_i, complexFresnel); } float T = 1 - R; float u = random_double(); @@ -503,6 +525,7 @@ class CookTorranceDielectric : public material { color albedo; float eta; std::shared_ptr MDF; + int complexFresnel; float FrDielectric(float cosTheta_i) const { float temp_eta = eta; @@ -522,7 +545,14 @@ class CookTorranceDielectric : public material { float r_perp = (cosTheta_i - (temp_eta * cosTheta_t)) / (cosTheta_i + (eta * cosTheta_t)); return ((r_parallel * r_parallel) + (r_perp * r_perp)) / 2; } + + float fresnelSchlick(float cosTheta, int exponent) const { + float F0 = pow(((1 - eta) / (1 + eta)), 2); + return F0 + (1.0 - F0) * pow(1.0 - cosTheta, exponent); + } + private: + color f_r(const ray& r_in, const HitInfo& rec, const ray& scattered, float R) const { vec3 V = -r_in.direction().unit_vector(); vec3 L = scattered.direction().unit_vector(); From 0c4d8bc7b8e09e62129d432d3cbee1db32606375 Mon Sep 17 00:00:00 2001 From: connortbot Date: Wed, 18 Sep 2024 22:21:51 -0400 Subject: [PATCH 086/142] Update main.cc --- main.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.cc b/main.cc index 0d96c1f..ce47005 100644 --- a/main.cc +++ b/main.cc @@ -39,7 +39,7 @@ void brdf_tests() { auto mt5 = make_shared(color(1.0, 1.0, 1.0), 0.1); // Dielectric comparison - auto mt6 = make_shared(color(1.0, 1.0, 1.0), 1.5, 0.0001); // model glass + auto mt6 = make_shared(color(1.0, 1.0, 1.0), 1.5, 0.0001, 5); // model glass auto mt7 = make_shared(color(1.0, 1.0, 1.0), 0.0, 0.0001); // model mirror // Example of MixtureBSDF From 4bb5091c28234e1436482408a5b1708a2cab19cc Mon Sep 17 00:00:00 2001 From: connortbot Date: Thu, 19 Sep 2024 10:46:40 -0400 Subject: [PATCH 087/142] CA-124 reclean main for casino --- main.cc | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/main.cc b/main.cc index 0d96c1f..41dbd5b 100644 --- a/main.cc +++ b/main.cc @@ -53,17 +53,6 @@ void brdf_tests() { // Example of LayeredBSDF: glass on diffuse auto mt9 = make_shared(mt6, mt3, 200); - // TEST ON LAYEREDBSDF - // HitInfo rec; - // rec.pos = point3(0,0,0); - // rec.normal = vec3(0,1,0); - // rec.front_face = true; - // rec.t = 0.0; - - // ray wi = ray(point3(1, 1, 0), (vec3(0,0,0) - vec3(1,1,0)).unit_vector(), 0.0); - // ray scattered; - // BSDFSample test_sample = mt9->sample(wi, rec, scattered); - // Adding 3 spheres auto sphere1 = make_shared(point3(0, 2, 2), mt5, 2, device); auto sphere2 = make_shared(point3(1, 2, -2), emit, 0.5, device); @@ -90,17 +79,21 @@ void brdf_tests() { } int main(int argc, char* argv[]) { - Config config = parseArguments(argc, argv); - - RenderData render_data; - const auto aspect_ratio = static_cast(config.image_width) / config.image_height; - setRenderData(render_data, aspect_ratio, config.image_width, config.samples_per_pixel, config.max_depth); - std::string filePath = config.inputFile; - RTCDevice device = initializeDevice(); - CSRParser parser; - auto scene_ptr = parser.parseCSR(filePath, device); - scene_ptr->commitScene(); - rtcReleaseDevice(device); + // LUT_test(); + // measurePerformance(); + brdf_tests(); - output(render_data, scene_ptr->cam, scene_ptr, config); + // Config config = parseArguments(argc, argv); + + // RenderData render_data; + // const auto aspect_ratio = static_cast(config.image_width) / config.image_height; + // setRenderData(render_data, aspect_ratio, config.image_width, config.samples_per_pixel, config.max_depth); + // std::string filePath = config.inputFile; + // RTCDevice device = initializeDevice(); + // CSRParser parser; + // auto scene_ptr = parser.parseCSR(filePath, device); + // scene_ptr->commitScene(); + // rtcReleaseDevice(device); + + // output(render_data, scene_ptr->cam, scene_ptr, config); } From 1c5127cb4053a76e1c7071cbfa4fdae3df686c9c Mon Sep 17 00:00:00 2001 From: connortbot Date: Thu, 19 Sep 2024 19:28:36 -0400 Subject: [PATCH 088/142] CA-124 LayeredSample with intermittent medium --- include/materials/material.h | 69 +++--------------------- main.cc | 21 +++----- src/materials/material.cc | 101 +++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 76 deletions(-) create mode 100644 src/materials/material.cc diff --git a/include/materials/material.h b/include/materials/material.h index 409b104..e5e6cd1 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -10,6 +10,7 @@ #include "microfacet.h" class hit_record; +class Medium; // CONSTANTS const float SPECULAR_ROUGHNESS_SAMPLING_CUTOFF = 0.1; @@ -789,7 +790,8 @@ class MixtureBSDF : public material { */ class LayeredBSDF : public material { public: - LayeredBSDF(std::shared_ptr top, std::shared_ptr bottom, int termination = 10) : top{top}, bottom{bottom}, termination{termination} {} + LayeredBSDF(std::shared_ptr top, std::shared_ptr bottom, std::shared_ptr medium, int termination = 10) + : top{top}, bottom{bottom}, medium{medium}, termination{termination} {} virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { ray r = r_in; @@ -896,71 +898,14 @@ class LayeredBSDF : public material { // We can check if the ray is bouncing back towards the INITIAL LAYER by if rec.front_face = on_top // This means that it can never exit via the non-initial layer as a result of Russian Roulette. This is a sacrifice becasue // there are currently no flags to check if a layer is transmissible or not to exit. However, we know that the initial layer must be. - BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const override { - // Return in case calculating a full simulation becomes impossible or irrelevant - BSDFSample absorbed; absorbed.scatter = false; - - ray r = r_in; - HitInfo rec_manip = rec; - - bool on_top = rec_manip.front_face; - rec_manip.front_face ? rec_manip.normal : -rec_manip.normal; - - std::shared_ptr current = on_top ? top : bottom; - BSDFSample bs = current->sample(r, rec_manip, scattered); - - color f = bs.bsdf_value; - float pdf = bs.pdf_value; - - if (!bs.scatter) { return absorbed; } - if (bs.type != BSDF_TYPE::TRANSMISSION && bs.type != BSDF_TYPE::TRANSPARENT) { - bs.type = (dot(bs.scatter_direction, rec.normal) < 0) ? BSDF_TYPE::TRANSMISSION : BSDF_TYPE::SPECULAR; - return bs; - } - for (int depth = 0; depth < termination; depth++) { - - // Follow random walk through layers to sample layered BSDF - on_top = !on_top; - current = on_top ? top : bottom; - - // Possibly terminate layered BSDF sampling with Russian Roulette - float rrBeta = fmax(fmax(f.x(), f.y()), f.z()) / bs.pdf_value; - if (depth > 3 && rrBeta < 0.25) { - float q = fmax(0, 1-rrBeta); - if (random_double() < q) { - if (on_top == rec.front_face) { - bs.type = (dot(bs.scatter_direction, rec.normal) < 0) ? BSDF_TYPE::TRANSMISSION : BSDF_TYPE::SPECULAR; - return bs; - } - } - pdf *= 1 - q; - } - - f = f * fabs(dot(rec_manip.normal, (bs.scatter_direction))); - r = ray(rec_manip.pos - bs.scatter_direction, bs.scatter_direction, r.time()); - - bs = current->sample(r, rec_manip, scattered); - f = f * bs.bsdf_value; - pdf = pdf * bs.pdf_value; - bs.bsdf_value = f; - bs.pdf_value = pdf; - - if (on_top && bs.type == BSDF_TYPE::TRANSMISSION) { - bs.type = (dot(bs.scatter_direction, rec.normal) < 0) ? BSDF_TYPE::TRANSMISSION : BSDF_TYPE::SPECULAR; - return bs; - } - - // Flip since coming from the bottom! - rec_manip.front_face = false; - rec_manip.normal = -rec_manip.normal; - } - bs.type = (dot(bs.scatter_direction, rec.normal) < 0) ? BSDF_TYPE::TRANSMISSION : BSDF_TYPE::SPECULAR; - return bs; - } + BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const override; private: + float thickness = 1.0; + int termination; std::shared_ptr top; + std::shared_ptr medium; std::shared_ptr bottom; }; diff --git a/main.cc b/main.cc index ce47005..c4fb566 100644 --- a/main.cc +++ b/main.cc @@ -50,19 +50,14 @@ void brdf_tests() { mats.push_back(mt7); auto mt8 = make_shared(weights, mats); - // Example of LayeredBSDF: glass on diffuse - auto mt9 = make_shared(mt6, mt3, 200); - - // TEST ON LAYEREDBSDF - // HitInfo rec; - // rec.pos = point3(0,0,0); - // rec.normal = vec3(0,1,0); - // rec.front_face = true; - // rec.t = 0.0; + // Example of creating medium with isotropic phase function + auto iso = make_shared(color(1.0,1.0,1.0)); + auto medium = make_shared(1, iso); - // ray wi = ray(point3(1, 1, 0), (vec3(0,0,0) - vec3(1,1,0)).unit_vector(), 0.0); - // ray scattered; - // BSDFSample test_sample = mt9->sample(wi, rec, scattered); + // Example of LayeredBSDF: glass on diffuse with white cloud inside + auto layered_iso = make_shared(color(0.1,0.8,0.1)); + auto layered_medium = make_shared(0.1, layered_iso); + auto mt9 = make_shared(mt6, mt3, layered_medium, 200); // Adding 3 spheres auto sphere1 = make_shared(point3(0, 2, 2), mt5, 2, device); @@ -73,8 +68,6 @@ void brdf_tests() { scene_ptr->add_physical_light(sphere2); // Create volume out of sphere1 - auto iso = make_shared(color(1,1,1)); - auto medium = make_shared(1, iso); auto volume1 = make_shared(medium, sphere1, device); scene_ptr->add_volume(volume1); diff --git a/src/materials/material.cc b/src/materials/material.cc new file mode 100644 index 0000000..6c5d1c0 --- /dev/null +++ b/src/materials/material.cc @@ -0,0 +1,101 @@ +#include "material.h" + +#include "medium.h" + +BSDFSample LayeredBSDF::sample(const ray& r_in, HitInfo& rec, ray& scattered) const { + // Return in case calculating a full simulation becomes impossible or irrelevant + BSDFSample absorbed; absorbed.scatter = false; + + ray r = r_in; + HitInfo rec_manip = rec; + vec3 outward_normal = rec.front_face ? rec.normal : -rec.normal; + + bool on_top = rec_manip.front_face; + rec_manip.front_face ? rec_manip.normal : -rec_manip.normal; + + std::shared_ptr current = on_top ? top : bottom; + BSDFSample bs = current->sample(r, rec_manip, scattered); + + color f = bs.bsdf_value; + float pdf = bs.pdf_value; + + float distance_in_from_top = rec.front_face ? 0.0 : 1.0; // amount of distance ray has traveled into between the layers + bool prev_medium = false; // previous hit was a medium + + if (!bs.scatter) { return absorbed; } + if (bs.type != BSDF_TYPE::TRANSMISSION && bs.type != BSDF_TYPE::TRANSPARENT) { + bs.type = (dot(bs.scatter_direction, rec.normal) < 0) ? BSDF_TYPE::TRANSMISSION : BSDF_TYPE::SPECULAR; + return bs; + } + for (int depth = 0; depth < termination; depth++) { + + // Follow random walk through layers to sample layered BSDF + on_top = !on_top; + current = on_top ? top : bottom; + + // Possibly terminate layered BSDF sampling with Russian Roulette + float rrBeta = fmax(fmax(f.x(), f.y()), f.z()) / bs.pdf_value; + if (depth > 3 && rrBeta < 0.50) { + float q = fmax(0, 1-rrBeta); + if (random_double() < q) { + if (on_top == rec.front_face) { + bs.type = (dot(bs.scatter_direction, rec.normal) < 0) ? BSDF_TYPE::TRANSMISSION : BSDF_TYPE::SPECULAR; + return bs; + } + } + pdf *= 1 - q; + } + + if (!prev_medium) { f = f * fabs(dot(rec_manip.normal, (bs.scatter_direction))); } + prev_medium = false; + r = ray(rec_manip.pos - bs.scatter_direction, bs.scatter_direction, r.time()); + + if (medium) { + float distance = medium->particleDistance(); + float distance_to_barrier; + if (on_top) { distance_to_barrier = distance_in_from_top; } + else { distance_to_barrier = thickness - distance_in_from_top; } + if (distance < distance_to_barrier) { // collide with particle + distance_in_from_top += (on_top * -distance) + (!on_top * distance); + bs = medium->phase->sample(r, rec_manip, scattered); + prev_medium = true; + + f = f * bs.bsdf_value; + pdf = pdf * bs.pdf_value; + bs.bsdf_value = f; + bs.pdf_value = pdf; + + if (dot(bs.scatter_direction, outward_normal) > 0) { + on_top = false; // because it will flip at the start of the next loop + rec_manip.front_face = false; + rec_manip.normal = -outward_normal; + } else { + on_top = true; // because it will flip at the start of the next loop + rec_manip.front_face = true; + rec_manip.normal = outward_normal; + } + + continue; + } else { + if (on_top) { distance_in_from_top = 0.0;} else { distance_in_from_top = 1.0; } + } + } + + bs = current->sample(r, rec_manip, scattered); + f = f * bs.bsdf_value; + pdf = pdf * bs.pdf_value; + bs.bsdf_value = f; + bs.pdf_value = pdf; + + if (on_top && bs.type == BSDF_TYPE::TRANSMISSION) { + bs.type = (dot(bs.scatter_direction, rec.normal) < 0) ? BSDF_TYPE::TRANSMISSION : BSDF_TYPE::SPECULAR; + return bs; + } + + // Flip since coming from the bottom! + rec_manip.front_face = false; + rec_manip.normal = -rec_manip.normal; + } + bs.type = (dot(bs.scatter_direction, rec.normal) < 0) ? BSDF_TYPE::TRANSMISSION : BSDF_TYPE::SPECULAR; + return bs; +} \ No newline at end of file From e59cfc8c90c9af60a56732ff237c3923ed9401bd Mon Sep 17 00:00:00 2001 From: connortbot Date: Thu, 19 Sep 2024 19:29:45 -0400 Subject: [PATCH 089/142] Update main.cc --- main.cc | 3 --- 1 file changed, 3 deletions(-) diff --git a/main.cc b/main.cc index 01f3cd2..8ff9553 100644 --- a/main.cc +++ b/main.cc @@ -83,10 +83,7 @@ void brdf_tests() { } int main(int argc, char* argv[]) { - // LUT_test(); - // measurePerformance(); brdf_tests(); - // Config config = parseArguments(argc, argv); // RenderData render_data; From 26a1b79e3a67e3e933916c36ed1df6f42212f149 Mon Sep 17 00:00:00 2001 From: connortbot Date: Thu, 19 Sep 2024 22:53:08 -0400 Subject: [PATCH 090/142] CA-124 note on black artifacts --- include/materials/material.h | 6 +++++- src/materials/material.cc | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/materials/material.h b/include/materials/material.h index e5e6cd1..1124c43 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -786,7 +786,11 @@ class MixtureBSDF : public material { * @param termination Russian Roulette termination condition for number of bounces. If exceeded, pretends light is absorbed. * * @note SCATTER, GENERATE, AND PDF DO NOT CONTAIN NECESSARY RUSSIAN ROULETTE OR LOGARITHMIC ACCUMULATION. THEY ARE NOT READY. - * Use sample instead. + * USE SAMPLE INSTEAD. + * + * @bug Using mediums will cause black artifacts that increase as samples increase. This is likely due to amount of bounces and loss of energy. + * This also occurs on a much lower scale without mediums, and is greatly remedied by Russian Roulette termination. But it is not perfect, and a better + * solution should be found! */ class LayeredBSDF : public material { public: diff --git a/src/materials/material.cc b/src/materials/material.cc index 6c5d1c0..e5f70b7 100644 --- a/src/materials/material.cc +++ b/src/materials/material.cc @@ -35,7 +35,7 @@ BSDFSample LayeredBSDF::sample(const ray& r_in, HitInfo& rec, ray& scattered) co // Possibly terminate layered BSDF sampling with Russian Roulette float rrBeta = fmax(fmax(f.x(), f.y()), f.z()) / bs.pdf_value; - if (depth > 3 && rrBeta < 0.50) { + if (depth > 3 && rrBeta < 0.25) { // rrBeta < 0.50 reduces by more, but probably reduces accuracy float q = fmax(0, 1-rrBeta); if (random_double() < q) { if (on_top == rec.front_face) { From 4399d9de7b1624b54dfcf01ebf4fe21f4e73f147 Mon Sep 17 00:00:00 2001 From: connortbot Date: Thu, 19 Sep 2024 23:24:06 -0400 Subject: [PATCH 091/142] CA-124 fix incorerct front face swtich in layered --- src/materials/material.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/materials/material.cc b/src/materials/material.cc index e5f70b7..0335bf7 100644 --- a/src/materials/material.cc +++ b/src/materials/material.cc @@ -11,7 +11,6 @@ BSDFSample LayeredBSDF::sample(const ray& r_in, HitInfo& rec, ray& scattered) co vec3 outward_normal = rec.front_face ? rec.normal : -rec.normal; bool on_top = rec_manip.front_face; - rec_manip.front_face ? rec_manip.normal : -rec_manip.normal; std::shared_ptr current = on_top ? top : bottom; BSDFSample bs = current->sample(r, rec_manip, scattered); @@ -87,13 +86,13 @@ BSDFSample LayeredBSDF::sample(const ray& r_in, HitInfo& rec, ray& scattered) co bs.bsdf_value = f; bs.pdf_value = pdf; - if (on_top && bs.type == BSDF_TYPE::TRANSMISSION) { + if (bs.type == BSDF_TYPE::TRANSMISSION) { bs.type = (dot(bs.scatter_direction, rec.normal) < 0) ? BSDF_TYPE::TRANSMISSION : BSDF_TYPE::SPECULAR; return bs; } // Flip since coming from the bottom! - rec_manip.front_face = false; + rec_manip.front_face = !rec_manip.front_face; rec_manip.normal = -rec_manip.normal; } bs.type = (dot(bs.scatter_direction, rec.normal) < 0) ? BSDF_TYPE::TRANSMISSION : BSDF_TYPE::SPECULAR; From 0e37fb2a8f5eec88638fb8a6881e1a6ab9fb561f Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 20 Sep 2024 00:31:13 -0400 Subject: [PATCH 092/142] CA-89 splitting into cc + documentation --- include/materials/material.h | 40 ++++++++++++------------------------ src/materials/material.cc | 22 ++++++++++++++++++++ 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/include/materials/material.h b/include/materials/material.h index 1124c43..cecf3f2 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -34,36 +34,19 @@ struct BSDFSample { class material { public: - virtual color emitted(double u, double v, const point3& p) const { - return color(0,0,0); - } - - virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { - return true; - } - virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { - return color(0,0,0); - } - virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { - return 1.0; - }; - - virtual BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const { - BSDFSample sample_data; - // Sample the microfacet distribution to get the scatter direction. - color attenuation; // placeholder until it gets removed from the scatter function header - sample_data.scatter = scatter(r_in, rec, attenuation, scattered); - sample_data.scatter_direction = scattered.direction().unit_vector(); + virtual color emitted(double u, double v, const point3& p) const; + virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const; + virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const; + virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const; - // Sample the BRDF for the value - sample_data.bsdf_value = generate(r_in, scattered, rec); - - // Find the PDF for the MDF - sample_data.pdf_value = pdf(r_in, scattered, rec); - return sample_data; - } + virtual BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const; }; +/** + * @class lambertian + * @brief Implements basic lambertian material with cosine direction sampling. + * @deprecated Use Oren-Nayar for diffuse if possible. At some point, CSR schema should use Diffuse and defualt to Oren-Nayar anyways. +*/ class lambertian : public material { public: @@ -93,6 +76,9 @@ class lambertian : public material { shared_ptr albedo; }; + + + class metal : public material { public: diff --git a/src/materials/material.cc b/src/materials/material.cc index 0335bf7..f117a2e 100644 --- a/src/materials/material.cc +++ b/src/materials/material.cc @@ -2,6 +2,28 @@ #include "medium.h" +color material::emitted(double u, double v, const point3& p) const { return color(0,0,0); } +bool material::scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { return true; } +color material::generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { return color(0,0,0); } +double material::pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { return 1.0; }; + +// Default behaviour for sample unless overriden by material +BSDFSample material::sample(const ray& r_in, HitInfo& rec, ray& scattered) const { + BSDFSample sample_data; + // Sample the microfacet distribution to get the scatter direction. + color attenuation; // placeholder until it gets removed from the scatter function header + sample_data.scatter = scatter(r_in, rec, attenuation, scattered); + sample_data.scatter_direction = scattered.direction().unit_vector(); + + // Sample the BRDF for the value + sample_data.bsdf_value = generate(r_in, scattered, rec); + + // Find the PDF for the MDF + sample_data.pdf_value = pdf(r_in, scattered, rec); + return sample_data; +} + + BSDFSample LayeredBSDF::sample(const ray& r_in, HitInfo& rec, ray& scattered) const { // Return in case calculating a full simulation becomes impossible or irrelevant BSDFSample absorbed; absorbed.scatter = false; From f38cfc907ed3ff2702281faa0d5f6acccdb1466c Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 20 Sep 2024 00:35:19 -0400 Subject: [PATCH 093/142] CA-89 docs for future deprecated materials --- include/materials/material.h | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/include/materials/material.h b/include/materials/material.h index cecf3f2..9653961 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -77,8 +77,14 @@ class lambertian : public material { }; - - +/** + * @class metal + * @brief Implements simple coloured perfect specular with fuzz, which is done by randomly warping output direction. Is not physically accurate + * since fuzz is not taken into account in f or pdf. Fresnel is not used. + * + * @deprecated Use CookTorrance instead. CSR should at some point use Metal, which is CookTorrance and NOT this class. + * +*/ class metal : public material { public: @@ -125,6 +131,14 @@ class metal : public material { double fuzz; }; +/** + * @class dielectric + * @brief Implements simple coloured perfect dielectric (without implementing roughness). Is physically incorrect, does not use fresnel or + * any worthwhile techniques. + * + * @deprecated Use CookTorranceDielectric instead. CSR should at some point use Transmission, which is CookTorranceDielectric and NOT this class. + * +*/ class dielectric : public material { public: From e75ea6dd5431ecb0b0f6e4d4412baeb9302b6c23 Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 20 Sep 2024 00:38:57 -0400 Subject: [PATCH 094/142] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index edf17b5..bd1b399 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ Flags like `--samples` and `--depth` control the amount of time spent on the ren Sometimes, CSR files will have features not supported in your version of `caitlyn`. You can check this with the version indicator at the top of the CSR file and with `./caitlyn --version`. For users who have a better understanding of their computer's resources, the `--threads` and `--vectorization` flags control the use of more efficient architecture. While `threads` dictate the amount of CPU threads to split the workloads on, the `vectorization` flag will dictate the type of SIMD batching. `[NONE|SSE|AVX|AVX512]`. +However, its important to note that this is increasingly untested with the new pathtracer. It is currently DEPRECATED. Use at own risk. ## Contribute From 34a5b7633d5a1cce36a72debeff07f18ac71ad16 Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 20 Sep 2024 01:34:34 -0400 Subject: [PATCH 095/142] CA-89 splitting material header --- include/materials/material.h | 193 +++-------------------------------- src/materials/material.cc | 106 +++++++++++++++++++ 2 files changed, 118 insertions(+), 181 deletions(-) diff --git a/include/materials/material.h b/include/materials/material.h index 9653961..44e82ed 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -224,46 +224,11 @@ class OrenNayar : public material { public: OrenNayar(color albedo, float roughness) : albedo{albedo}, roughness{roughness} {} - virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const override { - onb uvw; - uvw.build_from_w(rec.normal); - auto scatter_direction = uvw.local(random_cosine_direction()); - scattered = ray(rec.pos, scatter_direction, r_in.time()); - - return true; - } - - virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { - vec3 w_i = scattered.direction().unit_vector(); - vec3 w_o = -(r_in.direction().unit_vector()); - - // Calculate azimuthal angles. - vec3 projected_i = (w_i - (dot(w_i, rec.normal) * rec.normal)).unit_vector(); - vec3 projected_o = (w_o - (dot(w_o, rec.normal) * rec.normal)).unit_vector(); - float cos_azimuth = dot(projected_i, projected_o); - - - float theta_i = acos(dot(w_i, rec.normal)); - float theta_o = acos(dot(w_o, rec.normal)); - - float sigma2 = roughness * roughness; - float A = 1 - (sigma2 / (2 * (sigma2 + 0.33))); + virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const override; - float B = (0.45 * sigma2) / (sigma2 + 0.09); + virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const override; - float alpha = fmax(theta_i, theta_o); - float beta = fmin(theta_i, theta_o); - - color diffuse_term = albedo / pi; - - - return diffuse_term * (A + B * (fmax(0, cos_azimuth) * sin(alpha) * tan(beta))); - } - - virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { - auto cos_theta = dot(rec.normal, scattered.direction().unit_vector()); - return fmax(0.0, cos_theta / pi); - } + virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const override; private: color albedo; @@ -641,16 +606,9 @@ class isotropic : public material { isotropic(const color& albedo) : albedo{albedo} {} - virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { - scattered = ray(rec.pos, random_unit_vector(), r_in.time()); - return true; - } - virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { - return albedo / (4 * pi); - } - virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { - return 1 / (4 * pi); - }; + virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const; + virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const; + virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const; }; @@ -659,52 +617,10 @@ class pixel_lambertian : public material { public: pixel_lambertian(shared_ptr a) : albedo(a) {} - virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const override { - float t = random_double(); - color4 val = albedo->value(rec.u, rec.v); - if (t > val.A) { // transparent - rec.transparent = true; - scattered = ray(rec.pos, r_in.direction(), 0.0); - } else { - onb uvw; - uvw.build_from_w(rec.normal); - auto scatter_direction = uvw.local(random_cosine_direction()); - scattered = ray(rec.pos, scatter_direction, r_in.time()); - } - - - return true; - } - - virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { - if (!rec.transparent) { - return albedo->value(rec.u, rec.v).RGB / pi; - } else { - return color(1.0, 1.0, 1.0) / pi; - } - } - - virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { - auto cos_theta = dot(rec.normal, scattered.direction().unit_vector()); - if (!rec.transparent) { return fmax(0.0, cos_theta / pi); } - else { return fabs(cos_theta) / pi; } - } - - virtual BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const { - BSDFSample sample_data; - // Sample the microfacet distribution to get the scatter direction. - color attenuation; // placeholder until it gets removed from the scatter function header - sample_data.scatter = scatter(r_in, rec, attenuation, scattered); - sample_data.scatter_direction = scattered.direction().unit_vector(); - - // Sample the BRDF for the value - sample_data.bsdf_value = generate(r_in, scattered, rec); - - // Find the PDF for the MDF - sample_data.pdf_value = pdf(r_in, scattered, rec); - if (rec.transparent) { sample_data.type = BSDF_TYPE::TRANSPARENT; } - return sample_data; - } + virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const override; + virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const override; + virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const override; + virtual BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const; private: shared_ptr albedo; @@ -798,99 +714,14 @@ class LayeredBSDF : public material { : top{top}, bottom{bottom}, medium{medium}, termination{termination} {} virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { - ray r = r_in; - HitInfo rec_manip = rec; - - bool on_top = rec_manip.front_face; - rec_manip.front_face ? rec_manip.normal : -rec_manip.normal; - - std::shared_ptr current = on_top ? top : bottom; - BSDFSample bs = current->sample(r, rec_manip, scattered); - - if (!bs.scatter) { return false; } - if (bs.type != BSDF_TYPE::TRANSMISSION && bs.type != BSDF_TYPE::TRANSPARENT) { return true; } - for (int depth = 0; depth < termination; depth++) { - - on_top = !on_top; - current = on_top ? top : bottom; - r = ray(rec_manip.pos - bs.scatter_direction, bs.scatter_direction, r.time()); - - bs = current->sample(r, rec_manip, scattered); - - if (on_top && bs.type == BSDF_TYPE::TRANSMISSION) { return true; } - - // Flip since coming from the bottom! - rec_manip.front_face = false; - rec_manip.normal = -rec_manip.normal; - } - return false; + return true; } virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { - - ray r = r_in; - HitInfo rec_manip = rec; - - bool on_top = rec_manip.front_face; - rec_manip.front_face ? rec_manip.normal : -rec_manip.normal; - - ray scattered_copy = scattered; - std::shared_ptr current = on_top ? top : bottom; - BSDFSample bs = current->sample(r, rec_manip, scattered_copy); - color f = bs.bsdf_value; - - if (!bs.scatter) { return color(1.0, 1.0, 1.0); } - if (bs.type != BSDF_TYPE::TRANSMISSION && bs.type != BSDF_TYPE::TRANSPARENT) { return f; } - for (int depth = 0; depth < termination; depth++) { - - on_top = !on_top; - current = on_top ? top : bottom; - f = f * fabs(dot(rec_manip.normal, (bs.scatter_direction))); - r = ray(rec_manip.pos - bs.scatter_direction, bs.scatter_direction, r.time()); - - bs = current->sample(r, rec_manip, scattered_copy); - f = f * bs.bsdf_value; - - if (on_top && bs.type == BSDF_TYPE::TRANSMISSION) { return f; } - - // Flip since coming from the bottom! - rec_manip.front_face = false; - rec_manip.normal = -rec_manip.normal; - } - return color(1.0, 1.0, 1.0); + return color(1,1,1); } virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { - ray r = r_in; - HitInfo rec_manip = rec; - - bool on_top = rec_manip.front_face; - rec_manip.front_face ? rec_manip.normal : -rec_manip.normal; - - ray scattered_copy = scattered; - std::shared_ptr current = on_top ? top : bottom; - BSDFSample bs = current->sample(r, rec_manip, scattered_copy); - float pdf = bs.pdf_value; - - if (!bs.scatter) { return 1.0; } - if (bs.type != BSDF_TYPE::TRANSMISSION && bs.type != BSDF_TYPE::TRANSPARENT) { return pdf; } - for (int depth = 0; depth < termination; depth++) { - - on_top = !on_top; - current = on_top ? top : bottom; - r = ray(rec_manip.pos - bs.scatter_direction, bs.scatter_direction, r.time()); - - bs = current->sample(r, rec_manip, scattered_copy); - pdf = pdf * bs.pdf_value; - - if (on_top && bs.type == BSDF_TYPE::TRANSMISSION) { - return pdf; - } - - // Flip since coming from the bottom! - rec_manip.front_face = false; - rec_manip.normal = -rec_manip.normal; - } return 1.0; } diff --git a/src/materials/material.cc b/src/materials/material.cc index f117a2e..19c5fff 100644 --- a/src/materials/material.cc +++ b/src/materials/material.cc @@ -24,6 +24,112 @@ BSDFSample material::sample(const ray& r_in, HitInfo& rec, ray& scattered) const } +// ===================== OREN NAYAR =============================== +bool OrenNayar::scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { + onb uvw; + uvw.build_from_w(rec.normal); + auto scatter_direction = uvw.local(random_cosine_direction()); + scattered = ray(rec.pos, scatter_direction, r_in.time()); + + return true; +} + +color OrenNayar::generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { + vec3 w_i = scattered.direction().unit_vector(); + vec3 w_o = -(r_in.direction().unit_vector()); + + // Calculate azimuthal angles. + vec3 projected_i = (w_i - (dot(w_i, rec.normal) * rec.normal)).unit_vector(); + vec3 projected_o = (w_o - (dot(w_o, rec.normal) * rec.normal)).unit_vector(); + float cos_azimuth = dot(projected_i, projected_o); + + + float theta_i = acos(dot(w_i, rec.normal)); + float theta_o = acos(dot(w_o, rec.normal)); + + float sigma2 = roughness * roughness; + float A = 1 - (sigma2 / (2 * (sigma2 + 0.33))); + + float B = (0.45 * sigma2) / (sigma2 + 0.09); + + float alpha = fmax(theta_i, theta_o); + float beta = fmin(theta_i, theta_o); + + color diffuse_term = albedo / pi; + + + return diffuse_term * (A + B * (fmax(0, cos_azimuth) * sin(alpha) * tan(beta))); +} + +double OrenNayar::pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { + auto cos_theta = dot(rec.normal, scattered.direction().unit_vector()); + return fmax(0.0, cos_theta / pi); +} + +// ===================== COOK TORRANCE =============================== +// ===================== COOK DIELECTRIC =============================== + +// ===================== ISOTROPIC =============================== +bool isotropic::scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { + scattered = ray(rec.pos, random_unit_vector(), r_in.time()); + return true; +} +color isotropic::generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { + return albedo / (4 * pi); +} +double isotropic::pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { + return 1 / (4 * pi); +}; +// ===================== PIXEL LAMBERTIAN =============================== +bool pixel_lambertian::scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const{ + float t = random_double(); + color4 val = albedo->value(rec.u, rec.v); + if (t > val.A) { // transparent + rec.transparent = true; + scattered = ray(rec.pos, r_in.direction(), 0.0); + } else { + onb uvw; + uvw.build_from_w(rec.normal); + auto scatter_direction = uvw.local(random_cosine_direction()); + scattered = ray(rec.pos, scatter_direction, r_in.time()); + } + + + return true; +} + +color pixel_lambertian::generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { + if (!rec.transparent) { + return albedo->value(rec.u, rec.v).RGB / pi; + } else { + return color(1.0, 1.0, 1.0) / pi; + } +} + +double pixel_lambertian::pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { + auto cos_theta = dot(rec.normal, scattered.direction().unit_vector()); + if (!rec.transparent) { return fmax(0.0, cos_theta / pi); } + else { return fabs(cos_theta) / pi; } +} + +BSDFSample pixel_lambertian::sample(const ray& r_in, HitInfo& rec, ray& scattered) const { + BSDFSample sample_data; + // Sample the microfacet distribution to get the scatter direction. + color attenuation; // placeholder until it gets removed from the scatter function header + sample_data.scatter = scatter(r_in, rec, attenuation, scattered); + sample_data.scatter_direction = scattered.direction().unit_vector(); + + // Sample the BRDF for the value + sample_data.bsdf_value = generate(r_in, scattered, rec); + + // Find the PDF for the MDF + sample_data.pdf_value = pdf(r_in, scattered, rec); + if (rec.transparent) { sample_data.type = BSDF_TYPE::TRANSPARENT; } + return sample_data; +} + +// ===================== MIXTURE =============================== +// ===================== LAYERED =============================== BSDFSample LayeredBSDF::sample(const ray& r_in, HitInfo& rec, ray& scattered) const { // Return in case calculating a full simulation becomes impossible or irrelevant BSDFSample absorbed; absorbed.scatter = false; From 431e3f229b0f54685074f25e516301902cd28e19 Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 20 Sep 2024 01:48:46 -0400 Subject: [PATCH 096/142] CA-89 split of all non deprecated materials header --- include/materials/material.h | 358 +++-------------------------------- src/materials/material.cc | 342 +++++++++++++++++++++++++++++++++ 2 files changed, 364 insertions(+), 336 deletions(-) diff --git a/include/materials/material.h b/include/materials/material.h index 44e82ed..4ee0961 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -263,73 +263,10 @@ class CookTorrance : public material { CookTorrance(color absorption, color refraction, std::shared_ptr mdf) : complex(true), absorption_coefficient{absorption}, eta{refraction}, MDF{mdf} {} - virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const override { - vec3 microfacet_normal = MDF->sample(rec.normal); - rec.microfacet_normal = microfacet_normal; - vec3 scatter_direction = reflect(r_in.direction().unit_vector(), microfacet_normal); - scattered = ray(rec.pos, scatter_direction, r_in.time()); - - return (dot(scattered.direction(), rec.normal) > 0); - } - - virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { - vec3 L = scattered.direction().unit_vector(); - vec3 N = rec.normal; - vec3 V = -(r_in.direction().unit_vector()); - vec3 H = (V + L).unit_vector(); - - float NoV = clamp(dot(N, V), 0.0, 1.0); - float NoL = clamp(dot(N, L), 0.0, 1.0); - float NoH = clamp(dot(N, H), 0.0, 1.0); - float VoH = clamp(dot(V, H), 0.0, 1.0); - - vec3 f0 = albedo; vec3 F; - if (complex) { F = FrComplex(fabs(dot(V,rec.microfacet_normal)), absorption_coefficient, eta); } - else { F = fresnelSchlick(VoH, f0); } - - float D = MDF->D(NoH); - float G = MDF->G(NoV, NoL); - - vec3 spec = (F * D * G) / (4.0 * fmax(NoV, 0.001) * fmax(NoL, 0.001)); - - return spec; - } - - virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const override { - vec3 V = -r_in.direction().unit_vector(); - vec3 L = scattered.direction().unit_vector(); - vec3 H = (V + L).unit_vector(); - vec3 N = rec.normal; - - float NoH = clamp(dot(N, H), 0.0, 1.0); - float VoH = clamp(dot(V, H), 0.0, 1.0); - - float D = MDF->D(NoH); - // Convert D(N·H) to pdf based on the microfacet normal distribution. - // The Jacobian of the half-vector reflection transformation is |4 * (V·H)|. - // This accounts for the change in area density when mapping from H to L. - float jacobian = 4.0 * abs(dot(V, H)); - if (jacobian < 0.0001) return 0; - - return D / jacobian; - } - - virtual BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const override { - BSDFSample sample_data; - // Sample the microfacet distribution to get the scatter direction. - color attenuation; // placeholder until it gets removed from the scatter function header - sample_data.scatter = scatter(r_in, rec, attenuation, scattered); - sample_data.scatter_direction = scattered.direction().unit_vector(); - - // Sample the BRDF for the value - sample_data.bsdf_value = generate(r_in, scattered, rec); - - // Find the PDF for the MDF - sample_data.pdf_value = pdf(r_in, scattered, rec); - if (MDF->roughness < SPECULAR_ROUGHNESS_SAMPLING_CUTOFF) { sample_data.type = BSDF_TYPE::SPECULAR; } // 0.05 was picked arbitrarily, should experiment - else { sample_data.type = BSDF_TYPE::GLOSSY; } - return sample_data; - } + virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const override; + virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const override; + virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const override; + virtual BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const override; private: bool complex; @@ -339,31 +276,9 @@ class CookTorrance : public material { std::shared_ptr MDF; // F, G, D functions - vec3 fresnelSchlick(float cosTheta, vec3 F0) const { - return F0 + (color(1.0, 1.0, 1.0) - F0) * pow(1.0 - cosTheta, 5.0); - } - - float FrComplex(float cosTheta_i, std::complex eta) const { - using Complex = std::complex; - cosTheta_i = clamp(cosTheta_i, 0, 1); - float sin2Theta_i = 1 - (cosTheta_i * cosTheta_i); - Complex sin2Theta_t = sin2Theta_i / (eta * eta); - Complex val(1, -2); - Complex cosTheta_t = std::sqrt(val - sin2Theta_t); - - Complex r_parl = (eta * cosTheta_i - cosTheta_t) / - (eta * cosTheta_i + cosTheta_t); - Complex r_perp = (cosTheta_i - eta * cosTheta_t) / - (cosTheta_i + eta * cosTheta_t); - return (std::norm(r_parl) + std::norm(r_perp)) / 2; - } - - vec3 FrComplex(float cosTheta_v, vec3 k, vec3 eta) const { - float x = FrComplex(cosTheta_v, std::complex(eta.x(), k.x())); - float y = FrComplex(cosTheta_v, std::complex(eta.y(), k.y())); - float z = FrComplex(cosTheta_v, std::complex(eta.z(), k.z())); - return vec3(x,y,z); - } + vec3 fresnelSchlick(float cosTheta, vec3 F0) const; + float FrComplex(float cosTheta_i, std::complex eta) const; + vec3 FrComplex(float cosTheta_v, vec3 k, vec3 eta) const; }; @@ -386,106 +301,10 @@ class CookTorranceDielectric : public material { : albedo{albedo}, eta{(eta == 0.0f) ? 0.0f : (float)fmax(eta, 1.0001f)}, MDF{std::make_shared(roughness)}, complexFresnel{(int)fmax(complexFresnel, 0)} {} - virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { - vec3 wo = -r_in.direction().unit_vector(); - vec3 N = rec.normal; - vec3 wm = MDF->sample(N); // outward microfacet normal. - rec.microfacet_normal = wm; - - float cosTheta_i = dot(wo, wm); - float R; - if (complexFresnel == 0) { R = FrDielectric(cosTheta_i); } - else { R = fresnelSchlick(cosTheta_i, complexFresnel); } - float T = 1 - R; - - float u = random_double(); - double refraction_ratio = rec.front_face ? (1.0/eta) : eta; - double sinTheta_i = sqrt(1.0 - cosTheta_i*cosTheta_i); - - if (u < (R / (R + T)) || refraction_ratio * sinTheta_i > 1.0) { // reflectance - vec3 wi = reflect(-wo, wm); - scattered = ray(rec.pos, wi, r_in.time()); - } else { - vec3 wi = refract(-wo, wm, refraction_ratio); - scattered = ray(rec.pos, wi, r_in.time()); - } - return true; - } - virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { // assumes wm has been defined in rec - vec3 wo = -r_in.direction().unit_vector(); - vec3 N = rec.normal; - vec3 wi = scattered.direction(); - vec3 wm = rec.microfacet_normal; - float cosTheta_i = dot(wo, wm); - float R; - if (complexFresnel == 0) { R = FrDielectric(cosTheta_i); } - else { R = fresnelSchlick(cosTheta_i, complexFresnel); } - float T = 1 - R; - if (cosTheta_i > 0) { // reflectance - return f_r(r_in, rec, scattered, R); - } else { // refractance - return f_t(r_in, rec, scattered, T); - } - } - virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { // assumes wm has been defined in rec - vec3 wo = -r_in.direction().unit_vector(); - vec3 N = rec.normal; - vec3 wi = scattered.direction(); - vec3 wm = rec.microfacet_normal; - float cosTheta_i = dot(wo, wm); - float R; - if (complexFresnel == 0) { R = FrDielectric(cosTheta_i); } - else { R = fresnelSchlick(cosTheta_i, complexFresnel); } - float T = 1 - R; - if (cosTheta_i > 0) { // reflectance - return pdf_r(r_in, rec, scattered, R); - } else { // refractance - return pdf_t(r_in, rec, scattered, T); - } - }; - - BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const override { - // Vectors wo and wi are the outgoing and incident directions respectively. - vec3 wo = -r_in.direction().unit_vector(); - - BSDFSample sample_data; - vec3 N = rec.normal; - vec3 wm = MDF->sample(N); // outward microfacet normal. - rec.microfacet_normal = wm; - - float cosTheta_i = dot(wo, wm); - float R; - if (complexFresnel == 0) { R = FrDielectric(cosTheta_i); } - else { R = fresnelSchlick(cosTheta_i, complexFresnel); } - float T = 1 - R; - - float u = random_double(); - double refraction_ratio = rec.front_face ? (1.0/eta) : eta; - double sinTheta_i = sqrt(1.0 - cosTheta_i*cosTheta_i); - - if (u < (R / (R + T)) || refraction_ratio * sinTheta_i > 1.0) { // reflectance - vec3 wi = reflect(-wo, wm); - scattered = ray(rec.pos, wi, r_in.time()); - sample_data.scatter_direction = wi; - sample_data.scatter = (dot(wi, N) > 0); - - sample_data.bsdf_value = f_r(r_in, rec, scattered, R); - sample_data.pdf_value = pdf_r(r_in, rec, scattered, R); - if (MDF->roughness < SPECULAR_ROUGHNESS_SAMPLING_CUTOFF) { sample_data.type = BSDF_TYPE::SPECULAR; } // 0.05 was picked arbitrarily, should experiment - else { sample_data.type = BSDF_TYPE::GLOSSY; } - } else { // transmission - vec3 wi = refract(-wo, wm, refraction_ratio); - scattered = ray(rec.pos, wi, r_in.time()); - sample_data.scatter_direction = wi; - sample_data.scatter = (dot(wi, N) < 0); - - sample_data.bsdf_value = f_t(r_in, rec, scattered, T); - sample_data.pdf_value = pdf_t(r_in, rec, scattered, T); - sample_data.type = BSDF_TYPE::TRANSMISSION; - } - - return sample_data; - } + virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const; + virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const; + virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const; + BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const override; //private: color albedo; @@ -493,111 +312,16 @@ class CookTorranceDielectric : public material { std::shared_ptr MDF; int complexFresnel; - float FrDielectric(float cosTheta_i) const { - float temp_eta = eta; - cosTheta_i = clamp(cosTheta_i, -1.0, 1.0); - if (cosTheta_i < 0) { - temp_eta = 1 / eta; - cosTheta_i = -cosTheta_i; - } - - float sin2Theta_i = 1 - (cosTheta_i * cosTheta_i); - float sin2Theta_t = sin2Theta_i / (temp_eta * temp_eta); - if (sin2Theta_t >= 1.0) { - return 1.0; - } - float cosTheta_t = sqrt(1 - sin2Theta_t); - float r_parallel = (temp_eta * cosTheta_i - cosTheta_t) / (temp_eta * cosTheta_i + cosTheta_t); - float r_perp = (cosTheta_i - (temp_eta * cosTheta_t)) / (cosTheta_i + (eta * cosTheta_t)); - return ((r_parallel * r_parallel) + (r_perp * r_perp)) / 2; - } + float FrDielectric(float cosTheta_i) const; - float fresnelSchlick(float cosTheta, int exponent) const { - float F0 = pow(((1 - eta) / (1 + eta)), 2); - return F0 + (1.0 - F0) * pow(1.0 - cosTheta, exponent); - } + float fresnelSchlick(float cosTheta, int exponent) const; private: - color f_r(const ray& r_in, const HitInfo& rec, const ray& scattered, float R) const { - vec3 V = -r_in.direction().unit_vector(); - vec3 L = scattered.direction().unit_vector(); - vec3 H = (V + L).unit_vector(); - vec3 N = rec.normal; - - float NoH = clamp(dot(N, H), 0.0, 1.0); - float NoV = clamp(dot(N, V), 0.0, 1.0); - float NoL = clamp(dot(N, L), 0.0, 1.0); - - float D = MDF->D(NoH); - float G = MDF->G(NoV, NoL); - - color R_col = color(R, R, R) * albedo; - - color num = D * G * R_col; - float denom = (4.0 * fmax(NoV, 0.001) * fmax(NoL, 0.001)); - - return num / denom; - } - - float pdf_r(const ray& r_in, const HitInfo& rec, const ray& scattered, float R) const { - vec3 V = -r_in.direction().unit_vector(); - vec3 L = scattered.direction().unit_vector(); - vec3 H = (V + L).unit_vector(); - vec3 N = rec.normal; - - float NoH = clamp(dot(N, H), 0.0, 1.0); - float VoH = clamp(dot(V, H), 0.0, 1.0); - - float D = MDF->D(NoH); - // Convert D(N·H) to pdf based on the microfacet normal distribution. - // The Jacobian of the half-vector reflection transformation is |4 * (V·H)|. - // This accounts for the change in area density when mapping from H to L. - float jacobian = 4.0 * abs(dot(V, H)); - if (jacobian < 0.0001) return 0; - - return (D * R) / jacobian; - } - - float pdf_t(const ray& r_in, const HitInfo& rec, const ray& scattered, float T) const { - double etap = rec.front_face ? (1.0/eta) : eta; - - vec3 wo = -r_in.direction().unit_vector(); - vec3 wi = scattered.direction().unit_vector(); - vec3 wn = rec.normal; - vec3 wm = rec.microfacet_normal; - vec3 h = (wo + wi).unit_vector(); - - float denom = (dot(wi, wm) + dot(wo, wm) / etap) * (dot(wi, wm) + dot(wo, wm) / etap); - float dwm_dwi = fabs(dot(wi, wm)) / denom; - float NoM = dot(wm, wn); - float D = MDF->D(NoM); - return D * dwm_dwi * T; - } - - color f_t(const ray& r_in, const HitInfo& rec, const ray& scattered, float T) const { - double etap = rec.front_face ? (1.0/eta) : eta; - - vec3 wo = -r_in.direction().unit_vector(); - vec3 wi = scattered.direction().unit_vector(); - vec3 wn = rec.normal; - vec3 wm = rec.microfacet_normal; - vec3 h = (wo + wi).unit_vector(); - - float NoM = dot(wm, wn); - float NoO = dot(wn, wo); - float NoI = dot(wn, wi); - float D = MDF->D(NoM); - float G = MDF->G(fabs(NoO), fabs(NoI)); - color T_col = color(T, T, T) * albedo; - color num = D * G * T_col; - - float IoM = dot(wi, wm); - float OoM = dot(wo, wm); - float denom = (IoM + OoM / etap) * (IoM + OoM / etap); - float dotabs = fabs(IoM * OoM / (dot(wi, wn) * dot(wo, wn) * denom)); // 1: e+14, 2: inf - return num * dotabs; - } + color f_r(const ray& r_in, const HitInfo& rec, const ray& scattered, float R) const; + float pdf_r(const ray& r_in, const HitInfo& rec, const ray& scattered, float R) const; + float pdf_t(const ray& r_in, const HitInfo& rec, const ray& scattered, float T) const; + color f_t(const ray& r_in, const HitInfo& rec, const ray& scattered, float T) const; }; class isotropic : public material { @@ -641,55 +365,17 @@ class MixtureBSDF : public material { public: MixtureBSDF(std::vector weights, std::vector> mats) : weights{weights}, mats{mats} {} - virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { - rec.rand = random_double(); - int mat_ix = chooseSampleMaterial(rec.rand); - if (mat_ix >= 0) { // weights is valid - return mats[mat_ix]->scatter(r_in, rec, attenuation, scattered); - } else { - return false; - } - } - - // assumes that scatter has already been called or sample has already been called, and thus rand is already generated. - virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { - int mat_ix = chooseSampleMaterial(rec.rand); - if (mat_ix >= 0) { return mats[mat_ix]->generate(r_in, scattered, rec); - } else { return color(1,1,1); } - } - - virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { - int mat_ix = chooseSampleMaterial(rec.rand); - if (mat_ix >= 0) { return mats[mat_ix]->pdf(r_in, scattered, rec); - } else { return 1.0; } - } - - // NOTE: ASSUMES WEIGHTS IS AT LEAST OF SIZE 1 OTHERWISE BEHAVIOUR IS UNDEFINED - BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const override { - rec.rand = random_double(); - int mat_ix = chooseSampleMaterial(rec.rand); - if (mat_ix >= 0) { return mats[mat_ix]->sample(r_in, rec, scattered); - } else { - BSDFSample sample_data; - return sample_data; - } - } + virtual bool scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const; + virtual color generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const; // assumes that scatter has already been called or sample has already been called, and thus rand is already generated. + virtual double pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const; + BSDFSample sample(const ray& r_in, HitInfo& rec, ray& scattered) const override; // NOTE: ASSUMES WEIGHTS IS AT LEAST OF SIZE 1 OTHERWISE BEHAVIOUR IS UNDEFINED private: std::vector weights; std::vector> mats; // Returns negative if weights vector has nothing. - int chooseSampleMaterial(float rand) const { - float cumulative_weight = 0.0f; - for (size_t i = 0; i < weights.size(); i++) { - cumulative_weight += weights[i]; - if (rand < cumulative_weight) { - return i; - } - } - return (int)weights.size() - 1; - } + int chooseSampleMaterial(float rand) const; }; /** diff --git a/src/materials/material.cc b/src/materials/material.cc index 19c5fff..7dc6d64 100644 --- a/src/materials/material.cc +++ b/src/materials/material.cc @@ -67,8 +67,307 @@ double OrenNayar::pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) } // ===================== COOK TORRANCE =============================== + +bool CookTorrance::scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { + vec3 microfacet_normal = MDF->sample(rec.normal); + rec.microfacet_normal = microfacet_normal; + vec3 scatter_direction = reflect(r_in.direction().unit_vector(), microfacet_normal); + scattered = ray(rec.pos, scatter_direction, r_in.time()); + + return (dot(scattered.direction(), rec.normal) > 0); +} + +color CookTorrance::generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { + vec3 L = scattered.direction().unit_vector(); + vec3 N = rec.normal; + vec3 V = -(r_in.direction().unit_vector()); + vec3 H = (V + L).unit_vector(); + + float NoV = clamp(dot(N, V), 0.0, 1.0); + float NoL = clamp(dot(N, L), 0.0, 1.0); + float NoH = clamp(dot(N, H), 0.0, 1.0); + float VoH = clamp(dot(V, H), 0.0, 1.0); + + vec3 f0 = albedo; vec3 F; + if (complex) { F = FrComplex(fabs(dot(V,rec.microfacet_normal)), absorption_coefficient, eta); } + else { F = fresnelSchlick(VoH, f0); } + + float D = MDF->D(NoH); + float G = MDF->G(NoV, NoL); + + vec3 spec = (F * D * G) / (4.0 * fmax(NoV, 0.001) * fmax(NoL, 0.001)); + + return spec; +} + +double CookTorrance::pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { + vec3 V = -r_in.direction().unit_vector(); + vec3 L = scattered.direction().unit_vector(); + vec3 H = (V + L).unit_vector(); + vec3 N = rec.normal; + + float NoH = clamp(dot(N, H), 0.0, 1.0); + float VoH = clamp(dot(V, H), 0.0, 1.0); + + float D = MDF->D(NoH); + // Convert D(N·H) to pdf based on the microfacet normal distribution. + // The Jacobian of the half-vector reflection transformation is |4 * (V·H)|. + // This accounts for the change in area density when mapping from H to L. + float jacobian = 4.0 * abs(dot(V, H)); + if (jacobian < 0.0001) return 0; + + return D / jacobian; +} + +BSDFSample CookTorrance::sample(const ray& r_in, HitInfo& rec, ray& scattered) const { + BSDFSample sample_data; + // Sample the microfacet distribution to get the scatter direction. + color attenuation; // placeholder until it gets removed from the scatter function header + sample_data.scatter = scatter(r_in, rec, attenuation, scattered); + sample_data.scatter_direction = scattered.direction().unit_vector(); + + // Sample the BRDF for the value + sample_data.bsdf_value = generate(r_in, scattered, rec); + + // Find the PDF for the MDF + sample_data.pdf_value = pdf(r_in, scattered, rec); + if (MDF->roughness < SPECULAR_ROUGHNESS_SAMPLING_CUTOFF) { sample_data.type = BSDF_TYPE::SPECULAR; } // 0.05 was picked arbitrarily, should experiment + else { sample_data.type = BSDF_TYPE::GLOSSY; } + return sample_data; +} + +vec3 CookTorrance::fresnelSchlick(float cosTheta, vec3 F0) const { + return F0 + (color(1.0, 1.0, 1.0) - F0) * pow(1.0 - cosTheta, 5.0); +} + +float CookTorrance::FrComplex(float cosTheta_i, std::complex eta) const { + using Complex = std::complex; + cosTheta_i = clamp(cosTheta_i, 0, 1); + float sin2Theta_i = 1 - (cosTheta_i * cosTheta_i); + Complex sin2Theta_t = sin2Theta_i / (eta * eta); + Complex val(1, -2); + Complex cosTheta_t = std::sqrt(val - sin2Theta_t); + + Complex r_parl = (eta * cosTheta_i - cosTheta_t) / + (eta * cosTheta_i + cosTheta_t); + Complex r_perp = (cosTheta_i - eta * cosTheta_t) / + (cosTheta_i + eta * cosTheta_t); + return (std::norm(r_parl) + std::norm(r_perp)) / 2; +} + +vec3 CookTorrance::FrComplex(float cosTheta_v, vec3 k, vec3 eta) const { + float x = FrComplex(cosTheta_v, std::complex(eta.x(), k.x())); + float y = FrComplex(cosTheta_v, std::complex(eta.y(), k.y())); + float z = FrComplex(cosTheta_v, std::complex(eta.z(), k.z())); + return vec3(x,y,z); +} + // ===================== COOK DIELECTRIC =============================== +bool CookTorranceDielectric::scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { + vec3 wo = -r_in.direction().unit_vector(); + vec3 N = rec.normal; + vec3 wm = MDF->sample(N); // outward microfacet normal. + rec.microfacet_normal = wm; + + float cosTheta_i = dot(wo, wm); + float R; + if (complexFresnel == 0) { R = FrDielectric(cosTheta_i); } + else { R = fresnelSchlick(cosTheta_i, complexFresnel); } + float T = 1 - R; + + float u = random_double(); + double refraction_ratio = rec.front_face ? (1.0/eta) : eta; + double sinTheta_i = sqrt(1.0 - cosTheta_i*cosTheta_i); + + if (u < (R / (R + T)) || refraction_ratio * sinTheta_i > 1.0) { // reflectance + vec3 wi = reflect(-wo, wm); + scattered = ray(rec.pos, wi, r_in.time()); + } else { + vec3 wi = refract(-wo, wm, refraction_ratio); + scattered = ray(rec.pos, wi, r_in.time()); + } + return true; +} +color CookTorranceDielectric::generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { // assumes wm has been defined in rec + vec3 wo = -r_in.direction().unit_vector(); + vec3 N = rec.normal; + vec3 wi = scattered.direction(); + vec3 wm = rec.microfacet_normal; + float cosTheta_i = dot(wo, wm); + float R; + if (complexFresnel == 0) { R = FrDielectric(cosTheta_i); } + else { R = fresnelSchlick(cosTheta_i, complexFresnel); } + float T = 1 - R; + if (cosTheta_i > 0) { // reflectance + return f_r(r_in, rec, scattered, R); + } else { // refractance + return f_t(r_in, rec, scattered, T); + } +} +double CookTorranceDielectric::pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { // assumes wm has been defined in rec + vec3 wo = -r_in.direction().unit_vector(); + vec3 N = rec.normal; + vec3 wi = scattered.direction(); + vec3 wm = rec.microfacet_normal; + float cosTheta_i = dot(wo, wm); + float R; + if (complexFresnel == 0) { R = FrDielectric(cosTheta_i); } + else { R = fresnelSchlick(cosTheta_i, complexFresnel); } + float T = 1 - R; + if (cosTheta_i > 0) { // reflectance + return pdf_r(r_in, rec, scattered, R); + } else { // refractance + return pdf_t(r_in, rec, scattered, T); + } +}; + +BSDFSample CookTorranceDielectric::sample(const ray& r_in, HitInfo& rec, ray& scattered) const { + // Vectors wo and wi are the outgoing and incident directions respectively. + vec3 wo = -r_in.direction().unit_vector(); + + BSDFSample sample_data; + vec3 N = rec.normal; + vec3 wm = MDF->sample(N); // outward microfacet normal. + rec.microfacet_normal = wm; + + float cosTheta_i = dot(wo, wm); + float R; + if (complexFresnel == 0) { R = FrDielectric(cosTheta_i); } + else { R = fresnelSchlick(cosTheta_i, complexFresnel); } + float T = 1 - R; + + float u = random_double(); + double refraction_ratio = rec.front_face ? (1.0/eta) : eta; + double sinTheta_i = sqrt(1.0 - cosTheta_i*cosTheta_i); + + if (u < (R / (R + T)) || refraction_ratio * sinTheta_i > 1.0) { // reflectance + vec3 wi = reflect(-wo, wm); + scattered = ray(rec.pos, wi, r_in.time()); + sample_data.scatter_direction = wi; + sample_data.scatter = (dot(wi, N) > 0); + + sample_data.bsdf_value = f_r(r_in, rec, scattered, R); + sample_data.pdf_value = pdf_r(r_in, rec, scattered, R); + if (MDF->roughness < SPECULAR_ROUGHNESS_SAMPLING_CUTOFF) { sample_data.type = BSDF_TYPE::SPECULAR; } // 0.05 was picked arbitrarily, should experiment + else { sample_data.type = BSDF_TYPE::GLOSSY; } + } else { // transmission + vec3 wi = refract(-wo, wm, refraction_ratio); + scattered = ray(rec.pos, wi, r_in.time()); + sample_data.scatter_direction = wi; + sample_data.scatter = (dot(wi, N) < 0); + + sample_data.bsdf_value = f_t(r_in, rec, scattered, T); + sample_data.pdf_value = pdf_t(r_in, rec, scattered, T); + sample_data.type = BSDF_TYPE::TRANSMISSION; + } + + return sample_data; +} + +float CookTorranceDielectric::FrDielectric(float cosTheta_i) const { + float temp_eta = eta; + cosTheta_i = clamp(cosTheta_i, -1.0, 1.0); + if (cosTheta_i < 0) { + temp_eta = 1 / eta; + cosTheta_i = -cosTheta_i; + } + + float sin2Theta_i = 1 - (cosTheta_i * cosTheta_i); + float sin2Theta_t = sin2Theta_i / (temp_eta * temp_eta); + if (sin2Theta_t >= 1.0) { + return 1.0; + } + float cosTheta_t = sqrt(1 - sin2Theta_t); + float r_parallel = (temp_eta * cosTheta_i - cosTheta_t) / (temp_eta * cosTheta_i + cosTheta_t); + float r_perp = (cosTheta_i - (temp_eta * cosTheta_t)) / (cosTheta_i + (eta * cosTheta_t)); + return ((r_parallel * r_parallel) + (r_perp * r_perp)) / 2; +} + +float CookTorranceDielectric::fresnelSchlick(float cosTheta, int exponent) const { + float F0 = pow(((1 - eta) / (1 + eta)), 2); + return F0 + (1.0 - F0) * pow(1.0 - cosTheta, exponent); +} + +color CookTorranceDielectric::f_r(const ray& r_in, const HitInfo& rec, const ray& scattered, float R) const { + vec3 V = -r_in.direction().unit_vector(); + vec3 L = scattered.direction().unit_vector(); + vec3 H = (V + L).unit_vector(); + vec3 N = rec.normal; + + float NoH = clamp(dot(N, H), 0.0, 1.0); + float NoV = clamp(dot(N, V), 0.0, 1.0); + float NoL = clamp(dot(N, L), 0.0, 1.0); + + float D = MDF->D(NoH); + float G = MDF->G(NoV, NoL); + + color R_col = color(R, R, R) * albedo; + + color num = D * G * R_col; + float denom = (4.0 * fmax(NoV, 0.001) * fmax(NoL, 0.001)); + + return num / denom; +} + +float CookTorranceDielectric::pdf_r(const ray& r_in, const HitInfo& rec, const ray& scattered, float R) const { + vec3 V = -r_in.direction().unit_vector(); + vec3 L = scattered.direction().unit_vector(); + vec3 H = (V + L).unit_vector(); + vec3 N = rec.normal; + + float NoH = clamp(dot(N, H), 0.0, 1.0); + float VoH = clamp(dot(V, H), 0.0, 1.0); + + float D = MDF->D(NoH); + // Convert D(N·H) to pdf based on the microfacet normal distribution. + // The Jacobian of the half-vector reflection transformation is |4 * (V·H)|. + // This accounts for the change in area density when mapping from H to L. + float jacobian = 4.0 * abs(dot(V, H)); + if (jacobian < 0.0001) return 0; + + return (D * R) / jacobian; +} + +float CookTorranceDielectric::pdf_t(const ray& r_in, const HitInfo& rec, const ray& scattered, float T) const { + double etap = rec.front_face ? (1.0/eta) : eta; + + vec3 wo = -r_in.direction().unit_vector(); + vec3 wi = scattered.direction().unit_vector(); + vec3 wn = rec.normal; + vec3 wm = rec.microfacet_normal; + vec3 h = (wo + wi).unit_vector(); + + float denom = (dot(wi, wm) + dot(wo, wm) / etap) * (dot(wi, wm) + dot(wo, wm) / etap); + float dwm_dwi = fabs(dot(wi, wm)) / denom; + float NoM = dot(wm, wn); + float D = MDF->D(NoM); + return D * dwm_dwi * T; +} + +color CookTorranceDielectric::f_t(const ray& r_in, const HitInfo& rec, const ray& scattered, float T) const { + double etap = rec.front_face ? (1.0/eta) : eta; + + vec3 wo = -r_in.direction().unit_vector(); + vec3 wi = scattered.direction().unit_vector(); + vec3 wn = rec.normal; + vec3 wm = rec.microfacet_normal; + vec3 h = (wo + wi).unit_vector(); + + float NoM = dot(wm, wn); + float NoO = dot(wn, wo); + float NoI = dot(wn, wi); + float D = MDF->D(NoM); + float G = MDF->G(fabs(NoO), fabs(NoI)); + color T_col = color(T, T, T) * albedo; + color num = D * G * T_col; + + float IoM = dot(wi, wm); + float OoM = dot(wo, wm); + float denom = (IoM + OoM / etap) * (IoM + OoM / etap); + float dotabs = fabs(IoM * OoM / (dot(wi, wn) * dot(wo, wn) * denom)); // 1: e+14, 2: inf + return num * dotabs; +} // ===================== ISOTROPIC =============================== bool isotropic::scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { scattered = ray(rec.pos, random_unit_vector(), r_in.time()); @@ -129,6 +428,49 @@ BSDFSample pixel_lambertian::sample(const ray& r_in, HitInfo& rec, ray& scattere } // ===================== MIXTURE =============================== +bool MixtureBSDF::scatter(const ray& r_in, HitInfo& rec, color& attenuation, ray& scattered) const { + rec.rand = random_double(); + int mat_ix = chooseSampleMaterial(rec.rand); + if (mat_ix >= 0) { // weights is valid + return mats[mat_ix]->scatter(r_in, rec, attenuation, scattered); + } else { + return false; + } +} + +// assumes that scatter has already been called or sample has already been called, and thus rand is already generated. +color MixtureBSDF::generate(const ray& r_in, const ray& scattered, const HitInfo& rec) const { + int mat_ix = chooseSampleMaterial(rec.rand); + if (mat_ix >= 0) { return mats[mat_ix]->generate(r_in, scattered, rec); + } else { return color(1,1,1); } +} + +double MixtureBSDF::pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { + int mat_ix = chooseSampleMaterial(rec.rand); + if (mat_ix >= 0) { return mats[mat_ix]->pdf(r_in, scattered, rec); + } else { return 1.0; } +} + +// NOTE: ASSUMES WEIGHTS IS AT LEAST OF SIZE 1 OTHERWISE BEHAVIOUR IS UNDEFINED +BSDFSample MixtureBSDF::sample(const ray& r_in, HitInfo& rec, ray& scattered) const { + rec.rand = random_double(); + int mat_ix = chooseSampleMaterial(rec.rand); + if (mat_ix >= 0) { return mats[mat_ix]->sample(r_in, rec, scattered); + } else { + BSDFSample sample_data; + return sample_data; + } +} +int MixtureBSDF::chooseSampleMaterial(float rand) const { + float cumulative_weight = 0.0f; + for (size_t i = 0; i < weights.size(); i++) { + cumulative_weight += weights[i]; + if (rand < cumulative_weight) { + return i; + } + } + return (int)weights.size() - 1; +} // ===================== LAYERED =============================== BSDFSample LayeredBSDF::sample(const ray& r_in, HitInfo& rec, ray& scattered) const { // Return in case calculating a full simulation becomes impossible or irrelevant From 1102c4026903c5462aeb80cf87bfe80387aa8e0e Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 24 Sep 2024 23:37:31 -0400 Subject: [PATCH 097/142] CA-89 change version to 0.1.5 --- src/parsers/cli_parser.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parsers/cli_parser.cc b/src/parsers/cli_parser.cc index c4f78b4..47e5ce0 100644 --- a/src/parsers/cli_parser.cc +++ b/src/parsers/cli_parser.cc @@ -109,7 +109,7 @@ Config parseArguments(int argc, char* argv[]) { else if(arg == "-v" || arg == "--version") { config.showVersion = true; - std::cout << "caitlyn version 0.1.3" << std::endl; + std::cout << "caitlyn version 0.1.5" << std::endl; } else if(arg == "-h" || arg == "--help") { From cfcfd65d0999a84d897a8481c61d6c257bb06118 Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 24 Sep 2024 23:40:27 -0400 Subject: [PATCH 098/142] CA-89 new Dielectric with CookTorranceDielectric! --- src/parsers/csr_parser.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/parsers/csr_parser.cc b/src/parsers/csr_parser.cc index fb3107e..b183640 100644 --- a/src/parsers/csr_parser.cc +++ b/src/parsers/csr_parser.cc @@ -53,9 +53,11 @@ std::shared_ptr CSRParser::parseCSR(std::string& filePath, RTCDevice devi getNextLine(file, materialId); getNextLine(file, albedo); getNextLine(file, fuzz); materials[readStringProperty(materialId)] = std::make_shared(readXYZProperty(albedo), readDoubleProperty(fuzz)); } else if (materialType == "Dielectric") { - std::string materialId, ir; - getNextLine(file, materialId); getNextLine(file, ir); - materials[readStringProperty(materialId)] = std::make_shared(readDoubleProperty(ir)); + std::string materialId, albedo, eta, roughness, sheen; + getNextLine(file, materialId); getNextLine(file, albedo); getNextLine(file, eta); getNextLine(file, roughness); getNextLine(file, sheen); + materials[readStringProperty(materialId)] = std::make_shared( + readXYZProperty(albedo), readDoubleProperty(eta), readDoubleProperty(roughness), (int)readDoubleProperty(sheen) + ); } else if (materialType == "Emissive") { std::string materialId, rgb, strength; getNextLine(file, materialId); getNextLine(file, rgb); getNextLine(file, strength); From ed4370747a0e124f846b52700fbea1735c2c3490 Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 24 Sep 2024 23:40:36 -0400 Subject: [PATCH 099/142] Update material.h --- include/materials/material.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/materials/material.h b/include/materials/material.h index 4ee0961..5b35b8a 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -391,8 +391,10 @@ class MixtureBSDF : public material { * USE SAMPLE INSTEAD. * * @bug Using mediums will cause black artifacts that increase as samples increase. This is likely due to amount of bounces and loss of energy. - * This also occurs on a much lower scale without mediums, and is greatly remedied by Russian Roulette termination. But it is not perfect, and a better - * solution should be found! + * This also occurs on a much lower scale without mediums, and is partially remedied by: + * -> Russian Roulette termination. + * -> Setting a termination integer close to the default (e.g 20). + * But it is not perfect, and a better solution should be found! */ class LayeredBSDF : public material { public: From 26493e9f8cb28c7b858f8468cb08fd9b0d25df74 Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 24 Sep 2024 23:41:09 -0400 Subject: [PATCH 100/142] change testing scene --- main.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.cc b/main.cc index 8ff9553..0e3bccd 100644 --- a/main.cc +++ b/main.cc @@ -57,7 +57,7 @@ void brdf_tests() { // Example of LayeredBSDF: glass on diffuse with white cloud inside auto layered_iso = make_shared(color(0.1,0.8,0.1)); auto layered_medium = make_shared(0.1, layered_iso); - auto mt9 = make_shared(mt6, mt3, layered_medium, 200); + auto mt9 = make_shared(mt6, mt3, layered_medium, 10); // Adding 3 spheres auto sphere1 = make_shared(point3(0, 2, 2), mt5, 2, device); From 5b790bb42e48b331fc9cc66ab1981178f936ebf6 Mon Sep 17 00:00:00 2001 From: connortbot Date: Wed, 25 Sep 2024 01:42:36 -0400 Subject: [PATCH 101/142] CA-89 internal CSR support for CooKTorrance/OrenNayar --- src/parsers/csr_parser.cc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/parsers/csr_parser.cc b/src/parsers/csr_parser.cc index b183640..54c1a2d 100644 --- a/src/parsers/csr_parser.cc +++ b/src/parsers/csr_parser.cc @@ -32,14 +32,14 @@ std::shared_ptr CSRParser::parseCSR(std::string& filePath, RTCDevice devi auto idStart = line.find('[') + 1; auto idEnd = line.find(']'); std::string materialType = line.substr(idStart, idEnd - idStart); - if (materialType == "Lambertian") { + if (materialType == "Diffuse") { std::string materialId, texture; getNextLine(file, materialId); getNextLine(file, texture); std::string texture_id = readStringProperty(texture); if (texture_id == "no") { - std::string albedo; - getNextLine(file, albedo); - materials[readStringProperty(materialId)] = std::make_shared(readXYZProperty(albedo)); + std::string albedo, roughness; + getNextLine(file, albedo); getNextLine(file, roughness); + materials[readStringProperty(materialId)] = std::make_shared(readXYZProperty(albedo), readDoubleProperty(roughness)); } else { std::shared_ptr plamb = std::dynamic_pointer_cast(textures[texture_id]); if (plamb) { // texture is a pixel lambert @@ -49,9 +49,9 @@ std::shared_ptr CSRParser::parseCSR(std::string& filePath, RTCDevice devi } } } else if (materialType == "Metal") { - std::string materialId, albedo, fuzz; - getNextLine(file, materialId); getNextLine(file, albedo); getNextLine(file, fuzz); - materials[readStringProperty(materialId)] = std::make_shared(readXYZProperty(albedo), readDoubleProperty(fuzz)); + std::string materialId, albedo, roughness; + getNextLine(file, materialId); getNextLine(file, albedo); getNextLine(file, roughness); + materials[readStringProperty(materialId)] = std::make_shared(readXYZProperty(albedo), readDoubleProperty(roughness)); } else if (materialType == "Dielectric") { std::string materialId, albedo, eta, roughness, sheen; getNextLine(file, materialId); getNextLine(file, albedo); getNextLine(file, eta); getNextLine(file, roughness); getNextLine(file, sheen); From 79b3b2794f8a14977051e968c05f7f821f823276 Mon Sep 17 00:00:00 2001 From: connortbot Date: Wed, 25 Sep 2024 01:46:25 -0400 Subject: [PATCH 102/142] CA-116 restore boxes --- src/parsers/csr_parser.cc | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/parsers/csr_parser.cc b/src/parsers/csr_parser.cc index 54c1a2d..6b32c81 100644 --- a/src/parsers/csr_parser.cc +++ b/src/parsers/csr_parser.cc @@ -99,6 +99,12 @@ std::shared_ptr CSRParser::parseCSR(std::string& filePath, RTCDevice devi auto quad = make_shared(readXYZProperty(position), readXYZProperty(u), readXYZProperty(v), materials[readStringProperty(material)], device); primitives[readStringProperty(id)] = quad; scene_ptr->add_primitive(quad); + } else if (startsWith(line, "Box")) { + std::string id, position, a, b, c, material; + getNextLine(file, id); getNextLine(file, position); getNextLine(file, a); getNextLine(file, b); getNextLine(file, c); getNextLine(file, material); + auto box = make_shared(readXYZProperty(position), readXYZProperty(a), readXYZProperty(b), readXYZProperty(c), materials[readStringProperty(material)], device); + primitives[readStringProperty(id)] = box; + scene_ptr->add_primitive(box); } else if (startsWith(line, "Instance")) { auto idStart = line.find('[') + 1; auto idEnd = line.find(']'); @@ -135,9 +141,25 @@ std::shared_ptr CSRParser::parseCSR(std::string& filePath, RTCDevice devi } auto instance = make_shared(instance_ptr, transform, device); scene_ptr->add_primitive_instance(instance, device); + } else if (instanceType == "BoxPrimitive") { + std::string prim_id, translate; + getNextLine(file, prim_id); getNextLine(file, translate); + vec3 translateVector = readXYZProperty(translate); + float transform[12] = { + 1, 0, 0, translateVector.x(), + 0, 1, 0, translateVector.y(), + 0, 0, 1, translateVector.z() + }; + std::shared_ptr instance_ptr = std::dynamic_pointer_cast(primitives[readStringProperty(prim_id)]); + if (!instance_ptr) { + rtcReleaseDevice(device); + throw std::runtime_error("Instance key ERROR: " + readStringProperty(prim_id) + " is not a BoxPrimitive!"); + } + auto instance = make_shared(instance_ptr, transform, device); + scene_ptr->add_primitive_instance(instance, device); } else { rtcReleaseDevice(device); - throw std::runtime_error("Instance type UNDEFINED: Instance[SpherePrimitive|QuadPrimitive]"); + throw std::runtime_error("Instance type UNDEFINED: Instance[SpherePrimitive|QuadPrimitive|BoxPrimitive]"); } } } From 7f1bdfbca155679a060d67664039ef2ac23b6a04 Mon Sep 17 00:00:00 2001 From: connortbot Date: Wed, 25 Sep 2024 09:34:33 -0400 Subject: [PATCH 103/142] CA-89 global illumination if direct light sampling is disabled --- src/render.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/render.cc b/src/render.cc index efa84df..5a6e87d 100644 --- a/src/render.cc +++ b/src/render.cc @@ -37,7 +37,7 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { for (int i=0; i mat_ptr = nullptr; ray scattered; @@ -99,7 +99,7 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { if (sample_data.type != BSDF_TYPE::DIFFUSE) { direct = false; } if (incoming_type != BSDF_TYPE::DIFFUSE || sample_data.type == BSDF_TYPE::TRANSMISSION) { accumulated_color += weight * color_from_emission; - } else { + } else if (direct == false) { accumulated_color += weight * color_from_emission; } else { // To prevent double contribution of emission, only directly add if and only if: // => we are directly hitting the light, i.e (i==0) if (i == 0) { accumulated_color += weight * color_from_emission; } From f754d3763f0c8f2d9249614ea92c96d065cdf54f Mon Sep 17 00:00:00 2001 From: connortbot Date: Wed, 25 Sep 2024 11:13:49 -0400 Subject: [PATCH 104/142] CA-89 catch nullptr geomhit in direct sampling --- include/render.h | 8 +++++--- src/render.cc | 8 ++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/include/render.h b/include/render.h index 6cd02f8..6b297a2 100644 --- a/include/render.h +++ b/include/render.h @@ -10,9 +10,11 @@ /** * @brief Most updated integrator for path tracing through scenes - * @note Is INACCURATE WHEN BEGINNING WITHIN VOLUMES. Tracing relies are intersection with - * volume boundary to know if it "enters" or not. If ray begins within the volume, we "enter" and never exit other than - * doubling back. + * + * + * @bug Known Issues: + * - In expansive_box.csr, direct light sampling does not work -> "light_geomhit->getHitInfo(..." SEGFAULTS. + * There is a catch runtime error "MultiIntersect returned some id that does not exist in geom_map" */ color trace_ray(const ray& r, std::shared_ptr scene, int depth); diff --git a/src/render.cc b/src/render.cc index 5a6e87d..1a6cecf 100644 --- a/src/render.cc +++ b/src/render.cc @@ -37,7 +37,7 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { for (int i=0; i mat_ptr = nullptr; ray scattered; @@ -124,8 +124,11 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { std::vector tfars; // errors warning: overflow in conversion from 'float' to 'int' changes value from '+Inff' to '2147483647' [-Woverflow] // MultiIntersect(std::numeric_limits::infinity(), light_ray, scene->rtc_scene, ids, tfars); - MultiIntersect(5, light_ray, scene->rtc_scene, ids, tfars); + int intersections_to_accept = 10; + MultiIntersect(intersections_to_accept, light_ray, scene->rtc_scene, ids, tfars); // assume output ids.length() == tfars.length() + // In this section, we fire trace each recorded intersection from pos -> light + // It is hoped/assumed that we find it within 'intersections_to_accept' intersections or we find some obscurement bool non_medium_encountered = false; std::shared_ptr light_geomhit; int light_id; @@ -134,6 +137,7 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { int id = ids[j]; float tfar = tfars[j]; light_geomhit = scene->geom_map[id]; + if (!light_geomhit) { throw std::runtime_error("MultiIntersect returned some id that does not exist in geom_map"); } std::shared_ptr possible_volume_hit = std::dynamic_pointer_cast(light_geomhit); if (!possible_volume_hit) { // non volume encountered if (light_geomhit == light_ptr) { // hit the light From c87673899cf04f8f202a299a600603d18b769505 Mon Sep 17 00:00:00 2001 From: connortbot Date: Wed, 25 Sep 2024 11:15:58 -0400 Subject: [PATCH 105/142] CA-89 updated tests with Better materials --- tests/cornell_box.csr | 9 ++++++--- tests/earth.csr | 2 +- tests/instances.csr | 3 ++- tests/kylo.csr | 2 +- tests/quads.csr | 15 ++++++++++----- tests/simple_light.csr | 6 ++++-- tests/two_perlin_spheres.csr | 2 +- tests/two_spheres.csr | 2 +- 8 files changed, 26 insertions(+), 15 deletions(-) diff --git a/tests/cornell_box.csr b/tests/cornell_box.csr index 1a3308d..1830eca 100644 --- a/tests/cornell_box.csr +++ b/tests/cornell_box.csr @@ -10,20 +10,23 @@ aspect_ratio 1/1 aperture 0.0001 focus_dist 10.0 -Material[Lambertian] +Material[Diffuse] id red texture no albedo 0.65 0.05 0.05 +roughness 0.0001 -Material[Lambertian] +Material[Diffuse] id white texture no albedo 0.73 0.73 0.73 +roughness 0.0001 -Material[Lambertian] +Material[Diffuse] id green texture no albedo 0.12 0.45 0.15 +roughness 0.0001 Material[Emissive] id lightmaterial diff --git a/tests/earth.csr b/tests/earth.csr index f168610..10f313c 100644 --- a/tests/earth.csr +++ b/tests/earth.csr @@ -14,7 +14,7 @@ id earth_texture transparency false path earthmap.jpg -Material[Lambertian] +Material[Diffuse] id earth_surface texture earth_texture diff --git a/tests/instances.csr b/tests/instances.csr index 26cdd83..a6ed194 100644 --- a/tests/instances.csr +++ b/tests/instances.csr @@ -9,10 +9,11 @@ aspect_ratio 16/9 aperture 0.0001 focus_dist 10 -Material[Lambertian] +Material[Diffuse] id red texture no albedo 1.0 0.2 0.2 +roughness 0.0001 Sphere id sphere1 diff --git a/tests/kylo.csr b/tests/kylo.csr index 1536223..13df27b 100644 --- a/tests/kylo.csr +++ b/tests/kylo.csr @@ -15,7 +15,7 @@ id kylo_texture transparency true path kylo.png -Material[Lambertian] +Material[Diffuse] id kylo_material texture kylo_texture diff --git a/tests/quads.csr b/tests/quads.csr index cbc011e..65f5f52 100644 --- a/tests/quads.csr +++ b/tests/quads.csr @@ -9,30 +9,35 @@ aspect_ratio 16/9 aperture 0.0001 focus_dist 10 -Material[Lambertian] +Material[Diffuse] id left_red texture no albedo 1.0 0.2 0.2 +roughness 0.0001 -Material[Lambertian] +Material[Diffuse] id back_green texture no albedo 0.2 1.0 0.2 +roughness 0.0001 -Material[Lambertian] +Material[Diffuse] id right_blue texture no albedo 0.2 0.2 1.0 +roughness 0.0001 -Material[Lambertian] +Material[Diffuse] id upper_orange texture no albedo 1.0 0.5 0.0 +roughness 0.0001 -Material[Lambertian] +Material[Diffuse] id lower_teal texture no albedo 0.2 0.8 0.8 +roughness 0.0001 Quad id 1 diff --git a/tests/simple_light.csr b/tests/simple_light.csr index df221c0..224d71f 100644 --- a/tests/simple_light.csr +++ b/tests/simple_light.csr @@ -9,15 +9,17 @@ aspect_ratio 16/9 aperture 0.0001 focus_dist 10.0 -Material[Lambertian] +Material[Diffuse] id red texture no albedo 1.0 0.2 0.2 +roughness 0.0001 -Material[Lambertian] +Material[Diffuse] id green texture no albedo 0.2 1.0 0.2 +roughness 0.0001 Sphere id 1 diff --git a/tests/two_perlin_spheres.csr b/tests/two_perlin_spheres.csr index 5bc941b..5fc3358 100644 --- a/tests/two_perlin_spheres.csr +++ b/tests/two_perlin_spheres.csr @@ -13,7 +13,7 @@ Texture[Noise] id noise_texture scale 4 -Material[Lambertian] +Material[Diffuse] id noise_material texture noise_texture diff --git a/tests/two_spheres.csr b/tests/two_spheres.csr index ee185f2..ccbbeb1 100644 --- a/tests/two_spheres.csr +++ b/tests/two_spheres.csr @@ -15,7 +15,7 @@ scale 0.8 c1 0.2 0.3 0.1 c2 0.9 0.9 0.9 -Material[Lambertian] +Material[Diffuse] id checkered_surface texture checker From 9609da3e036614749560f4ed70b96b248bd4389b Mon Sep 17 00:00:00 2001 From: connortbot Date: Wed, 25 Sep 2024 12:16:34 -0400 Subject: [PATCH 106/142] CA-89 direct light sampling CSR support spheres+quads --- src/parsers/csr_parser.cc | 18 ++++++++++++++++-- src/render.cc | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/parsers/csr_parser.cc b/src/parsers/csr_parser.cc index 6b32c81..620a757 100644 --- a/src/parsers/csr_parser.cc +++ b/src/parsers/csr_parser.cc @@ -7,6 +7,7 @@ std::shared_ptr CSRParser::parseCSR(std::string& filePath, RTCDevice devi file = std::ifstream(filePath); std::string line; std::map> materials; + std::map> emissives; std::map> textures; std::map> primitives; @@ -62,6 +63,7 @@ std::shared_ptr CSRParser::parseCSR(std::string& filePath, RTCDevice devi std::string materialId, rgb, strength; getNextLine(file, materialId); getNextLine(file, rgb); getNextLine(file, strength); materials[readStringProperty(materialId)] = std::make_shared( (readDoubleProperty(strength) * readXYZProperty(rgb)) ); + emissives[readStringProperty(materialId)] = std::make_shared( (readDoubleProperty(strength) * readXYZProperty(rgb)) ); } else { rtcReleaseDevice(device); throw std::runtime_error("Material type UNDEFINED: Material[Lambertian|Metal|Dielectric|Emissive]"); @@ -90,15 +92,27 @@ std::shared_ptr CSRParser::parseCSR(std::string& filePath, RTCDevice devi } else if (startsWith(line, "Sphere")) { std::string id, position, material, radius; getNextLine(file, id); getNextLine(file, position); getNextLine(file, material); getNextLine(file, radius); - auto sphere = make_shared(readXYZProperty(position), materials[readStringProperty(material)], readDoubleProperty(radius), device); + bool usesEmissive = (emissives.find(readStringProperty(material)) != emissives.end()); + auto sphere = make_shared( + readXYZProperty(position), + (usesEmissive ? emissives[readStringProperty(material)] : materials[readStringProperty(material)]), + readDoubleProperty(radius), device + ); primitives[readStringProperty(id)] = sphere; scene_ptr->add_primitive(sphere); + if (usesEmissive) { scene_ptr->add_physical_light(sphere); } } else if (startsWith(line, "Quad")) { std::string id, position, u, v, material; getNextLine(file, id); getNextLine(file, position); getNextLine(file, u); getNextLine(file, v); getNextLine(file, material); - auto quad = make_shared(readXYZProperty(position), readXYZProperty(u), readXYZProperty(v), materials[readStringProperty(material)], device); + bool usesEmissive = (emissives.find(readStringProperty(material)) != emissives.end()); + auto quad = make_shared( + readXYZProperty(position), readXYZProperty(u), readXYZProperty(v), + (usesEmissive ? emissives[readStringProperty(material)] : materials[readStringProperty(material)]), + device + ); primitives[readStringProperty(id)] = quad; scene_ptr->add_primitive(quad); + if (usesEmissive) { scene_ptr->add_physical_light(quad); } } else if (startsWith(line, "Box")) { std::string id, position, a, b, c, material; getNextLine(file, id); getNextLine(file, position); getNextLine(file, a); getNextLine(file, b); getNextLine(file, c); getNextLine(file, material); diff --git a/src/render.cc b/src/render.cc index 1a6cecf..c460462 100644 --- a/src/render.cc +++ b/src/render.cc @@ -37,7 +37,7 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { for (int i=0; i mat_ptr = nullptr; ray scattered; From eee2eccc3778026d929d97db6006116f393810e8 Mon Sep 17 00:00:00 2001 From: connortbot Date: Wed, 25 Sep 2024 20:35:09 -0400 Subject: [PATCH 107/142] CA-89 quad importance sample bug possible fix --- include/render.h | 9 +++++++-- src/render.cc | 14 ++++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/include/render.h b/include/render.h index 6b297a2..630505a 100644 --- a/include/render.h +++ b/include/render.h @@ -13,8 +13,13 @@ * * * @bug Known Issues: - * - In expansive_box.csr, direct light sampling does not work -> "light_geomhit->getHitInfo(..." SEGFAULTS. - * There is a catch runtime error "MultiIntersect returned some id that does not exist in geom_map" + * - MultiIntersect returns 0 length vector during direct light sampling + * - This occurs when sampling FROM a quad, whereby the sampled point of the light yields + * a direction PARALLEL to a u or v vector of the quad itself. + * e.g if the quad is perpendicular to the y axis and the direction sampled is y=0, then: + * -> MultiIntersect returns 0 length vector which normally leads to SEGFAULT. + * -> (Unstable?) fix is added, which is to apply a small offset by the normal of the hit progressively until + * MultiIntersect succeeds. */ color trace_ray(const ray& r, std::shared_ptr scene, int depth); diff --git a/src/render.cc b/src/render.cc index c460462..b7c3fd3 100644 --- a/src/render.cc +++ b/src/render.cc @@ -126,7 +126,18 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { // MultiIntersect(std::numeric_limits::infinity(), light_ray, scene->rtc_scene, ids, tfars); int intersections_to_accept = 10; MultiIntersect(intersections_to_accept, light_ray, scene->rtc_scene, ids, tfars); // assume output ids.length() == tfars.length() - + + float epsilon = distWithinMedium * 1e-5; + int count = 1; + while ((int)ids.size() == 0) { + // This case may occur if the ray sampled runs parallel to a quad. + // To remedy, we apply a small offset by the hit normal. + point3 new_pos = record.pos + count*epsilon * record.normal; + light_ray = ray(new_pos, (sampled_point - light_dir).unit_vector(), 0.0); + MultiIntersect(intersections_to_accept, light_ray, scene->rtc_scene, ids, tfars); + count++; + } + // In this section, we fire trace each recorded intersection from pos -> light // It is hoped/assumed that we find it within 'intersections_to_accept' intersections or we find some obscurement bool non_medium_encountered = false; @@ -137,7 +148,6 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { int id = ids[j]; float tfar = tfars[j]; light_geomhit = scene->geom_map[id]; - if (!light_geomhit) { throw std::runtime_error("MultiIntersect returned some id that does not exist in geom_map"); } std::shared_ptr possible_volume_hit = std::dynamic_pointer_cast(light_geomhit); if (!possible_volume_hit) { // non volume encountered if (light_geomhit == light_ptr) { // hit the light From c7d71b7544aba987abf2ca87dbc880157d35ef68 Mon Sep 17 00:00:00 2001 From: connortbot Date: Wed, 25 Sep 2024 20:45:48 -0400 Subject: [PATCH 108/142] Update render.cc --- src/render.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/render.cc b/src/render.cc index b7c3fd3..558e3cc 100644 --- a/src/render.cc +++ b/src/render.cc @@ -133,7 +133,7 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { // This case may occur if the ray sampled runs parallel to a quad. // To remedy, we apply a small offset by the hit normal. point3 new_pos = record.pos + count*epsilon * record.normal; - light_ray = ray(new_pos, (sampled_point - light_dir).unit_vector(), 0.0); + light_ray = ray(new_pos, (sampled_point - new_pos).unit_vector(), 0.0); MultiIntersect(intersections_to_accept, light_ray, scene->rtc_scene, ids, tfars); count++; } From e8d7f68c3a598fc4c08ad99e1c693cc780bc45ee Mon Sep 17 00:00:00 2001 From: connortbot Date: Wed, 25 Sep 2024 21:11:21 -0400 Subject: [PATCH 109/142] CA-89 disable direct --- include/render.h | 2 ++ src/render.cc | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/render.h b/include/render.h index 630505a..c9e73f2 100644 --- a/include/render.h +++ b/include/render.h @@ -20,6 +20,8 @@ * -> MultiIntersect returns 0 length vector which normally leads to SEGFAULT. * -> (Unstable?) fix is added, which is to apply a small offset by the normal of the hit progressively until * MultiIntersect succeeds. + * + * For now, direct is disabled. */ color trace_ray(const ray& r, std::shared_ptr scene, int depth); diff --git a/src/render.cc b/src/render.cc index 558e3cc..830e3ae 100644 --- a/src/render.cc +++ b/src/render.cc @@ -37,7 +37,7 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { for (int i=0; i mat_ptr = nullptr; ray scattered; From a2ae2d17d44aa71cc330077ef8dce0b35660d289 Mon Sep 17 00:00:00 2001 From: connortbot Date: Wed, 25 Sep 2024 21:11:31 -0400 Subject: [PATCH 110/142] CA-89 CSR MixtureBSDF support --- src/parsers/csr_parser.cc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/parsers/csr_parser.cc b/src/parsers/csr_parser.cc index 620a757..9a7397b 100644 --- a/src/parsers/csr_parser.cc +++ b/src/parsers/csr_parser.cc @@ -64,6 +64,19 @@ std::shared_ptr CSRParser::parseCSR(std::string& filePath, RTCDevice devi getNextLine(file, materialId); getNextLine(file, rgb); getNextLine(file, strength); materials[readStringProperty(materialId)] = std::make_shared( (readDoubleProperty(strength) * readXYZProperty(rgb)) ); emissives[readStringProperty(materialId)] = std::make_shared( (readDoubleProperty(strength) * readXYZProperty(rgb)) ); + } else if (materialType == "Mixture") { + std::vector weights; + std::vector> mats; + std::string materialId, number; + getNextLine(file, materialId); getNextLine(file, number); + int num_mats = (int)readDoubleProperty(number); + for (int i=0; i(weights, mats); } else { rtcReleaseDevice(device); throw std::runtime_error("Material type UNDEFINED: Material[Lambertian|Metal|Dielectric|Emissive]"); From c013c079d500a5f1b384a57725ddc46e74cec1bb Mon Sep 17 00:00:00 2001 From: connortbot Date: Wed, 25 Sep 2024 21:13:54 -0400 Subject: [PATCH 111/142] CA-89 disable CSR importance --- src/parsers/csr_parser.cc | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/parsers/csr_parser.cc b/src/parsers/csr_parser.cc index 9a7397b..5ba16af 100644 --- a/src/parsers/csr_parser.cc +++ b/src/parsers/csr_parser.cc @@ -63,7 +63,7 @@ std::shared_ptr CSRParser::parseCSR(std::string& filePath, RTCDevice devi std::string materialId, rgb, strength; getNextLine(file, materialId); getNextLine(file, rgb); getNextLine(file, strength); materials[readStringProperty(materialId)] = std::make_shared( (readDoubleProperty(strength) * readXYZProperty(rgb)) ); - emissives[readStringProperty(materialId)] = std::make_shared( (readDoubleProperty(strength) * readXYZProperty(rgb)) ); + // emissives[readStringProperty(materialId)] = std::make_shared( (readDoubleProperty(strength) * readXYZProperty(rgb)) ); } else if (materialType == "Mixture") { std::vector weights; std::vector> mats; @@ -105,27 +105,29 @@ std::shared_ptr CSRParser::parseCSR(std::string& filePath, RTCDevice devi } else if (startsWith(line, "Sphere")) { std::string id, position, material, radius; getNextLine(file, id); getNextLine(file, position); getNextLine(file, material); getNextLine(file, radius); - bool usesEmissive = (emissives.find(readStringProperty(material)) != emissives.end()); + // bool usesEmissive = (emissives.find(readStringProperty(material)) != emissives.end()); auto sphere = make_shared( readXYZProperty(position), - (usesEmissive ? emissives[readStringProperty(material)] : materials[readStringProperty(material)]), + // (usesEmissive ? emissives[readStringProperty(material)] : + materials[readStringProperty(material)]), readDoubleProperty(radius), device ); primitives[readStringProperty(id)] = sphere; scene_ptr->add_primitive(sphere); - if (usesEmissive) { scene_ptr->add_physical_light(sphere); } + // if (usesEmissive) { scene_ptr->add_physical_light(sphere); } } else if (startsWith(line, "Quad")) { std::string id, position, u, v, material; getNextLine(file, id); getNextLine(file, position); getNextLine(file, u); getNextLine(file, v); getNextLine(file, material); - bool usesEmissive = (emissives.find(readStringProperty(material)) != emissives.end()); + // bool usesEmissive = (emissives.find(readStringProperty(material)) != emissives.end()); auto quad = make_shared( readXYZProperty(position), readXYZProperty(u), readXYZProperty(v), - (usesEmissive ? emissives[readStringProperty(material)] : materials[readStringProperty(material)]), + // (usesEmissive ? emissives[readStringProperty(material)] : + materials[readStringProperty(material)]), device ); primitives[readStringProperty(id)] = quad; scene_ptr->add_primitive(quad); - if (usesEmissive) { scene_ptr->add_physical_light(quad); } + // if (usesEmissive) { scene_ptr->add_physical_light(quad); } } else if (startsWith(line, "Box")) { std::string id, position, a, b, c, material; getNextLine(file, id); getNextLine(file, position); getNextLine(file, a); getNextLine(file, b); getNextLine(file, c); getNextLine(file, material); From 6efe69c5b548c90b6e406b91875a25b775363e49 Mon Sep 17 00:00:00 2001 From: connortbot Date: Wed, 25 Sep 2024 21:17:19 -0400 Subject: [PATCH 112/142] Update csr_parser.cc --- src/parsers/csr_parser.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/parsers/csr_parser.cc b/src/parsers/csr_parser.cc index 5ba16af..69ab7bb 100644 --- a/src/parsers/csr_parser.cc +++ b/src/parsers/csr_parser.cc @@ -108,8 +108,8 @@ std::shared_ptr CSRParser::parseCSR(std::string& filePath, RTCDevice devi // bool usesEmissive = (emissives.find(readStringProperty(material)) != emissives.end()); auto sphere = make_shared( readXYZProperty(position), - // (usesEmissive ? emissives[readStringProperty(material)] : - materials[readStringProperty(material)]), + // usesEmissive ? emissives[readStringProperty(material)] : + materials[readStringProperty(material)], readDoubleProperty(radius), device ); primitives[readStringProperty(id)] = sphere; @@ -121,8 +121,8 @@ std::shared_ptr CSRParser::parseCSR(std::string& filePath, RTCDevice devi // bool usesEmissive = (emissives.find(readStringProperty(material)) != emissives.end()); auto quad = make_shared( readXYZProperty(position), readXYZProperty(u), readXYZProperty(v), - // (usesEmissive ? emissives[readStringProperty(material)] : - materials[readStringProperty(material)]), + // usesEmissive ? emissives[readStringProperty(material)] : + materials[readStringProperty(material)], device ); primitives[readStringProperty(id)] = quad; From e10e736316e6bf3c8faf1b1ba3547dd4bf2d87f1 Mon Sep 17 00:00:00 2001 From: connortbot Date: Thu, 26 Sep 2024 18:52:09 -0400 Subject: [PATCH 113/142] CA-89 sky colours in Scene --- include/scene.h | 7 +++++++ src/scene.cc | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/include/scene.h b/include/scene.h index 0ca26e7..920af0f 100644 --- a/include/scene.h +++ b/include/scene.h @@ -10,6 +10,7 @@ #include "instances.h" #include "volume.h" #include "hit_info.hh" +#include "color.h" // SCENE INTERFACE // The scene class object covers all relevant objects in a scene: @@ -32,6 +33,10 @@ class Scene { std::map> geom_map; RTCScene rtc_scene; + // Sky Colours + color sky_bottom; + color sky_top; + // Lights std::vector> physical_lights; std::vector> lights; @@ -50,6 +55,8 @@ class Scene { void add_physical_light(std::shared_ptr geom_ptr); unsigned int add_primitive_instance(std::shared_ptr pi_ptr, RTCDevice device); + + void set_sky_colour(color bottom, color top); }; void add_sphere(RTCDevice device, RTCScene scene); diff --git a/src/scene.cc b/src/scene.cc index b3191ee..d87caf3 100644 --- a/src/scene.cc +++ b/src/scene.cc @@ -99,3 +99,8 @@ void add_triangle(RTCDevice device, RTCScene scene) { unsigned int triangleID = rtcAttachGeometry(scene, geom); rtcReleaseGeometry(geom); } + +void Scene::set_sky_colour(color bottom, color top) { + sky_bottom = bottom; + sky_top = top; +} \ No newline at end of file From 8b735b586c9dc1658385750b65cbe1bd03e36e83 Mon Sep 17 00:00:00 2001 From: connortbot Date: Thu, 26 Sep 2024 18:52:21 -0400 Subject: [PATCH 114/142] CA-89 change sky colour based on Scene --- src/render.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/render.cc b/src/render.cc index 830e3ae..5d3e32e 100644 --- a/src/render.cc +++ b/src/render.cc @@ -66,12 +66,10 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { } else if (rayhit.hit.geomID != RTC_INVALID_GEOMETRY_ID) { targetID = rayhit.hit.geomID; } else { - // Sky background (gradient blue-white) vec3 unit_direction = r_in.direction().unit_vector(); auto t = 0.5*(unit_direction.y() + 1.0); - // color sky = color(0,0,0); - color sky = (1.0-t)*color(1.0, 1.0, 1.0) + t*color(0.5, 0.7, 1.0); // lerp formula (1.0-t)*start + t*endval + color sky = (1.0-t)*(scene->sky_top) + t*(scene->sky_bottom); // lerp formula (1.0-t)*start + t*endval accumulated_color += weight * sky; return accumulated_color; } From 212ade1cf31df5ecf85624e29e27cef4bc7be53c Mon Sep 17 00:00:00 2001 From: connortbot Date: Thu, 26 Sep 2024 18:52:46 -0400 Subject: [PATCH 115/142] CA-89 Mediums/Volumes, Sky colours --- src/parsers/csr_parser.cc | 47 +++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/src/parsers/csr_parser.cc b/src/parsers/csr_parser.cc index 69ab7bb..e26cec9 100644 --- a/src/parsers/csr_parser.cc +++ b/src/parsers/csr_parser.cc @@ -10,6 +10,10 @@ std::shared_ptr CSRParser::parseCSR(std::string& filePath, RTCDevice devi std::map> emissives; std::map> textures; std::map> primitives; + std::map> medium_primitives; + + std::map> mediums; + std::map> volumes; if (!file.is_open() || !file.good()) { rtcReleaseDevice(device); @@ -28,7 +32,11 @@ std::shared_ptr CSRParser::parseCSR(std::string& filePath, RTCDevice devi while (getNextLine(file, line)) { line = trim(line); - if (startsWith(line, "Material")) { + if (startsWith(line, "Sky")) { + std::string top, bottom; + getNextLine(file, top); getNextLine(file, bottom); + scene_ptr->set_sky_colour(readXYZProperty(bottom), readXYZProperty(top)); + } else if (startsWith(line, "Material")) { // Extract material ID from brackets (e.g., Material[Lambertian] -> Lambertian) auto idStart = line.find('[') + 1; auto idEnd = line.find(']'); @@ -103,8 +111,9 @@ std::shared_ptr CSRParser::parseCSR(std::string& filePath, RTCDevice devi throw std::runtime_error("Texture type UNDEFINED: Texture[Checker|Image|Noise]"); } } else if (startsWith(line, "Sphere")) { - std::string id, position, material, radius; + std::string id, position, material, radius, medium; getNextLine(file, id); getNextLine(file, position); getNextLine(file, material); getNextLine(file, radius); + getNextLine(file, medium); // bool usesEmissive = (emissives.find(readStringProperty(material)) != emissives.end()); auto sphere = make_shared( readXYZProperty(position), @@ -112,8 +121,12 @@ std::shared_ptr CSRParser::parseCSR(std::string& filePath, RTCDevice devi materials[readStringProperty(material)], readDoubleProperty(radius), device ); - primitives[readStringProperty(id)] = sphere; - scene_ptr->add_primitive(sphere); + if (!readBooleanProperty(medium)) { + primitives[readStringProperty(id)] = sphere; + scene_ptr->add_primitive(sphere); + } else { + medium_primitives[readStringProperty(id)] = sphere; + } // if (usesEmissive) { scene_ptr->add_physical_light(sphere); } } else if (startsWith(line, "Quad")) { std::string id, position, u, v, material; @@ -129,11 +142,16 @@ std::shared_ptr CSRParser::parseCSR(std::string& filePath, RTCDevice devi scene_ptr->add_primitive(quad); // if (usesEmissive) { scene_ptr->add_physical_light(quad); } } else if (startsWith(line, "Box")) { - std::string id, position, a, b, c, material; + std::string id, position, a, b, c, material, medium; getNextLine(file, id); getNextLine(file, position); getNextLine(file, a); getNextLine(file, b); getNextLine(file, c); getNextLine(file, material); + getNextLine(file, medium); auto box = make_shared(readXYZProperty(position), readXYZProperty(a), readXYZProperty(b), readXYZProperty(c), materials[readStringProperty(material)], device); - primitives[readStringProperty(id)] = box; - scene_ptr->add_primitive(box); + if (!readBooleanProperty(medium)) { + primitives[readStringProperty(id)] = box; + scene_ptr->add_primitive(box); + } else { + medium_primitives[readStringProperty(id)] = box; + } } else if (startsWith(line, "Instance")) { auto idStart = line.find('[') + 1; auto idEnd = line.find(']'); @@ -190,6 +208,21 @@ std::shared_ptr CSRParser::parseCSR(std::string& filePath, RTCDevice devi rtcReleaseDevice(device); throw std::runtime_error("Instance type UNDEFINED: Instance[SpherePrimitive|QuadPrimitive|BoxPrimitive]"); } + } else if (startsWith(line, "Medium")) { + std::string medium_id, density, albedo; + getNextLine(file, medium_id); getNextLine(file, density); getNextLine(file, albedo); + auto medium = make_shared(readDoubleProperty(density), make_shared(readXYZProperty(albedo))); + mediums[readStringProperty(medium_id)] = medium; + } else if (startsWith(line, "Volume")) { + std::string volume_id, medium_id, prim_id; + getNextLine(file, volume_id); getNextLine(file, medium_id); getNextLine(file, prim_id); + auto volume = make_shared( + mediums[readStringProperty(medium_id)], + medium_primitives[readStringProperty(prim_id)], + device + ); + volumes[readStringProperty(volume_id)] = volume; + scene_ptr->add_volume(volume); } } From 9d91069f4e84610d5e81288b98f2971e5cd4903d Mon Sep 17 00:00:00 2001 From: connortbot Date: Thu, 26 Sep 2024 18:54:07 -0400 Subject: [PATCH 116/142] Create 0.1.5-showcase.csr --- tests/0.1.5-showcase.csr | 176 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 tests/0.1.5-showcase.csr diff --git a/tests/0.1.5-showcase.csr b/tests/0.1.5-showcase.csr new file mode 100644 index 0000000..9be37ec --- /dev/null +++ b/tests/0.1.5-showcase.csr @@ -0,0 +1,176 @@ +version 0.1.5 + +Camera +lookfrom 30 8 0 +lookat 0 4 0 +vup 0 1 0 +vfov 50 +aspect_ratio 16/9 +aperture 0.0001 +focus_dist 10.0 + +# NEW FEATURE: We can set the sky colours! +Sky +top 1.0 0.9 0.5 +bottom 1.0 0.0 0.0 + +# Scene Setup with red ground +Material[Diffuse] +id dark_brown +texture no +albedo 0.9 0.9 0.9 +roughness 0.0001 + +Sphere +id ground +position 0 -2000 0 +material dark_brown +radius 2000 +medium false + + +# NEW FEATURE: Making a Volume +# First, we create a sphere. +Sphere +id volume_sphere +position 0 4 -8 +material dark_brown +radius 4 +medium true + +# Then, we make a Medium (the properties of the volume) +Medium +id medium_example +density 0.75 +albedo 0.23 0.1 0.6 + +# Finally, we build the physical volume with the +# shape of the Sphere and properties of the Medium +Volume +id volume_example +medium_id medium_example +prim_id volume_sphere + +# NEW FEATURE: Making a Box with the new Diamond! + +# Lets start with making our new Dielectrics (like a diamond :p) +Material[Dielectric] +id cyan_diamond +albedo 0.1 0.8 0.8 +eta 2.4 +roughness 0.0001 +sheen 0 + +# And now we make the box with our dielectric material! +Box +id diamond_box +position 0 4 4 +a 8 0 0 +b 0 -4 0 +c 0 0 8 +material cyan_diamond +medium false + +# We can also make volumes with this one +Box +id volume_box +position 0 8 4 +a 8 0 0 +b 0 -8 0 +c 0 0 8 +material cyan_diamond +medium true + +# Then, we make another Medium (the properties of the volume) +Medium +id medium_box_example +density 0.31 # lets make this one a little thinner! +albedo 0.5 0.1 0.5 + +# Now make a Medium out of this +Volume +id volume_box_example +medium_id medium_box_example +prim_id volume_box + + +# We already covered the new dielectric model, but let's also make the two new types! + +# First, the new diffuse! (It's actually already visible on the ground, but lets make another). +Material[Diffuse] +id green_diffuse +texture no +albedo 0.13 0.9 0.1 +roughness 0.0001 + +Sphere +id green_sphere +position -8 6 0 +material green_diffuse +radius 6 +medium false + +# Now, lets check out the new metal! +# Let's make a very shiny mirror...which involves a Metal and a Quad! +Material[Metal] +id mirror_example +albedo 0.9 0.89 1.0 # to make it easy to see, we'll make it a little scratched up +roughness 0.005 + +Quad +id mirror_quad +position -20 18 -8 +u 15 0 -15 +v 0 -17 0 +material mirror_example + +# A very cool new feature is the Pixel Lambertian +# You can finally add pixel art into your scenes! In fact, let's make Kylo Ren in front of +# all the objects. And we'll make it a bit more cinematic... + +# Make the Kylo Ren +Texture[Image] +id kylo_texture +transparency true +path kylo.png + +Material[Diffuse] +id kylo_material +texture kylo_texture + +Quad +id 0 +position 5 0 2.5 +u 0 0 -7.5 +v 0 7.5 0 +material kylo_material + +# Now, let's make it a little more cinematic... +# Emissive +Material[Emissive] +id key_light +rgb 0.6 0.6 0.1 +strength 10 + +Sphere +id key_sphere +position -4000 0 4000 +material key_light +radius 2000 +medium false + +# You could edit this so that it has a black sky. If it does, maybe you want to add a red fill light +# so that Kylo's front isn't black. +# In this case, lets add a red fill light from the front. + +Material[Emissive] +id fill_light +rgb 1.0 0.2 0.2 +strength 2.5 + +Quad +id 0 +position 35 0 20 +u 0 0 -20 +v 0 20 0 +material fill_light \ No newline at end of file From 25962b9b831d9e5121303a003cee0af9560724ca Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 27 Sep 2024 02:42:40 -0400 Subject: [PATCH 117/142] CA-89 update csr-schema validator submodule --- csr-schema | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csr-schema b/csr-schema index fd69650..37df3e3 160000 --- a/csr-schema +++ b/csr-schema @@ -1 +1 @@ -Subproject commit fd69650a7bf69b08228135054263a5c8345906df +Subproject commit 37df3e35b4c2d283df216e54b15fed0142fe4ed1 From 223a6f791696febcd427220e95b169300244a064 Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 27 Sep 2024 14:55:41 -0400 Subject: [PATCH 118/142] Update 0.1.5-showcase.csr --- tests/0.1.5-showcase.csr | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/tests/0.1.5-showcase.csr b/tests/0.1.5-showcase.csr index 9be37ec..d241ce6 100644 --- a/tests/0.1.5-showcase.csr +++ b/tests/0.1.5-showcase.csr @@ -159,18 +159,22 @@ material key_light radius 2000 medium false -# You could edit this so that it has a black sky. If it does, maybe you want to add a red fill light -# so that Kylo's front isn't black. -# In this case, lets add a red fill light from the front. - -Material[Emissive] -id fill_light -rgb 1.0 0.2 0.2 -strength 2.5 +# Let's also make a small floor underneath Kylo thats a MIX of multiple different materials! +# NEW FEATURE: Mixture + +Material[Mixture] +id mixture_example +num 3 +mixed mirror_example +weight 0.333 +mixed green_diffuse +weight 0.333 +mixed cyan_diamond +weight 0.334 Quad -id 0 -position 35 0 20 -u 0 0 -20 -v 0 20 0 -material fill_light \ No newline at end of file +id floor_quad +position 4 0 -1 +u 0 0 2 +v 2 0 0 +material mixture_example \ No newline at end of file From 53467e042f03fb5c5c265d7052c0d0574a11cd23 Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 27 Sep 2024 14:58:49 -0400 Subject: [PATCH 119/142] CA-89 update validator with new changes --- csr-schema | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csr-schema b/csr-schema index 37df3e3..a7b84ad 160000 --- a/csr-schema +++ b/csr-schema @@ -1 +1 @@ -Subproject commit 37df3e35b4c2d283df216e54b15fed0142fe4ed1 +Subproject commit a7b84ad5c43708163ecdf8d68c4bec624a558f5c From 59b0edb6d4ac60eaead3b67ee1b7c08f51a8d563 Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 27 Sep 2024 15:09:31 -0400 Subject: [PATCH 120/142] CA-89 reposition showcase Mixture --- tests/0.1.5-showcase.csr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/0.1.5-showcase.csr b/tests/0.1.5-showcase.csr index d241ce6..3253b5e 100644 --- a/tests/0.1.5-showcase.csr +++ b/tests/0.1.5-showcase.csr @@ -174,7 +174,7 @@ weight 0.334 Quad id floor_quad -position 4 0 -1 -u 0 0 2 -v 2 0 0 +position 1 0 2.5 +u 0 0 -7.5 +v 8 0 0 material mixture_example \ No newline at end of file From 18844d1aa3b4f50c9566f1b221e523e941d4e578 Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 27 Sep 2024 15:10:59 -0400 Subject: [PATCH 121/142] CA-89 restore main to no-hardcode --- main.cc | 98 ++++++--------------------------------------------------- 1 file changed, 10 insertions(+), 88 deletions(-) diff --git a/main.cc b/main.cc index 0e3bccd..e1cdc3b 100644 --- a/main.cc +++ b/main.cc @@ -5,96 +5,18 @@ #include "output.h" -void brdf_tests() { - RenderData render_data; - const auto aspect_ratio = 16.0 / 9.0; - setRenderData(render_data, aspect_ratio, 1200, 25, 200); - - point3 lookfrom(10, 3, 0); - point3 lookat(0, 2, 0); - vec3 vup(0,1,0); - double vfov = 60; - double aperture = 0.0001; - double dist_to_focus = 10.0; - - Camera cam(lookfrom, lookat, vup, vfov, aspect_ratio, aperture, dist_to_focus); - +int main(int argc, char* argv[]) { + Config config = parseArguments(argc, argv); + + RenderData render_data; + const auto aspect_ratio = static_cast(config.image_width) / config.image_height; + setRenderData(render_data, aspect_ratio, config.image_width, config.samples_per_pixel, config.max_depth); + std::string filePath = config.inputFile; RTCDevice device = initializeDevice(); - auto scene_ptr = make_shared(device, cam); - - // ALL MATERIALS - // DEFAULTS V0.1.X - auto emit = make_shared(color(15.0, 15.0, 15.0)); - auto mt0 = make_shared(1.5); - auto mt1 = make_shared(color(1.0, 1.0, 1.0), 0.1); - auto mt2 = make_shared(color(1.0, 1.0, 1.0)); - - // Oren-Nayar - auto mt3 = make_shared(color(1.0, 1.0, 1.0), 0.0); - - // Cook-Torrance - // Complex example: - auto mt4 = make_shared(color(1.0, 1.0, 1.0), color(1.0, 1.0, 1.0), 0.05); - // Non-complex example: - auto mt5 = make_shared(color(1.0, 1.0, 1.0), 0.1); - - // Dielectric comparison - auto mt6 = make_shared(color(1.0, 1.0, 1.0), 1.5, 0.0001, 5); // model glass - auto mt7 = make_shared(color(1.0, 1.0, 1.0), 0.0, 0.0001); // model mirror - - // Example of MixtureBSDF - std::vector weights = {0.333f, 0.334f, 0.333f}; - std::vector> mats; - mats.push_back(mt3); - mats.push_back(mt6); - mats.push_back(mt7); - auto mt8 = make_shared(weights, mats); - - // Example of creating medium with isotropic phase function - auto iso = make_shared(color(1.0,1.0,1.0)); - auto medium = make_shared(1, iso); - - // Example of LayeredBSDF: glass on diffuse with white cloud inside - auto layered_iso = make_shared(color(0.1,0.8,0.1)); - auto layered_medium = make_shared(0.1, layered_iso); - auto mt9 = make_shared(mt6, mt3, layered_medium, 10); - - // Adding 3 spheres - auto sphere1 = make_shared(point3(0, 2, 2), mt5, 2, device); - auto sphere2 = make_shared(point3(1, 2, -2), emit, 0.5, device); - auto sphere3 = make_shared(point3(-4, 2, -1), mt9, 2, device); - scene_ptr->add_primitive(sphere2); - scene_ptr->add_primitive(sphere3); - scene_ptr->add_physical_light(sphere2); - - // Create volume out of sphere1 - auto volume1 = make_shared(medium, sphere1, device); - scene_ptr->add_volume(volume1); - - auto red = make_shared(color(1.0, 0.2, 0.2)); - auto ground = make_shared(point3(0,-10000,0), red, 10000, device); - scene_ptr->add_primitive(ground); - + CSRParser parser; + auto scene_ptr = parser.parseCSR(filePath, device); scene_ptr->commitScene(); rtcReleaseDevice(device); - Config config; - output(render_data, cam, scene_ptr, config); -} - -int main(int argc, char* argv[]) { - brdf_tests(); - // Config config = parseArguments(argc, argv); - - // RenderData render_data; - // const auto aspect_ratio = static_cast(config.image_width) / config.image_height; - // setRenderData(render_data, aspect_ratio, config.image_width, config.samples_per_pixel, config.max_depth); - // std::string filePath = config.inputFile; - // RTCDevice device = initializeDevice(); - // CSRParser parser; - // auto scene_ptr = parser.parseCSR(filePath, device); - // scene_ptr->commitScene(); - // rtcReleaseDevice(device); - - // output(render_data, scene_ptr->cam, scene_ptr, config); + output(render_data, scene_ptr->cam, scene_ptr, config); } From a90df5bf3ad9f02fb2b94a4899fa08191d2056ee Mon Sep 17 00:00:00 2001 From: connortbot Date: Fri, 27 Sep 2024 15:27:49 -0400 Subject: [PATCH 122/142] CA-89 better tests running script --- tools/tests.sh | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) mode change 100644 => 100755 tools/tests.sh diff --git a/tools/tests.sh b/tools/tests.sh old mode 100644 new mode 100755 index a21404a..ab28205 --- a/tools/tests.sh +++ b/tools/tests.sh @@ -1,16 +1,25 @@ #!/bin/bash +# Run this from inside the tools folder, as the following paths are RELATIVE to your +# current location + # Defaults for if caitlyn is in the build folder. Create a test_outputs folder in build/. test_outputs_dir="../build/test_outputs" tests_dir="../tests" executable="../build/caitlyn" -if [ $1 == 'yes' ] -then +if [ "$1" == 'yes' ]; then + # Navigate to the build directory + pushd ../build > /dev/null + make clean make + + # Return to the original directory + popd > /dev/null fi for file in "$tests_dir"/*.csr; do + echo "$file" "$executable" -i "$file" -t png -o "$test_outputs_dir/$(basename "$file" .csr).png" -s 2 -d 2 -V done \ No newline at end of file From 5ebe910779a071da4589b359b17ec02896e8642f Mon Sep 17 00:00:00 2001 From: connortbot Date: Sat, 28 Sep 2024 12:06:44 -0400 Subject: [PATCH 123/142] CA-89 updated tests CSR --- tests/instances.csr | 2 ++ tests/simple_light.csr | 3 +++ tests/two_perlin_spheres.csr | 4 +++- tests/two_spheres.csr | 4 +++- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/tests/instances.csr b/tests/instances.csr index 9f3bcd5..a6fded3 100644 --- a/tests/instances.csr +++ b/tests/instances.csr @@ -21,6 +21,7 @@ id sphere1 position 0 -1 3 material red radius 1 +medium false Quad id quad1 @@ -36,6 +37,7 @@ a 1 0 0 b 0 1 0 c 0 0 1 material red +medium false Instance[SpherePrimitive] prim_id sphere1 diff --git a/tests/simple_light.csr b/tests/simple_light.csr index 224d71f..6a8a0fd 100644 --- a/tests/simple_light.csr +++ b/tests/simple_light.csr @@ -26,12 +26,14 @@ id 1 position 0 -1000 0 material red radius 1000 +medium false Sphere id 2 position 0 2 0 material green radius 2 +medium false Material[Emissive] id light_material @@ -43,6 +45,7 @@ id lightsphere position 0 7 0 material light_material radius 2 +medium false Quad id lightquad diff --git a/tests/two_perlin_spheres.csr b/tests/two_perlin_spheres.csr index 5fc3358..625e442 100644 --- a/tests/two_perlin_spheres.csr +++ b/tests/two_perlin_spheres.csr @@ -22,9 +22,11 @@ id 1 position 0 -1000 0 material noise_material radius 1000 +medium false Sphere id 2 position 0 2 0 material noise_material -radius 2 \ No newline at end of file +radius 2 +medium false \ No newline at end of file diff --git a/tests/two_spheres.csr b/tests/two_spheres.csr index ccbbeb1..bb6e019 100644 --- a/tests/two_spheres.csr +++ b/tests/two_spheres.csr @@ -24,9 +24,11 @@ id top position 0 -10 0 material checkered_surface radius 10 +medium false Sphere id bottom position 0 10 0 material checkered_surface -radius 10 \ No newline at end of file +radius 10 +medium false \ No newline at end of file From 5ac6155f05100a7bc34a3b40ab6acef70b75dd67 Mon Sep 17 00:00:00 2001 From: connortbot Date: Sat, 28 Sep 2024 12:12:48 -0400 Subject: [PATCH 124/142] CA-89 skies in test csr --- tests/earth.csr | 4 ++++ tests/instances.csr | 4 ++++ tests/kylo.csr | 4 ++++ tests/quads.csr | 4 ++++ tests/simple_light.csr | 4 ++++ tests/two_perlin_spheres.csr | 4 ++++ tests/two_spheres.csr | 4 ++++ tools/tests.sh | 2 +- 8 files changed, 29 insertions(+), 1 deletion(-) diff --git a/tests/earth.csr b/tests/earth.csr index 10f313c..f989add 100644 --- a/tests/earth.csr +++ b/tests/earth.csr @@ -9,6 +9,10 @@ aspect_ratio 16/9 aperture 0.0001 focus_dist 10.0 +Sky +top 0.5 0.7 1.0 +bottom 1.0 1.0 1.0 + Texture[Image] id earth_texture transparency false diff --git a/tests/instances.csr b/tests/instances.csr index a6fded3..93ded5f 100644 --- a/tests/instances.csr +++ b/tests/instances.csr @@ -10,6 +10,10 @@ aspect_ratio 16/9 aperture 0.0001 focus_dist 10 +Sky +top 0.5 0.7 1.0 +bottom 1.0 1.0 1.0 + Material[Diffuse] id red texture no diff --git a/tests/kylo.csr b/tests/kylo.csr index 13df27b..ab7a189 100644 --- a/tests/kylo.csr +++ b/tests/kylo.csr @@ -10,6 +10,10 @@ aspect_ratio 16/9 aperture 0.0001 focus_dist 10.0 +Sky +top 0.5 0.7 1.0 +bottom 1.0 1.0 1.0 + Texture[Image] id kylo_texture transparency true diff --git a/tests/quads.csr b/tests/quads.csr index 65f5f52..bebc00f 100644 --- a/tests/quads.csr +++ b/tests/quads.csr @@ -9,6 +9,10 @@ aspect_ratio 16/9 aperture 0.0001 focus_dist 10 +Sky +top 0.5 0.7 1.0 +bottom 1.0 1.0 1.0 + Material[Diffuse] id left_red texture no diff --git a/tests/simple_light.csr b/tests/simple_light.csr index 6a8a0fd..3758540 100644 --- a/tests/simple_light.csr +++ b/tests/simple_light.csr @@ -9,6 +9,10 @@ aspect_ratio 16/9 aperture 0.0001 focus_dist 10.0 +Sky +top 0.5 0.7 1.0 +bottom 1.0 1.0 1.0 + Material[Diffuse] id red texture no diff --git a/tests/two_perlin_spheres.csr b/tests/two_perlin_spheres.csr index 625e442..de92dfa 100644 --- a/tests/two_perlin_spheres.csr +++ b/tests/two_perlin_spheres.csr @@ -9,6 +9,10 @@ aspect_ratio 16/9 aperture 0.0001 focus_dist 10 +Sky +top 0.5 0.7 1.0 +bottom 1.0 1.0 1.0 + Texture[Noise] id noise_texture scale 4 diff --git a/tests/two_spheres.csr b/tests/two_spheres.csr index bb6e019..ea9f242 100644 --- a/tests/two_spheres.csr +++ b/tests/two_spheres.csr @@ -9,6 +9,10 @@ aspect_ratio 16/9 aperture 0.0001 focus_dist 10.0 +Sky +top 0.5 0.7 1.0 +bottom 1.0 1.0 1.0 + Texture[Checker] id checker scale 0.8 diff --git a/tools/tests.sh b/tools/tests.sh index ab28205..db52bda 100755 --- a/tools/tests.sh +++ b/tools/tests.sh @@ -21,5 +21,5 @@ fi for file in "$tests_dir"/*.csr; do echo "$file" - "$executable" -i "$file" -t png -o "$test_outputs_dir/$(basename "$file" .csr).png" -s 2 -d 2 -V + "$executable" -i "$file" -t png -o "$test_outputs_dir/$(basename "$file" .csr).png" -s 5 -d 200 -V done \ No newline at end of file From 5c0ade741b209b5b14177ee8a91c511591972455 Mon Sep 17 00:00:00 2001 From: connortbot Date: Sat, 28 Sep 2024 21:19:43 -0400 Subject: [PATCH 125/142] CA-89 fixed lambert term --- src/materials/material.cc | 2 +- src/render.cc | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/materials/material.cc b/src/materials/material.cc index 7dc6d64..e2054d1 100644 --- a/src/materials/material.cc +++ b/src/materials/material.cc @@ -62,7 +62,7 @@ color OrenNayar::generate(const ray& r_in, const ray& scattered, const HitInfo& } double OrenNayar::pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { - auto cos_theta = dot(rec.normal, scattered.direction().unit_vector()); + auto cos_theta = dot(rec.normal, -r_in.direction().unit_vector()); return fmax(0.0, cos_theta / pi); } diff --git a/src/render.cc b/src/render.cc index 5d3e32e..3f57ecb 100644 --- a/src/render.cc +++ b/src/render.cc @@ -104,6 +104,7 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { } // Direct Light Sampling + // Disabled as of v0.1.5, needs improvements! if (direct && color_from_emission.length() == 0.0) { int N = (int)scene->physical_lights.size(); // amount of lights for (auto& light_ptr : scene->physical_lights) { // only accounts for physical lights currently @@ -201,7 +202,7 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { if (!sample_data.scatter) { return accumulated_color; } - double cos_theta = fabs(dot(record.normal, (sample_data.scatter_direction))); + double cos_theta = fabs(dot(record.normal, -r_in.direction().unit_vector())); if (!raymarched) { weight = weight * (sample_data.bsdf_value * cos_theta / sample_data.pdf_value); } else { From c23348e4c76dc80eb89b611c6ed863c0f49aa939 Mon Sep 17 00:00:00 2001 From: connortbot Date: Sun, 29 Sep 2024 00:39:32 -0400 Subject: [PATCH 126/142] CA-89 black artifacts fix when sample pdf is 0 --- src/render.cc | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/render.cc b/src/render.cc index 3f57ecb..2c2da24 100644 --- a/src/render.cc +++ b/src/render.cc @@ -1,5 +1,7 @@ #include "render.h" +color INVALID_SAMPLE = color(INT16_MIN, INT16_MIN, INT16_MIN); + color trace_ray(const ray& r, std::shared_ptr scene, int depth) { HitInfo record; @@ -105,6 +107,9 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { // Direct Light Sampling // Disabled as of v0.1.5, needs improvements! + // Additionally: + // => may be using wrong w in cos term + // => check if pdfs are 0 are not in place (invalid sample if so) if (direct && color_from_emission.length() == 0.0) { int N = (int)scene->physical_lights.size(); // amount of lights for (auto& light_ptr : scene->physical_lights) { // only accounts for physical lights currently @@ -204,8 +209,10 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { } double cos_theta = fabs(dot(record.normal, -r_in.direction().unit_vector())); if (!raymarched) { + if (sample_data.pdf_value == 0) { return INVALID_SAMPLE; } weight = weight * (sample_data.bsdf_value * cos_theta / sample_data.pdf_value); } else { + if (sample_data.pdf_value == 0) { return INVALID_SAMPLE; } weight = weight * (sample_data.bsdf_value / sample_data.pdf_value); } r_in = scattered; @@ -284,6 +291,8 @@ void render_scanlines(int lines, int start_line, std::shared_ptr scene_pt for (int i=0; i scene_pt auto u = (i + (px + random_double()) / sqrt_samples) / (image_width - 1); auto v = (j + (py + random_double()) / sqrt_samples) / (image_height - 1); ray r = cam.get_ray(u, v); - pixel_color += trace_ray(r, scene_ptr, max_depth); + color curr_sample = trace_ray(r, scene_ptr, max_depth); + + // Check if the sample is invalid (PDF = 0 or another condition) + //if (curr_sample == INVALID_SAMPLE) { + if ( + curr_sample.x() == INVALID_SAMPLE.x() && + curr_sample.y() == INVALID_SAMPLE.y() && + curr_sample.z() == INVALID_SAMPLE.z() + ) { + // Replace the invalid sample with a "fake" sample that is the average + if (valid_sample_count > 0) { + curr_sample = valid_sample_accum / valid_sample_count; // Use average of valid samples so far + } else { + curr_sample = color(0, 0, 0); // No valid samples yet, use default color + } + } else { + // Accumulate the valid sample and increase the count + valid_sample_accum += curr_sample; + valid_sample_count++; + } + + pixel_color += curr_sample; } } From db5dbac47243f5a2b80b12a3dbf1484eee851db9 Mon Sep 17 00:00:00 2001 From: connortbot Date: Sun, 29 Sep 2024 01:03:50 -0400 Subject: [PATCH 127/142] CA-89 correct cos term in LayeredBSDF --- src/materials/material.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/materials/material.cc b/src/materials/material.cc index e2054d1..bb586c9 100644 --- a/src/materials/material.cc +++ b/src/materials/material.cc @@ -515,7 +515,7 @@ BSDFSample LayeredBSDF::sample(const ray& r_in, HitInfo& rec, ray& scattered) co pdf *= 1 - q; } - if (!prev_medium) { f = f * fabs(dot(rec_manip.normal, (bs.scatter_direction))); } + if (!prev_medium) { f = f * fabs(dot(rec_manip.normal, (r.direction().unit_vector()))); } prev_medium = false; r = ray(rec_manip.pos - bs.scatter_direction, bs.scatter_direction, r.time()); From 03c4db2bc5cacf96ffd805f5e72515343ba7f799 Mon Sep 17 00:00:00 2001 From: connortbot Date: Sun, 29 Sep 2024 01:23:12 -0400 Subject: [PATCH 128/142] Update material.h --- include/materials/material.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/include/materials/material.h b/include/materials/material.h index 5b35b8a..f80eeb6 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -387,14 +387,8 @@ class MixtureBSDF : public material { * @param bottom Bottom Layer BSDF. * @param termination Russian Roulette termination condition for number of bounces. If exceeded, pretends light is absorbed. * - * @note SCATTER, GENERATE, AND PDF DO NOT CONTAIN NECESSARY RUSSIAN ROULETTE OR LOGARITHMIC ACCUMULATION. THEY ARE NOT READY. + * @note SCATTER, GENERATE, AND PDF ARE NOT READY. * USE SAMPLE INSTEAD. - * - * @bug Using mediums will cause black artifacts that increase as samples increase. This is likely due to amount of bounces and loss of energy. - * This also occurs on a much lower scale without mediums, and is partially remedied by: - * -> Russian Roulette termination. - * -> Setting a termination integer close to the default (e.g 20). - * But it is not perfect, and a better solution should be found! */ class LayeredBSDF : public material { public: From 82be8b713501b5247fc155ca5accc22bacdc6b90 Mon Sep 17 00:00:00 2001 From: connortbot Date: Sun, 29 Sep 2024 23:00:19 -0400 Subject: [PATCH 129/142] CA-124 CSR Support for Layered --- src/parsers/csr_parser.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/parsers/csr_parser.cc b/src/parsers/csr_parser.cc index e26cec9..604c277 100644 --- a/src/parsers/csr_parser.cc +++ b/src/parsers/csr_parser.cc @@ -85,6 +85,14 @@ std::shared_ptr CSRParser::parseCSR(std::string& filePath, RTCDevice devi mats.push_back(materials[readStringProperty(mixed)]); } materials[readStringProperty(materialId)] = make_shared(weights, mats); + } else if (materialType == "Layered") { + std::string materialId, top, bottom, medium; + getNextLine(file, materialId); getNextLine(file, top); getNextLine(file, bottom); getNextLine(file, medium); + materials[readStringProperty(materialId)] = make_shared( + materials[readStringProperty(top)], + materials[readStringProperty(bottom)], + (readStringProperty(medium) == "no") ? nullptr : mediums[readStringProperty(medium)] + ); } else { rtcReleaseDevice(device); throw std::runtime_error("Material type UNDEFINED: Material[Lambertian|Metal|Dielectric|Emissive]"); From 41b0c2987e43920b37df9afd02fa928484aae9b0 Mon Sep 17 00:00:00 2001 From: connortbot Date: Sun, 29 Sep 2024 23:00:47 -0400 Subject: [PATCH 130/142] CA-89 Updated showcase with Layered --- tests/0.1.5-showcase.csr | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/0.1.5-showcase.csr b/tests/0.1.5-showcase.csr index 3253b5e..a94ee3d 100644 --- a/tests/0.1.5-showcase.csr +++ b/tests/0.1.5-showcase.csr @@ -177,4 +177,21 @@ id floor_quad position 1 0 2.5 u 0 0 -7.5 v 8 0 0 -material mixture_example \ No newline at end of file +material mixture_example + +# NEW FEATURE: Layered materials +# Lets make a cool combination that isn't a mixture, but rather two layers of materials. +# What if we want to give the diffuse a diamond exterior? + +Material[Layered] +id layered_example +top cyan_diamond +bottom green_diffuse +medium medium_example # it could also be 'no' if we didn't want to use one + +Sphere +id layered_sphere +position 7 2 -8 +material layered_example +radius 2 +medium false \ No newline at end of file From c822aa189f8f8707ff0457acfe5257f909c5d042 Mon Sep 17 00:00:00 2001 From: connortbot Date: Sun, 29 Sep 2024 23:21:36 -0400 Subject: [PATCH 131/142] CA-89 Finite checks for samples and bug note --- include/materials/material.h | 5 +++++ src/render.cc | 11 +++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/include/materials/material.h b/include/materials/material.h index f80eeb6..0a2de23 100644 --- a/include/materials/material.h +++ b/include/materials/material.h @@ -389,6 +389,11 @@ class MixtureBSDF : public material { * * @note SCATTER, GENERATE, AND PDF ARE NOT READY. * USE SAMPLE INSTEAD. + * + * @bug Bright noise still present + * => Replicate with a diffuse overlaid by dielectric (e.g diamond) and notice that + * bright noise does not reduce with samples. inf checks do not remove it, its possible that there are + * very "large" numbers occurring! */ class LayeredBSDF : public material { public: diff --git a/src/render.cc b/src/render.cc index 2c2da24..598d3df 100644 --- a/src/render.cc +++ b/src/render.cc @@ -303,11 +303,18 @@ void render_scanlines(int lines, int start_line, std::shared_ptr scene_pt color curr_sample = trace_ray(r, scene_ptr, max_depth); // Check if the sample is invalid (PDF = 0 or another condition) + // trace_ray should catch invalid sampled, but we do another check here + bool nan_or_inf = ( + !std::isfinite(curr_sample.x()) || + !std::isfinite(curr_sample.y()) || + !std::isfinite(curr_sample.z()) + ); //if (curr_sample == INVALID_SAMPLE) { if ( - curr_sample.x() == INVALID_SAMPLE.x() && + (curr_sample.x() == INVALID_SAMPLE.x() && curr_sample.y() == INVALID_SAMPLE.y() && - curr_sample.z() == INVALID_SAMPLE.z() + curr_sample.z() == INVALID_SAMPLE.z()) || + nan_or_inf ) { // Replace the invalid sample with a "fake" sample that is the average if (valid_sample_count > 0) { From 1a3f1f484d6babc123a7d780b7cc4d41c22fd871 Mon Sep 17 00:00:00 2001 From: connortbot Date: Sun, 29 Sep 2024 23:39:13 -0400 Subject: [PATCH 132/142] Update csr-schema --- csr-schema | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csr-schema b/csr-schema index a7b84ad..983fced 160000 --- a/csr-schema +++ b/csr-schema @@ -1 +1 @@ -Subproject commit a7b84ad5c43708163ecdf8d68c4bec624a558f5c +Subproject commit 983fced58e645cd300abbc69d5a778e1756ea624 From 6af7d9788b86b51d8d3f00eded6539665a84c712 Mon Sep 17 00:00:00 2001 From: connortbot Date: Mon, 30 Sep 2024 10:36:15 -0400 Subject: [PATCH 133/142] CA-89 Fix OrenNayar pdf to depend on outgoing --- src/materials/material.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/materials/material.cc b/src/materials/material.cc index bb586c9..479c4dc 100644 --- a/src/materials/material.cc +++ b/src/materials/material.cc @@ -62,7 +62,7 @@ color OrenNayar::generate(const ray& r_in, const ray& scattered, const HitInfo& } double OrenNayar::pdf(const ray& r_in, const ray& scattered, const HitInfo& rec) const { - auto cos_theta = dot(rec.normal, -r_in.direction().unit_vector()); + auto cos_theta = dot(rec.normal, scattered.direction().unit_vector()); return fmax(0.0, cos_theta / pi); } From ae8b72f9e6a840bbb7886b20827a1134988b4381 Mon Sep 17 00:00:00 2001 From: connortbot Date: Mon, 30 Sep 2024 10:36:36 -0400 Subject: [PATCH 134/142] CA-89 Correct Lambert law in LayeredBSDF sampler --- src/materials/material.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/materials/material.cc b/src/materials/material.cc index 479c4dc..2c5d5ba 100644 --- a/src/materials/material.cc +++ b/src/materials/material.cc @@ -515,7 +515,7 @@ BSDFSample LayeredBSDF::sample(const ray& r_in, HitInfo& rec, ray& scattered) co pdf *= 1 - q; } - if (!prev_medium) { f = f * fabs(dot(rec_manip.normal, (r.direction().unit_vector()))); } + if (!prev_medium) { f = f * fabs(dot(rec_manip.normal, bs.scatter_direction.unit_vector())); } prev_medium = false; r = ray(rec_manip.pos - bs.scatter_direction, bs.scatter_direction, r.time()); From 54db406fe2a28dd1addfb8ccda5370e0f36cdb1d Mon Sep 17 00:00:00 2001 From: connortbot Date: Mon, 30 Sep 2024 10:36:48 -0400 Subject: [PATCH 135/142] CA-89 correct Lambert Law in integrator --- src/render.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/render.cc b/src/render.cc index 598d3df..21521ba 100644 --- a/src/render.cc +++ b/src/render.cc @@ -207,7 +207,7 @@ color trace_ray(const ray& r, std::shared_ptr scene, int depth) { if (!sample_data.scatter) { return accumulated_color; } - double cos_theta = fabs(dot(record.normal, -r_in.direction().unit_vector())); + double cos_theta = fabs(dot(record.normal, sample_data.scatter_direction.unit_vector())); if (!raymarched) { if (sample_data.pdf_value == 0) { return INVALID_SAMPLE; } weight = weight * (sample_data.bsdf_value * cos_theta / sample_data.pdf_value); From 720f19543e189029416b962c3648ba8d3e71b20d Mon Sep 17 00:00:00 2001 From: Pranav Bedi Date: Mon, 30 Sep 2024 14:07:28 -0400 Subject: [PATCH 136/142] Unix line endings for sh files --- .gitattributes | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 43118ac..fba8c75 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ -*.csr eol=lf \ No newline at end of file +*.csr eol=lf +*.sh eol=lf \ No newline at end of file From b3dea6bda0c1c699f269fb03464577af138c0e9a Mon Sep 17 00:00:00 2001 From: Pranav Bedi Date: Mon, 30 Sep 2024 14:09:57 -0400 Subject: [PATCH 137/142] Update .gitattributes --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index fba8c75..8010e9f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,2 @@ *.csr eol=lf -*.sh eol=lf \ No newline at end of file +*.sh text eol=lf \ No newline at end of file From 720b34d476b64b77d7e7418db1967ab9ff989102 Mon Sep 17 00:00:00 2001 From: connortbot Date: Mon, 30 Sep 2024 15:29:39 -0400 Subject: [PATCH 138/142] CA-89 dielectric grid test still needs light and texture in background --- tests/dielectric_grid.csr | 3249 +++++++++++++++++++++++++++++++++++++ 1 file changed, 3249 insertions(+) create mode 100644 tests/dielectric_grid.csr diff --git a/tests/dielectric_grid.csr b/tests/dielectric_grid.csr new file mode 100644 index 0000000..40d8bd4 --- /dev/null +++ b/tests/dielectric_grid.csr @@ -0,0 +1,3249 @@ +version 0.1.5 + +Camera +lookfrom -30 -10 0 +lookat 0 -10 0 +vup 0 1 0 +vfov 50 +aspect_ratio 16/9 +aperture 0.0001 +focus_dist 10.0 + +Sky +top 1.0 0.2 0.2 +bottom 0.2 0.2 1.0 + + +Material[Dielectric] +id 1 +albedo 1 1 1 +eta 1 +roughness 0.0001 +sheen 0 + +Sphere +id 1 +position 0 0 -20 +material 1 +radius 1 +medium false + +Material[Dielectric] +id 2 +albedo 1 1 1 +eta 1 +roughness 0.05 +sheen 0 + +Sphere +id 2 +position 0 0 -18 +material 2 +radius 1 +medium false + +Material[Dielectric] +id 3 +albedo 1 1 1 +eta 1 +roughness 0.1 +sheen 0 + +Sphere +id 3 +position 0 0 -16 +material 3 +radius 1 +medium false + +Material[Dielectric] +id 4 +albedo 1 1 1 +eta 1 +roughness 0.15 +sheen 0 + +Sphere +id 4 +position 0 0 -14 +material 4 +radius 1 +medium false + +Material[Dielectric] +id 5 +albedo 1 1 1 +eta 1 +roughness 0.2 +sheen 0 + +Sphere +id 5 +position 0 0 -12 +material 5 +radius 1 +medium false + +Material[Dielectric] +id 6 +albedo 1 1 1 +eta 1 +roughness 0.25 +sheen 0 + +Sphere +id 6 +position 0 0 -10 +material 6 +radius 1 +medium false + +Material[Dielectric] +id 7 +albedo 1 1 1 +eta 1 +roughness 0.3 +sheen 0 + +Sphere +id 7 +position 0 0 -8 +material 7 +radius 1 +medium false + +Material[Dielectric] +id 8 +albedo 1 1 1 +eta 1 +roughness 0.35 +sheen 0 + +Sphere +id 8 +position 0 0 -6 +material 8 +radius 1 +medium false + +Material[Dielectric] +id 9 +albedo 1 1 1 +eta 1 +roughness 0.4 +sheen 0 + +Sphere +id 9 +position 0 0 -4 +material 9 +radius 1 +medium false + +Material[Dielectric] +id 10 +albedo 1 1 1 +eta 1 +roughness 0.45 +sheen 0 + +Sphere +id 10 +position 0 0 -2 +material 10 +radius 1 +medium false + +Material[Dielectric] +id 11 +albedo 1 1 1 +eta 1 +roughness 0.5 +sheen 0 + +Sphere +id 11 +position 0 0 0 +material 11 +radius 1 +medium false + +Material[Dielectric] +id 12 +albedo 1 1 1 +eta 1 +roughness 0.55 +sheen 0 + +Sphere +id 12 +position 0 0 2 +material 12 +radius 1 +medium false + +Material[Dielectric] +id 13 +albedo 1 1 1 +eta 1 +roughness 0.6 +sheen 0 + +Sphere +id 13 +position 0 0 4 +material 13 +radius 1 +medium false + +Material[Dielectric] +id 14 +albedo 1 1 1 +eta 1 +roughness 0.65 +sheen 0 + +Sphere +id 14 +position 0 0 6 +material 14 +radius 1 +medium false + +Material[Dielectric] +id 15 +albedo 1 1 1 +eta 1 +roughness 0.7 +sheen 0 + +Sphere +id 15 +position 0 0 8 +material 15 +radius 1 +medium false + +Material[Dielectric] +id 16 +albedo 1 1 1 +eta 1 +roughness 0.75 +sheen 0 + +Sphere +id 16 +position 0 0 10 +material 16 +radius 1 +medium false + +Material[Dielectric] +id 17 +albedo 1 1 1 +eta 1 +roughness 0.8 +sheen 0 + +Sphere +id 17 +position 0 0 12 +material 17 +radius 1 +medium false + +Material[Dielectric] +id 18 +albedo 1 1 1 +eta 1 +roughness 0.85 +sheen 0 + +Sphere +id 18 +position 0 0 14 +material 18 +radius 1 +medium false + +Material[Dielectric] +id 19 +albedo 1 1 1 +eta 1 +roughness 0.9 +sheen 0 + +Sphere +id 19 +position 0 0 16 +material 19 +radius 1 +medium false + +Material[Dielectric] +id 20 +albedo 1 1 1 +eta 1 +roughness 0.95 +sheen 0 + +Sphere +id 20 +position 0 0 18 +material 20 +radius 1 +medium false + +Material[Dielectric] +id 21 +albedo 1 1 1 +eta 1 +roughness 1 +sheen 0 + +Sphere +id 21 +position 0 0 20 +material 21 +radius 1 +medium false + +Material[Dielectric] +id 22 +albedo 1 1 1 +eta 1.2 +roughness 0.0001 +sheen 0 + +Sphere +id 22 +position 0 -2 -20 +material 22 +radius 1 +medium false + +Material[Dielectric] +id 23 +albedo 1 1 1 +eta 1.2 +roughness 0.05 +sheen 0 + +Sphere +id 23 +position 0 -2 -18 +material 23 +radius 1 +medium false + +Material[Dielectric] +id 24 +albedo 1 1 1 +eta 1.2 +roughness 0.1 +sheen 0 + +Sphere +id 24 +position 0 -2 -16 +material 24 +radius 1 +medium false + +Material[Dielectric] +id 25 +albedo 1 1 1 +eta 1.2 +roughness 0.15 +sheen 0 + +Sphere +id 25 +position 0 -2 -14 +material 25 +radius 1 +medium false + +Material[Dielectric] +id 26 +albedo 1 1 1 +eta 1.2 +roughness 0.2 +sheen 0 + +Sphere +id 26 +position 0 -2 -12 +material 26 +radius 1 +medium false + +Material[Dielectric] +id 27 +albedo 1 1 1 +eta 1.2 +roughness 0.25 +sheen 0 + +Sphere +id 27 +position 0 -2 -10 +material 27 +radius 1 +medium false + +Material[Dielectric] +id 28 +albedo 1 1 1 +eta 1.2 +roughness 0.3 +sheen 0 + +Sphere +id 28 +position 0 -2 -8 +material 28 +radius 1 +medium false + +Material[Dielectric] +id 29 +albedo 1 1 1 +eta 1.2 +roughness 0.35 +sheen 0 + +Sphere +id 29 +position 0 -2 -6 +material 29 +radius 1 +medium false + +Material[Dielectric] +id 30 +albedo 1 1 1 +eta 1.2 +roughness 0.4 +sheen 0 + +Sphere +id 30 +position 0 -2 -4 +material 30 +radius 1 +medium false + +Material[Dielectric] +id 31 +albedo 1 1 1 +eta 1.2 +roughness 0.45 +sheen 0 + +Sphere +id 31 +position 0 -2 -2 +material 31 +radius 1 +medium false + +Material[Dielectric] +id 32 +albedo 1 1 1 +eta 1.2 +roughness 0.5 +sheen 0 + +Sphere +id 32 +position 0 -2 0 +material 32 +radius 1 +medium false + +Material[Dielectric] +id 33 +albedo 1 1 1 +eta 1.2 +roughness 0.55 +sheen 0 + +Sphere +id 33 +position 0 -2 2 +material 33 +radius 1 +medium false + +Material[Dielectric] +id 34 +albedo 1 1 1 +eta 1.2 +roughness 0.6 +sheen 0 + +Sphere +id 34 +position 0 -2 4 +material 34 +radius 1 +medium false + +Material[Dielectric] +id 35 +albedo 1 1 1 +eta 1.2 +roughness 0.65 +sheen 0 + +Sphere +id 35 +position 0 -2 6 +material 35 +radius 1 +medium false + +Material[Dielectric] +id 36 +albedo 1 1 1 +eta 1.2 +roughness 0.7 +sheen 0 + +Sphere +id 36 +position 0 -2 8 +material 36 +radius 1 +medium false + +Material[Dielectric] +id 37 +albedo 1 1 1 +eta 1.2 +roughness 0.75 +sheen 0 + +Sphere +id 37 +position 0 -2 10 +material 37 +radius 1 +medium false + +Material[Dielectric] +id 38 +albedo 1 1 1 +eta 1.2 +roughness 0.8 +sheen 0 + +Sphere +id 38 +position 0 -2 12 +material 38 +radius 1 +medium false + +Material[Dielectric] +id 39 +albedo 1 1 1 +eta 1.2 +roughness 0.85 +sheen 0 + +Sphere +id 39 +position 0 -2 14 +material 39 +radius 1 +medium false + +Material[Dielectric] +id 40 +albedo 1 1 1 +eta 1.2 +roughness 0.9 +sheen 0 + +Sphere +id 40 +position 0 -2 16 +material 40 +radius 1 +medium false + +Material[Dielectric] +id 41 +albedo 1 1 1 +eta 1.2 +roughness 0.95 +sheen 0 + +Sphere +id 41 +position 0 -2 18 +material 41 +radius 1 +medium false + +Material[Dielectric] +id 42 +albedo 1 1 1 +eta 1.2 +roughness 1 +sheen 0 + +Sphere +id 42 +position 0 -2 20 +material 42 +radius 1 +medium false + +Material[Dielectric] +id 43 +albedo 1 1 1 +eta 1.4 +roughness 0.0001 +sheen 0 + +Sphere +id 43 +position 0 -4 -20 +material 43 +radius 1 +medium false + +Material[Dielectric] +id 44 +albedo 1 1 1 +eta 1.4 +roughness 0.05 +sheen 0 + +Sphere +id 44 +position 0 -4 -18 +material 44 +radius 1 +medium false + +Material[Dielectric] +id 45 +albedo 1 1 1 +eta 1.4 +roughness 0.1 +sheen 0 + +Sphere +id 45 +position 0 -4 -16 +material 45 +radius 1 +medium false + +Material[Dielectric] +id 46 +albedo 1 1 1 +eta 1.4 +roughness 0.15 +sheen 0 + +Sphere +id 46 +position 0 -4 -14 +material 46 +radius 1 +medium false + +Material[Dielectric] +id 47 +albedo 1 1 1 +eta 1.4 +roughness 0.2 +sheen 0 + +Sphere +id 47 +position 0 -4 -12 +material 47 +radius 1 +medium false + +Material[Dielectric] +id 48 +albedo 1 1 1 +eta 1.4 +roughness 0.25 +sheen 0 + +Sphere +id 48 +position 0 -4 -10 +material 48 +radius 1 +medium false + +Material[Dielectric] +id 49 +albedo 1 1 1 +eta 1.4 +roughness 0.3 +sheen 0 + +Sphere +id 49 +position 0 -4 -8 +material 49 +radius 1 +medium false + +Material[Dielectric] +id 50 +albedo 1 1 1 +eta 1.4 +roughness 0.35 +sheen 0 + +Sphere +id 50 +position 0 -4 -6 +material 50 +radius 1 +medium false + +Material[Dielectric] +id 51 +albedo 1 1 1 +eta 1.4 +roughness 0.4 +sheen 0 + +Sphere +id 51 +position 0 -4 -4 +material 51 +radius 1 +medium false + +Material[Dielectric] +id 52 +albedo 1 1 1 +eta 1.4 +roughness 0.45 +sheen 0 + +Sphere +id 52 +position 0 -4 -2 +material 52 +radius 1 +medium false + +Material[Dielectric] +id 53 +albedo 1 1 1 +eta 1.4 +roughness 0.5 +sheen 0 + +Sphere +id 53 +position 0 -4 0 +material 53 +radius 1 +medium false + +Material[Dielectric] +id 54 +albedo 1 1 1 +eta 1.4 +roughness 0.55 +sheen 0 + +Sphere +id 54 +position 0 -4 2 +material 54 +radius 1 +medium false + +Material[Dielectric] +id 55 +albedo 1 1 1 +eta 1.4 +roughness 0.6 +sheen 0 + +Sphere +id 55 +position 0 -4 4 +material 55 +radius 1 +medium false + +Material[Dielectric] +id 56 +albedo 1 1 1 +eta 1.4 +roughness 0.65 +sheen 0 + +Sphere +id 56 +position 0 -4 6 +material 56 +radius 1 +medium false + +Material[Dielectric] +id 57 +albedo 1 1 1 +eta 1.4 +roughness 0.7 +sheen 0 + +Sphere +id 57 +position 0 -4 8 +material 57 +radius 1 +medium false + +Material[Dielectric] +id 58 +albedo 1 1 1 +eta 1.4 +roughness 0.75 +sheen 0 + +Sphere +id 58 +position 0 -4 10 +material 58 +radius 1 +medium false + +Material[Dielectric] +id 59 +albedo 1 1 1 +eta 1.4 +roughness 0.8 +sheen 0 + +Sphere +id 59 +position 0 -4 12 +material 59 +radius 1 +medium false + +Material[Dielectric] +id 60 +albedo 1 1 1 +eta 1.4 +roughness 0.85 +sheen 0 + +Sphere +id 60 +position 0 -4 14 +material 60 +radius 1 +medium false + +Material[Dielectric] +id 61 +albedo 1 1 1 +eta 1.4 +roughness 0.9 +sheen 0 + +Sphere +id 61 +position 0 -4 16 +material 61 +radius 1 +medium false + +Material[Dielectric] +id 62 +albedo 1 1 1 +eta 1.4 +roughness 0.95 +sheen 0 + +Sphere +id 62 +position 0 -4 18 +material 62 +radius 1 +medium false + +Material[Dielectric] +id 63 +albedo 1 1 1 +eta 1.4 +roughness 1 +sheen 0 + +Sphere +id 63 +position 0 -4 20 +material 63 +radius 1 +medium false + +Material[Dielectric] +id 64 +albedo 1 1 1 +eta 1.6 +roughness 0.0001 +sheen 0 + +Sphere +id 64 +position 0 -6 -20 +material 64 +radius 1 +medium false + +Material[Dielectric] +id 65 +albedo 1 1 1 +eta 1.6 +roughness 0.05 +sheen 0 + +Sphere +id 65 +position 0 -6 -18 +material 65 +radius 1 +medium false + +Material[Dielectric] +id 66 +albedo 1 1 1 +eta 1.6 +roughness 0.1 +sheen 0 + +Sphere +id 66 +position 0 -6 -16 +material 66 +radius 1 +medium false + +Material[Dielectric] +id 67 +albedo 1 1 1 +eta 1.6 +roughness 0.15 +sheen 0 + +Sphere +id 67 +position 0 -6 -14 +material 67 +radius 1 +medium false + +Material[Dielectric] +id 68 +albedo 1 1 1 +eta 1.6 +roughness 0.2 +sheen 0 + +Sphere +id 68 +position 0 -6 -12 +material 68 +radius 1 +medium false + +Material[Dielectric] +id 69 +albedo 1 1 1 +eta 1.6 +roughness 0.25 +sheen 0 + +Sphere +id 69 +position 0 -6 -10 +material 69 +radius 1 +medium false + +Material[Dielectric] +id 70 +albedo 1 1 1 +eta 1.6 +roughness 0.3 +sheen 0 + +Sphere +id 70 +position 0 -6 -8 +material 70 +radius 1 +medium false + +Material[Dielectric] +id 71 +albedo 1 1 1 +eta 1.6 +roughness 0.35 +sheen 0 + +Sphere +id 71 +position 0 -6 -6 +material 71 +radius 1 +medium false + +Material[Dielectric] +id 72 +albedo 1 1 1 +eta 1.6 +roughness 0.4 +sheen 0 + +Sphere +id 72 +position 0 -6 -4 +material 72 +radius 1 +medium false + +Material[Dielectric] +id 73 +albedo 1 1 1 +eta 1.6 +roughness 0.45 +sheen 0 + +Sphere +id 73 +position 0 -6 -2 +material 73 +radius 1 +medium false + +Material[Dielectric] +id 74 +albedo 1 1 1 +eta 1.6 +roughness 0.5 +sheen 0 + +Sphere +id 74 +position 0 -6 0 +material 74 +radius 1 +medium false + +Material[Dielectric] +id 75 +albedo 1 1 1 +eta 1.6 +roughness 0.55 +sheen 0 + +Sphere +id 75 +position 0 -6 2 +material 75 +radius 1 +medium false + +Material[Dielectric] +id 76 +albedo 1 1 1 +eta 1.6 +roughness 0.6 +sheen 0 + +Sphere +id 76 +position 0 -6 4 +material 76 +radius 1 +medium false + +Material[Dielectric] +id 77 +albedo 1 1 1 +eta 1.6 +roughness 0.65 +sheen 0 + +Sphere +id 77 +position 0 -6 6 +material 77 +radius 1 +medium false + +Material[Dielectric] +id 78 +albedo 1 1 1 +eta 1.6 +roughness 0.7 +sheen 0 + +Sphere +id 78 +position 0 -6 8 +material 78 +radius 1 +medium false + +Material[Dielectric] +id 79 +albedo 1 1 1 +eta 1.6 +roughness 0.75 +sheen 0 + +Sphere +id 79 +position 0 -6 10 +material 79 +radius 1 +medium false + +Material[Dielectric] +id 80 +albedo 1 1 1 +eta 1.6 +roughness 0.8 +sheen 0 + +Sphere +id 80 +position 0 -6 12 +material 80 +radius 1 +medium false + +Material[Dielectric] +id 81 +albedo 1 1 1 +eta 1.6 +roughness 0.85 +sheen 0 + +Sphere +id 81 +position 0 -6 14 +material 81 +radius 1 +medium false + +Material[Dielectric] +id 82 +albedo 1 1 1 +eta 1.6 +roughness 0.9 +sheen 0 + +Sphere +id 82 +position 0 -6 16 +material 82 +radius 1 +medium false + +Material[Dielectric] +id 83 +albedo 1 1 1 +eta 1.6 +roughness 0.95 +sheen 0 + +Sphere +id 83 +position 0 -6 18 +material 83 +radius 1 +medium false + +Material[Dielectric] +id 84 +albedo 1 1 1 +eta 1.6 +roughness 1 +sheen 0 + +Sphere +id 84 +position 0 -6 20 +material 84 +radius 1 +medium false + +Material[Dielectric] +id 85 +albedo 1 1 1 +eta 1.8 +roughness 0.0001 +sheen 0 + +Sphere +id 85 +position 0 -8 -20 +material 85 +radius 1 +medium false + +Material[Dielectric] +id 86 +albedo 1 1 1 +eta 1.8 +roughness 0.05 +sheen 0 + +Sphere +id 86 +position 0 -8 -18 +material 86 +radius 1 +medium false + +Material[Dielectric] +id 87 +albedo 1 1 1 +eta 1.8 +roughness 0.1 +sheen 0 + +Sphere +id 87 +position 0 -8 -16 +material 87 +radius 1 +medium false + +Material[Dielectric] +id 88 +albedo 1 1 1 +eta 1.8 +roughness 0.15 +sheen 0 + +Sphere +id 88 +position 0 -8 -14 +material 88 +radius 1 +medium false + +Material[Dielectric] +id 89 +albedo 1 1 1 +eta 1.8 +roughness 0.2 +sheen 0 + +Sphere +id 89 +position 0 -8 -12 +material 89 +radius 1 +medium false + +Material[Dielectric] +id 90 +albedo 1 1 1 +eta 1.8 +roughness 0.25 +sheen 0 + +Sphere +id 90 +position 0 -8 -10 +material 90 +radius 1 +medium false + +Material[Dielectric] +id 91 +albedo 1 1 1 +eta 1.8 +roughness 0.3 +sheen 0 + +Sphere +id 91 +position 0 -8 -8 +material 91 +radius 1 +medium false + +Material[Dielectric] +id 92 +albedo 1 1 1 +eta 1.8 +roughness 0.35 +sheen 0 + +Sphere +id 92 +position 0 -8 -6 +material 92 +radius 1 +medium false + +Material[Dielectric] +id 93 +albedo 1 1 1 +eta 1.8 +roughness 0.4 +sheen 0 + +Sphere +id 93 +position 0 -8 -4 +material 93 +radius 1 +medium false + +Material[Dielectric] +id 94 +albedo 1 1 1 +eta 1.8 +roughness 0.45 +sheen 0 + +Sphere +id 94 +position 0 -8 -2 +material 94 +radius 1 +medium false + +Material[Dielectric] +id 95 +albedo 1 1 1 +eta 1.8 +roughness 0.5 +sheen 0 + +Sphere +id 95 +position 0 -8 0 +material 95 +radius 1 +medium false + +Material[Dielectric] +id 96 +albedo 1 1 1 +eta 1.8 +roughness 0.55 +sheen 0 + +Sphere +id 96 +position 0 -8 2 +material 96 +radius 1 +medium false + +Material[Dielectric] +id 97 +albedo 1 1 1 +eta 1.8 +roughness 0.6 +sheen 0 + +Sphere +id 97 +position 0 -8 4 +material 97 +radius 1 +medium false + +Material[Dielectric] +id 98 +albedo 1 1 1 +eta 1.8 +roughness 0.65 +sheen 0 + +Sphere +id 98 +position 0 -8 6 +material 98 +radius 1 +medium false + +Material[Dielectric] +id 99 +albedo 1 1 1 +eta 1.8 +roughness 0.7 +sheen 0 + +Sphere +id 99 +position 0 -8 8 +material 99 +radius 1 +medium false + +Material[Dielectric] +id 100 +albedo 1 1 1 +eta 1.8 +roughness 0.75 +sheen 0 + +Sphere +id 100 +position 0 -8 10 +material 100 +radius 1 +medium false + +Material[Dielectric] +id 101 +albedo 1 1 1 +eta 1.8 +roughness 0.8 +sheen 0 + +Sphere +id 101 +position 0 -8 12 +material 101 +radius 1 +medium false + +Material[Dielectric] +id 102 +albedo 1 1 1 +eta 1.8 +roughness 0.85 +sheen 0 + +Sphere +id 102 +position 0 -8 14 +material 102 +radius 1 +medium false + +Material[Dielectric] +id 103 +albedo 1 1 1 +eta 1.8 +roughness 0.9 +sheen 0 + +Sphere +id 103 +position 0 -8 16 +material 103 +radius 1 +medium false + +Material[Dielectric] +id 104 +albedo 1 1 1 +eta 1.8 +roughness 0.95 +sheen 0 + +Sphere +id 104 +position 0 -8 18 +material 104 +radius 1 +medium false + +Material[Dielectric] +id 105 +albedo 1 1 1 +eta 1.8 +roughness 1 +sheen 0 + +Sphere +id 105 +position 0 -8 20 +material 105 +radius 1 +medium false + +Material[Dielectric] +id 106 +albedo 1 1 1 +eta 2 +roughness 0.0001 +sheen 0 + +Sphere +id 106 +position 0 -10 -20 +material 106 +radius 1 +medium false + +Material[Dielectric] +id 107 +albedo 1 1 1 +eta 2 +roughness 0.05 +sheen 0 + +Sphere +id 107 +position 0 -10 -18 +material 107 +radius 1 +medium false + +Material[Dielectric] +id 108 +albedo 1 1 1 +eta 2 +roughness 0.1 +sheen 0 + +Sphere +id 108 +position 0 -10 -16 +material 108 +radius 1 +medium false + +Material[Dielectric] +id 109 +albedo 1 1 1 +eta 2 +roughness 0.15 +sheen 0 + +Sphere +id 109 +position 0 -10 -14 +material 109 +radius 1 +medium false + +Material[Dielectric] +id 110 +albedo 1 1 1 +eta 2 +roughness 0.2 +sheen 0 + +Sphere +id 110 +position 0 -10 -12 +material 110 +radius 1 +medium false + +Material[Dielectric] +id 111 +albedo 1 1 1 +eta 2 +roughness 0.25 +sheen 0 + +Sphere +id 111 +position 0 -10 -10 +material 111 +radius 1 +medium false + +Material[Dielectric] +id 112 +albedo 1 1 1 +eta 2 +roughness 0.3 +sheen 0 + +Sphere +id 112 +position 0 -10 -8 +material 112 +radius 1 +medium false + +Material[Dielectric] +id 113 +albedo 1 1 1 +eta 2 +roughness 0.35 +sheen 0 + +Sphere +id 113 +position 0 -10 -6 +material 113 +radius 1 +medium false + +Material[Dielectric] +id 114 +albedo 1 1 1 +eta 2 +roughness 0.4 +sheen 0 + +Sphere +id 114 +position 0 -10 -4 +material 114 +radius 1 +medium false + +Material[Dielectric] +id 115 +albedo 1 1 1 +eta 2 +roughness 0.45 +sheen 0 + +Sphere +id 115 +position 0 -10 -2 +material 115 +radius 1 +medium false + +Material[Dielectric] +id 116 +albedo 1 1 1 +eta 2 +roughness 0.5 +sheen 0 + +Sphere +id 116 +position 0 -10 0 +material 116 +radius 1 +medium false + +Material[Dielectric] +id 117 +albedo 1 1 1 +eta 2 +roughness 0.55 +sheen 0 + +Sphere +id 117 +position 0 -10 2 +material 117 +radius 1 +medium false + +Material[Dielectric] +id 118 +albedo 1 1 1 +eta 2 +roughness 0.6 +sheen 0 + +Sphere +id 118 +position 0 -10 4 +material 118 +radius 1 +medium false + +Material[Dielectric] +id 119 +albedo 1 1 1 +eta 2 +roughness 0.65 +sheen 0 + +Sphere +id 119 +position 0 -10 6 +material 119 +radius 1 +medium false + +Material[Dielectric] +id 120 +albedo 1 1 1 +eta 2 +roughness 0.7 +sheen 0 + +Sphere +id 120 +position 0 -10 8 +material 120 +radius 1 +medium false + +Material[Dielectric] +id 121 +albedo 1 1 1 +eta 2 +roughness 0.75 +sheen 0 + +Sphere +id 121 +position 0 -10 10 +material 121 +radius 1 +medium false + +Material[Dielectric] +id 122 +albedo 1 1 1 +eta 2 +roughness 0.8 +sheen 0 + +Sphere +id 122 +position 0 -10 12 +material 122 +radius 1 +medium false + +Material[Dielectric] +id 123 +albedo 1 1 1 +eta 2 +roughness 0.85 +sheen 0 + +Sphere +id 123 +position 0 -10 14 +material 123 +radius 1 +medium false + +Material[Dielectric] +id 124 +albedo 1 1 1 +eta 2 +roughness 0.9 +sheen 0 + +Sphere +id 124 +position 0 -10 16 +material 124 +radius 1 +medium false + +Material[Dielectric] +id 125 +albedo 1 1 1 +eta 2 +roughness 0.95 +sheen 0 + +Sphere +id 125 +position 0 -10 18 +material 125 +radius 1 +medium false + +Material[Dielectric] +id 126 +albedo 1 1 1 +eta 2 +roughness 1 +sheen 0 + +Sphere +id 126 +position 0 -10 20 +material 126 +radius 1 +medium false + +Material[Dielectric] +id 127 +albedo 1 1 1 +eta 2.2 +roughness 0.0001 +sheen 0 + +Sphere +id 127 +position 0 -12 -20 +material 127 +radius 1 +medium false + +Material[Dielectric] +id 128 +albedo 1 1 1 +eta 2.2 +roughness 0.05 +sheen 0 + +Sphere +id 128 +position 0 -12 -18 +material 128 +radius 1 +medium false + +Material[Dielectric] +id 129 +albedo 1 1 1 +eta 2.2 +roughness 0.1 +sheen 0 + +Sphere +id 129 +position 0 -12 -16 +material 129 +radius 1 +medium false + +Material[Dielectric] +id 130 +albedo 1 1 1 +eta 2.2 +roughness 0.15 +sheen 0 + +Sphere +id 130 +position 0 -12 -14 +material 130 +radius 1 +medium false + +Material[Dielectric] +id 131 +albedo 1 1 1 +eta 2.2 +roughness 0.2 +sheen 0 + +Sphere +id 131 +position 0 -12 -12 +material 131 +radius 1 +medium false + +Material[Dielectric] +id 132 +albedo 1 1 1 +eta 2.2 +roughness 0.25 +sheen 0 + +Sphere +id 132 +position 0 -12 -10 +material 132 +radius 1 +medium false + +Material[Dielectric] +id 133 +albedo 1 1 1 +eta 2.2 +roughness 0.3 +sheen 0 + +Sphere +id 133 +position 0 -12 -8 +material 133 +radius 1 +medium false + +Material[Dielectric] +id 134 +albedo 1 1 1 +eta 2.2 +roughness 0.35 +sheen 0 + +Sphere +id 134 +position 0 -12 -6 +material 134 +radius 1 +medium false + +Material[Dielectric] +id 135 +albedo 1 1 1 +eta 2.2 +roughness 0.4 +sheen 0 + +Sphere +id 135 +position 0 -12 -4 +material 135 +radius 1 +medium false + +Material[Dielectric] +id 136 +albedo 1 1 1 +eta 2.2 +roughness 0.45 +sheen 0 + +Sphere +id 136 +position 0 -12 -2 +material 136 +radius 1 +medium false + +Material[Dielectric] +id 137 +albedo 1 1 1 +eta 2.2 +roughness 0.5 +sheen 0 + +Sphere +id 137 +position 0 -12 0 +material 137 +radius 1 +medium false + +Material[Dielectric] +id 138 +albedo 1 1 1 +eta 2.2 +roughness 0.55 +sheen 0 + +Sphere +id 138 +position 0 -12 2 +material 138 +radius 1 +medium false + +Material[Dielectric] +id 139 +albedo 1 1 1 +eta 2.2 +roughness 0.6 +sheen 0 + +Sphere +id 139 +position 0 -12 4 +material 139 +radius 1 +medium false + +Material[Dielectric] +id 140 +albedo 1 1 1 +eta 2.2 +roughness 0.65 +sheen 0 + +Sphere +id 140 +position 0 -12 6 +material 140 +radius 1 +medium false + +Material[Dielectric] +id 141 +albedo 1 1 1 +eta 2.2 +roughness 0.7 +sheen 0 + +Sphere +id 141 +position 0 -12 8 +material 141 +radius 1 +medium false + +Material[Dielectric] +id 142 +albedo 1 1 1 +eta 2.2 +roughness 0.75 +sheen 0 + +Sphere +id 142 +position 0 -12 10 +material 142 +radius 1 +medium false + +Material[Dielectric] +id 143 +albedo 1 1 1 +eta 2.2 +roughness 0.8 +sheen 0 + +Sphere +id 143 +position 0 -12 12 +material 143 +radius 1 +medium false + +Material[Dielectric] +id 144 +albedo 1 1 1 +eta 2.2 +roughness 0.85 +sheen 0 + +Sphere +id 144 +position 0 -12 14 +material 144 +radius 1 +medium false + +Material[Dielectric] +id 145 +albedo 1 1 1 +eta 2.2 +roughness 0.9 +sheen 0 + +Sphere +id 145 +position 0 -12 16 +material 145 +radius 1 +medium false + +Material[Dielectric] +id 146 +albedo 1 1 1 +eta 2.2 +roughness 0.95 +sheen 0 + +Sphere +id 146 +position 0 -12 18 +material 146 +radius 1 +medium false + +Material[Dielectric] +id 147 +albedo 1 1 1 +eta 2.2 +roughness 1 +sheen 0 + +Sphere +id 147 +position 0 -12 20 +material 147 +radius 1 +medium false + +Material[Dielectric] +id 148 +albedo 1 1 1 +eta 2.4 +roughness 0.0001 +sheen 0 + +Sphere +id 148 +position 0 -14 -20 +material 148 +radius 1 +medium false + +Material[Dielectric] +id 149 +albedo 1 1 1 +eta 2.4 +roughness 0.05 +sheen 0 + +Sphere +id 149 +position 0 -14 -18 +material 149 +radius 1 +medium false + +Material[Dielectric] +id 150 +albedo 1 1 1 +eta 2.4 +roughness 0.1 +sheen 0 + +Sphere +id 150 +position 0 -14 -16 +material 150 +radius 1 +medium false + +Material[Dielectric] +id 151 +albedo 1 1 1 +eta 2.4 +roughness 0.15 +sheen 0 + +Sphere +id 151 +position 0 -14 -14 +material 151 +radius 1 +medium false + +Material[Dielectric] +id 152 +albedo 1 1 1 +eta 2.4 +roughness 0.2 +sheen 0 + +Sphere +id 152 +position 0 -14 -12 +material 152 +radius 1 +medium false + +Material[Dielectric] +id 153 +albedo 1 1 1 +eta 2.4 +roughness 0.25 +sheen 0 + +Sphere +id 153 +position 0 -14 -10 +material 153 +radius 1 +medium false + +Material[Dielectric] +id 154 +albedo 1 1 1 +eta 2.4 +roughness 0.3 +sheen 0 + +Sphere +id 154 +position 0 -14 -8 +material 154 +radius 1 +medium false + +Material[Dielectric] +id 155 +albedo 1 1 1 +eta 2.4 +roughness 0.35 +sheen 0 + +Sphere +id 155 +position 0 -14 -6 +material 155 +radius 1 +medium false + +Material[Dielectric] +id 156 +albedo 1 1 1 +eta 2.4 +roughness 0.4 +sheen 0 + +Sphere +id 156 +position 0 -14 -4 +material 156 +radius 1 +medium false + +Material[Dielectric] +id 157 +albedo 1 1 1 +eta 2.4 +roughness 0.45 +sheen 0 + +Sphere +id 157 +position 0 -14 -2 +material 157 +radius 1 +medium false + +Material[Dielectric] +id 158 +albedo 1 1 1 +eta 2.4 +roughness 0.5 +sheen 0 + +Sphere +id 158 +position 0 -14 0 +material 158 +radius 1 +medium false + +Material[Dielectric] +id 159 +albedo 1 1 1 +eta 2.4 +roughness 0.55 +sheen 0 + +Sphere +id 159 +position 0 -14 2 +material 159 +radius 1 +medium false + +Material[Dielectric] +id 160 +albedo 1 1 1 +eta 2.4 +roughness 0.6 +sheen 0 + +Sphere +id 160 +position 0 -14 4 +material 160 +radius 1 +medium false + +Material[Dielectric] +id 161 +albedo 1 1 1 +eta 2.4 +roughness 0.65 +sheen 0 + +Sphere +id 161 +position 0 -14 6 +material 161 +radius 1 +medium false + +Material[Dielectric] +id 162 +albedo 1 1 1 +eta 2.4 +roughness 0.7 +sheen 0 + +Sphere +id 162 +position 0 -14 8 +material 162 +radius 1 +medium false + +Material[Dielectric] +id 163 +albedo 1 1 1 +eta 2.4 +roughness 0.75 +sheen 0 + +Sphere +id 163 +position 0 -14 10 +material 163 +radius 1 +medium false + +Material[Dielectric] +id 164 +albedo 1 1 1 +eta 2.4 +roughness 0.8 +sheen 0 + +Sphere +id 164 +position 0 -14 12 +material 164 +radius 1 +medium false + +Material[Dielectric] +id 165 +albedo 1 1 1 +eta 2.4 +roughness 0.85 +sheen 0 + +Sphere +id 165 +position 0 -14 14 +material 165 +radius 1 +medium false + +Material[Dielectric] +id 166 +albedo 1 1 1 +eta 2.4 +roughness 0.9 +sheen 0 + +Sphere +id 166 +position 0 -14 16 +material 166 +radius 1 +medium false + +Material[Dielectric] +id 167 +albedo 1 1 1 +eta 2.4 +roughness 0.95 +sheen 0 + +Sphere +id 167 +position 0 -14 18 +material 167 +radius 1 +medium false + +Material[Dielectric] +id 168 +albedo 1 1 1 +eta 2.4 +roughness 1 +sheen 0 + +Sphere +id 168 +position 0 -14 20 +material 168 +radius 1 +medium false + +Material[Dielectric] +id 169 +albedo 1 1 1 +eta 2.6 +roughness 0.0001 +sheen 0 + +Sphere +id 169 +position 0 -16 -20 +material 169 +radius 1 +medium false + +Material[Dielectric] +id 170 +albedo 1 1 1 +eta 2.6 +roughness 0.05 +sheen 0 + +Sphere +id 170 +position 0 -16 -18 +material 170 +radius 1 +medium false + +Material[Dielectric] +id 171 +albedo 1 1 1 +eta 2.6 +roughness 0.1 +sheen 0 + +Sphere +id 171 +position 0 -16 -16 +material 171 +radius 1 +medium false + +Material[Dielectric] +id 172 +albedo 1 1 1 +eta 2.6 +roughness 0.15 +sheen 0 + +Sphere +id 172 +position 0 -16 -14 +material 172 +radius 1 +medium false + +Material[Dielectric] +id 173 +albedo 1 1 1 +eta 2.6 +roughness 0.2 +sheen 0 + +Sphere +id 173 +position 0 -16 -12 +material 173 +radius 1 +medium false + +Material[Dielectric] +id 174 +albedo 1 1 1 +eta 2.6 +roughness 0.25 +sheen 0 + +Sphere +id 174 +position 0 -16 -10 +material 174 +radius 1 +medium false + +Material[Dielectric] +id 175 +albedo 1 1 1 +eta 2.6 +roughness 0.3 +sheen 0 + +Sphere +id 175 +position 0 -16 -8 +material 175 +radius 1 +medium false + +Material[Dielectric] +id 176 +albedo 1 1 1 +eta 2.6 +roughness 0.35 +sheen 0 + +Sphere +id 176 +position 0 -16 -6 +material 176 +radius 1 +medium false + +Material[Dielectric] +id 177 +albedo 1 1 1 +eta 2.6 +roughness 0.4 +sheen 0 + +Sphere +id 177 +position 0 -16 -4 +material 177 +radius 1 +medium false + +Material[Dielectric] +id 178 +albedo 1 1 1 +eta 2.6 +roughness 0.45 +sheen 0 + +Sphere +id 178 +position 0 -16 -2 +material 178 +radius 1 +medium false + +Material[Dielectric] +id 179 +albedo 1 1 1 +eta 2.6 +roughness 0.5 +sheen 0 + +Sphere +id 179 +position 0 -16 0 +material 179 +radius 1 +medium false + +Material[Dielectric] +id 180 +albedo 1 1 1 +eta 2.6 +roughness 0.55 +sheen 0 + +Sphere +id 180 +position 0 -16 2 +material 180 +radius 1 +medium false + +Material[Dielectric] +id 181 +albedo 1 1 1 +eta 2.6 +roughness 0.6 +sheen 0 + +Sphere +id 181 +position 0 -16 4 +material 181 +radius 1 +medium false + +Material[Dielectric] +id 182 +albedo 1 1 1 +eta 2.6 +roughness 0.65 +sheen 0 + +Sphere +id 182 +position 0 -16 6 +material 182 +radius 1 +medium false + +Material[Dielectric] +id 183 +albedo 1 1 1 +eta 2.6 +roughness 0.7 +sheen 0 + +Sphere +id 183 +position 0 -16 8 +material 183 +radius 1 +medium false + +Material[Dielectric] +id 184 +albedo 1 1 1 +eta 2.6 +roughness 0.75 +sheen 0 + +Sphere +id 184 +position 0 -16 10 +material 184 +radius 1 +medium false + +Material[Dielectric] +id 185 +albedo 1 1 1 +eta 2.6 +roughness 0.8 +sheen 0 + +Sphere +id 185 +position 0 -16 12 +material 185 +radius 1 +medium false + +Material[Dielectric] +id 186 +albedo 1 1 1 +eta 2.6 +roughness 0.85 +sheen 0 + +Sphere +id 186 +position 0 -16 14 +material 186 +radius 1 +medium false + +Material[Dielectric] +id 187 +albedo 1 1 1 +eta 2.6 +roughness 0.9 +sheen 0 + +Sphere +id 187 +position 0 -16 16 +material 187 +radius 1 +medium false + +Material[Dielectric] +id 188 +albedo 1 1 1 +eta 2.6 +roughness 0.95 +sheen 0 + +Sphere +id 188 +position 0 -16 18 +material 188 +radius 1 +medium false + +Material[Dielectric] +id 189 +albedo 1 1 1 +eta 2.6 +roughness 1 +sheen 0 + +Sphere +id 189 +position 0 -16 20 +material 189 +radius 1 +medium false + +Material[Dielectric] +id 190 +albedo 1 1 1 +eta 2.8 +roughness 0.0001 +sheen 0 + +Sphere +id 190 +position 0 -18 -20 +material 190 +radius 1 +medium false + +Material[Dielectric] +id 191 +albedo 1 1 1 +eta 2.8 +roughness 0.05 +sheen 0 + +Sphere +id 191 +position 0 -18 -18 +material 191 +radius 1 +medium false + +Material[Dielectric] +id 192 +albedo 1 1 1 +eta 2.8 +roughness 0.1 +sheen 0 + +Sphere +id 192 +position 0 -18 -16 +material 192 +radius 1 +medium false + +Material[Dielectric] +id 193 +albedo 1 1 1 +eta 2.8 +roughness 0.15 +sheen 0 + +Sphere +id 193 +position 0 -18 -14 +material 193 +radius 1 +medium false + +Material[Dielectric] +id 194 +albedo 1 1 1 +eta 2.8 +roughness 0.2 +sheen 0 + +Sphere +id 194 +position 0 -18 -12 +material 194 +radius 1 +medium false + +Material[Dielectric] +id 195 +albedo 1 1 1 +eta 2.8 +roughness 0.25 +sheen 0 + +Sphere +id 195 +position 0 -18 -10 +material 195 +radius 1 +medium false + +Material[Dielectric] +id 196 +albedo 1 1 1 +eta 2.8 +roughness 0.3 +sheen 0 + +Sphere +id 196 +position 0 -18 -8 +material 196 +radius 1 +medium false + +Material[Dielectric] +id 197 +albedo 1 1 1 +eta 2.8 +roughness 0.35 +sheen 0 + +Sphere +id 197 +position 0 -18 -6 +material 197 +radius 1 +medium false + +Material[Dielectric] +id 198 +albedo 1 1 1 +eta 2.8 +roughness 0.4 +sheen 0 + +Sphere +id 198 +position 0 -18 -4 +material 198 +radius 1 +medium false + +Material[Dielectric] +id 199 +albedo 1 1 1 +eta 2.8 +roughness 0.45 +sheen 0 + +Sphere +id 199 +position 0 -18 -2 +material 199 +radius 1 +medium false + +Material[Dielectric] +id 200 +albedo 1 1 1 +eta 2.8 +roughness 0.5 +sheen 0 + +Sphere +id 200 +position 0 -18 0 +material 200 +radius 1 +medium false + +Material[Dielectric] +id 201 +albedo 1 1 1 +eta 2.8 +roughness 0.55 +sheen 0 + +Sphere +id 201 +position 0 -18 2 +material 201 +radius 1 +medium false + +Material[Dielectric] +id 202 +albedo 1 1 1 +eta 2.8 +roughness 0.6 +sheen 0 + +Sphere +id 202 +position 0 -18 4 +material 202 +radius 1 +medium false + +Material[Dielectric] +id 203 +albedo 1 1 1 +eta 2.8 +roughness 0.65 +sheen 0 + +Sphere +id 203 +position 0 -18 6 +material 203 +radius 1 +medium false + +Material[Dielectric] +id 204 +albedo 1 1 1 +eta 2.8 +roughness 0.7 +sheen 0 + +Sphere +id 204 +position 0 -18 8 +material 204 +radius 1 +medium false + +Material[Dielectric] +id 205 +albedo 1 1 1 +eta 2.8 +roughness 0.75 +sheen 0 + +Sphere +id 205 +position 0 -18 10 +material 205 +radius 1 +medium false + +Material[Dielectric] +id 206 +albedo 1 1 1 +eta 2.8 +roughness 0.8 +sheen 0 + +Sphere +id 206 +position 0 -18 12 +material 206 +radius 1 +medium false + +Material[Dielectric] +id 207 +albedo 1 1 1 +eta 2.8 +roughness 0.85 +sheen 0 + +Sphere +id 207 +position 0 -18 14 +material 207 +radius 1 +medium false + +Material[Dielectric] +id 208 +albedo 1 1 1 +eta 2.8 +roughness 0.9 +sheen 0 + +Sphere +id 208 +position 0 -18 16 +material 208 +radius 1 +medium false + +Material[Dielectric] +id 209 +albedo 1 1 1 +eta 2.8 +roughness 0.95 +sheen 0 + +Sphere +id 209 +position 0 -18 18 +material 209 +radius 1 +medium false + +Material[Dielectric] +id 210 +albedo 1 1 1 +eta 2.8 +roughness 1 +sheen 0 + +Sphere +id 210 +position 0 -18 20 +material 210 +radius 1 +medium false + +Material[Dielectric] +id 211 +albedo 1 1 1 +eta 3 +roughness 0.0001 +sheen 0 + +Sphere +id 211 +position 0 -20 -20 +material 211 +radius 1 +medium false + +Material[Dielectric] +id 212 +albedo 1 1 1 +eta 3 +roughness 0.05 +sheen 0 + +Sphere +id 212 +position 0 -20 -18 +material 212 +radius 1 +medium false + +Material[Dielectric] +id 213 +albedo 1 1 1 +eta 3 +roughness 0.1 +sheen 0 + +Sphere +id 213 +position 0 -20 -16 +material 213 +radius 1 +medium false + +Material[Dielectric] +id 214 +albedo 1 1 1 +eta 3 +roughness 0.15 +sheen 0 + +Sphere +id 214 +position 0 -20 -14 +material 214 +radius 1 +medium false + +Material[Dielectric] +id 215 +albedo 1 1 1 +eta 3 +roughness 0.2 +sheen 0 + +Sphere +id 215 +position 0 -20 -12 +material 215 +radius 1 +medium false + +Material[Dielectric] +id 216 +albedo 1 1 1 +eta 3 +roughness 0.25 +sheen 0 + +Sphere +id 216 +position 0 -20 -10 +material 216 +radius 1 +medium false + +Material[Dielectric] +id 217 +albedo 1 1 1 +eta 3 +roughness 0.3 +sheen 0 + +Sphere +id 217 +position 0 -20 -8 +material 217 +radius 1 +medium false + +Material[Dielectric] +id 218 +albedo 1 1 1 +eta 3 +roughness 0.35 +sheen 0 + +Sphere +id 218 +position 0 -20 -6 +material 218 +radius 1 +medium false + +Material[Dielectric] +id 219 +albedo 1 1 1 +eta 3 +roughness 0.4 +sheen 0 + +Sphere +id 219 +position 0 -20 -4 +material 219 +radius 1 +medium false + +Material[Dielectric] +id 220 +albedo 1 1 1 +eta 3 +roughness 0.45 +sheen 0 + +Sphere +id 220 +position 0 -20 -2 +material 220 +radius 1 +medium false + +Material[Dielectric] +id 221 +albedo 1 1 1 +eta 3 +roughness 0.5 +sheen 0 + +Sphere +id 221 +position 0 -20 0 +material 221 +radius 1 +medium false + +Material[Dielectric] +id 222 +albedo 1 1 1 +eta 3 +roughness 0.55 +sheen 0 + +Sphere +id 222 +position 0 -20 2 +material 222 +radius 1 +medium false + +Material[Dielectric] +id 223 +albedo 1 1 1 +eta 3 +roughness 0.6 +sheen 0 + +Sphere +id 223 +position 0 -20 4 +material 223 +radius 1 +medium false + +Material[Dielectric] +id 224 +albedo 1 1 1 +eta 3 +roughness 0.65 +sheen 0 + +Sphere +id 224 +position 0 -20 6 +material 224 +radius 1 +medium false + +Material[Dielectric] +id 225 +albedo 1 1 1 +eta 3 +roughness 0.7 +sheen 0 + +Sphere +id 225 +position 0 -20 8 +material 225 +radius 1 +medium false + +Material[Dielectric] +id 226 +albedo 1 1 1 +eta 3 +roughness 0.75 +sheen 0 + +Sphere +id 226 +position 0 -20 10 +material 226 +radius 1 +medium false + +Material[Dielectric] +id 227 +albedo 1 1 1 +eta 3 +roughness 0.8 +sheen 0 + +Sphere +id 227 +position 0 -20 12 +material 227 +radius 1 +medium false + +Material[Dielectric] +id 228 +albedo 1 1 1 +eta 3 +roughness 0.85 +sheen 0 + +Sphere +id 228 +position 0 -20 14 +material 228 +radius 1 +medium false + +Material[Dielectric] +id 229 +albedo 1 1 1 +eta 3 +roughness 0.9 +sheen 0 + +Sphere +id 229 +position 0 -20 16 +material 229 +radius 1 +medium false + +Material[Dielectric] +id 230 +albedo 1 1 1 +eta 3 +roughness 0.95 +sheen 0 + +Sphere +id 230 +position 0 -20 18 +material 230 +radius 1 +medium false + +Material[Dielectric] +id 231 +albedo 1 1 1 +eta 3 +roughness 1 +sheen 0 + +Sphere +id 231 +position 0 -20 20 +material 231 +radius 1 +medium false \ No newline at end of file From ccc2059d88d367f9780a666788e200c860d82727 Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 1 Oct 2024 00:42:17 -0400 Subject: [PATCH 139/142] CA-89 new tests --- tests/dielectric_grid.csr | 4 +- tests/rough_metals.csr | 137 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 tests/rough_metals.csr diff --git a/tests/dielectric_grid.csr b/tests/dielectric_grid.csr index 40d8bd4..4e935dd 100644 --- a/tests/dielectric_grid.csr +++ b/tests/dielectric_grid.csr @@ -10,8 +10,8 @@ aperture 0.0001 focus_dist 10.0 Sky -top 1.0 0.2 0.2 -bottom 0.2 0.2 1.0 +top 1.0 0.1 0.1 +bottom 0.1 1.0 0.1 Material[Dielectric] diff --git a/tests/rough_metals.csr b/tests/rough_metals.csr new file mode 100644 index 0000000..1a4935e --- /dev/null +++ b/tests/rough_metals.csr @@ -0,0 +1,137 @@ +version 0.1.5 + +Camera +lookfrom 20 8 0 +lookat 0 4 0 +vup 0 1 0 +vfov 50 +aspect_ratio 16/9 +aperture 0.0001 +focus_dist 10.0 + +Sky +top 0.4 0.4 0.4 +bottom 0.4 0.4 0.4 + +# Scene Setup with red ground +Material[Diffuse] +id gray_ground +texture no +albedo 0.8 0.4 0.4 +roughness 0.0001 + +Sphere +id ground +position 0 -5000 0 +material gray_ground +radius 5000 +medium false + +# Emissive +Material[Emissive] +id key_light +rgb 1.0 1.0 1.0 +strength 10 + +Sphere +id key_sphere +position 30 25 -30 +material key_light +radius 10 +medium false + +Material[Metal] +id m0 +albedo 1.0 1.0 1.0 +roughness 0.0 + +Sphere +id sphere_0 +position 0 2 14 +material m0 +radius 1.75 +medium false + +Material[Metal] +id m0.01 +albedo 1.0 1.0 1.0 +roughness 0.01 + +Sphere +id sphere_1 +position 0 2 10 +material m0.01 +radius 1.75 +medium false + +Material[Metal] +id m0.05 +albedo 1.0 1.0 1.0 +roughness 0.05 + +Sphere +id sphere_2 +position 0 2 6 +material m0.05 +radius 1.75 +medium false + +Material[Metal] +id m0.1 +albedo 1.0 1.0 1.0 +roughness 0.1 + +Sphere +id sphere_3 +position 0 2 2 +material m0.1 +radius 1.75 +medium false + +Material[Metal] +id m0.15 +albedo 1.0 1.0 1.0 +roughness 0.15 + +Sphere +id sphere_4 +position 0 2 -2 +material m0.15 +radius 1.75 +medium false + +Material[Metal] +id m0.2 +albedo 1.0 1.0 1.0 +roughness 0.2 + +Sphere +id sphere_5 +position 0 2 -6 +material m0.2 +radius 1.75 +medium false + +Material[Metal] +id m0.25 +albedo 1.0 1.0 1.0 +roughness 0.25 + +Sphere +id sphere_6 +position 0 2 -10 +material m0.25 +radius 1.75 +medium false + +Material[Metal] +id m0.3 +albedo 1.0 1.0 1.0 +roughness 0.3 + +Sphere +id sphere_7 +position 0 2 -14 +material m0.3 +radius 1.75 +medium false \ No newline at end of file From 6ca46fc5ee3ce560b9561ed473e814b6327736a9 Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 1 Oct 2024 13:29:44 -0400 Subject: [PATCH 140/142] v0.1.5 --- README.md | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index bd1b399..4e487ee 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,9 @@

Render1

-Caitlyn is an in-development Monte Carlo ray tracer built in C++ by a team of students from the University of Waterloo and Wilfrid Laurier University. Caitlyn is actively working towards being a beautiful tool for bridging the accuracy and lighting of raytracing and the breathtaking visuals of pixel art (see our [portfolio](#our-portfolio) and Odd Tales' "The Last Night"!) +Caitlyn is an reverse path tracer built for implementing raytracing lighting to 'pixellax' 3D visuals. It is built in C++ by a team of students from the University of Waterloo and Wilfrid Laurier University. + +'Pixellax' is an art style with a focus on using 2D pixel art in 3D environments with an emphasis on beautiful, colourful lighting. (see our [portfolio](#our-portfolio) and Odd Tales' "The Last Night"!) _Interested in getting involved? Contact [Connor Loi](ctloi@uwaterloo.ca) or [Samuel Bai](sbai@uwaterloo.ca)._ @@ -20,24 +22,24 @@ Caitlyn MCRT is built on Debian 12. It may work on other distros, but we recomme ### Setup Before continuing: - Install `Docker Desktop`. -- Pull the latest `docker pull connortbot/caitlyn-mcrt:base-vX.X.X` +- Pull the latest `docker pull connortbot/caitlyn-mcrt:base-v0.2.1` ### Build You may pull the repository from within the container or mount a volume. Either works! -Run `cmake -B build/ -S .` to create files in the `build` folder. `cd build`, and `make`. Don't forget to initialize the submodules. +Run `cmake -B build/ -S .` to create files in the `build` folder. `cd build`, and `make`. _Don't forget to initialize the submodules!_ ### Basic Rendering -Caitlyn renders scenes from our custom filetype `.csr`. By default, the `caitlyn` executable will read the scene from a `scene.csr` file, so you need to have one before running. In this guide, we'll just run the `example.csr`, which you can copy from [here](https://github.com/cypraeno/csr-schema/blob/main/examples/example.csr). +Caitlyn renders scenes from our custom filetype `.csr`. By default, the `caitlyn` executable will read the scene from a `scene.csr` file, so you need to have one before running. In this guide, we'll just run the `0.1.5-showcase.csr`, which you can copy from the `tests` folder. To learn how to write CSR files, check out the [Basic Guide](https://github.com/cypraeno/csr-schema/blob/main/docs/basic-guide.md). -Caitlyn has a user-friendly command line interface, allowing you to customize samples, depth, type of multithreading, and more. Once you have the executable, you can run `./caitlyn --help` to see all the options at your disposal. +Caitlyn has a user-friendly command line interface, allowing you to customize samples, depth, use multithreading, and more. Once you have the executable, you can run `./caitlyn --help` to see all the options at your disposal. Let's render a PNG file of the example scene! Ensure that you have your CSR file in the same directory. ``` -./caitlyn -i example.csr -t png -r 600 600 +./caitlyn -i 0.1.5-showcase.csr -o 0.1.5-showcase.png -t png -s 50 ``` -This will read the scene from `example.csr` and output as a `png`. +This will read the scene from `0.1.5-showcase.csr` and output as a `png`. And now you have your first caitlyn-rendered scene! ## Our Portfolio @@ -57,14 +59,10 @@ Flags like `--samples` and `--depth` control the amount of time spent on the ren Sometimes, CSR files will have features not supported in your version of `caitlyn`. You can check this with the version indicator at the top of the CSR file and with `./caitlyn --version`. -For users who have a better understanding of their computer's resources, the `--threads` and `--vectorization` flags control the use of more efficient architecture. While `threads` dictate the amount of CPU threads to split the workloads on, the `vectorization` flag will dictate the type of SIMD batching. `[NONE|SSE|AVX|AVX512]`. -However, its important to note that this is increasingly untested with the new pathtracer. It is currently DEPRECATED. Use at own risk. - - ## Contribute For contribution or general inquiries, please email one of us at [Connor Loi](ctloi@uwaterloo.ca) or [Samuel Bai](sbai@uwaterloo.ca). -The people the made it happen: +Acknowledgements:
Connor Loi, @@ -74,5 +72,16 @@ The people the made it happen: Jonathan Wang, Gen Ichihashi, Max Tan, -Ricky Huang, +Ricky Huang
+ +Citation: +```bibtex +@software{Caitlyn, + title = {Caitlyn MCRT}, + author = {Connor Loi and Samuel Bai}, + note = {https://github.com/cypraeno/caitlyn}, + version = {0.1.5}, + year = 2024 +} +``` \ No newline at end of file From b6ec12bebd5644f7255de48f7e191474b7185c63 Mon Sep 17 00:00:00 2001 From: connortbot Date: Tue, 1 Oct 2024 13:31:46 -0400 Subject: [PATCH 141/142] CA-89 temporarily deprecate SIMD --- src/parsers/cli_parser.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/parsers/cli_parser.cc b/src/parsers/cli_parser.cc index 47e5ce0..f4ff089 100644 --- a/src/parsers/cli_parser.cc +++ b/src/parsers/cli_parser.cc @@ -20,7 +20,7 @@ void outputHelpGuide(std::ostream& out) { << " -h, --help Show this help message.\n" << " -V, --verbose Enables more descriptive messages of scenes and rendering process.\n" << " -T, --threads If multithreading is enabled, sets amount of threads used.\n" - << " -Vx, --vectorization Set SIMD vectorization batch size [0|4|8|16]. If NONE = 0, do not enable the flag.\n"; + << " -Vx, --vectorization [UNSUPPORTED/DEPRECATED] Set SIMD vectorization batch size [0|4|8|16]. If NONE = 0, do not enable the flag.\n"; exit(0); } @@ -102,9 +102,11 @@ Config parseArguments(int argc, char* argv[]) { } else if(arg == "-Vx" || arg == "--vectorization") { - int choice = checkValidIntegerInput(i, argc, argv, "-Vx/--vectorization"); - if (choice == 0 || choice == 4 || choice == 8 || choice == 16) config.vectorization = choice; - else throw std::invalid_argument("Error: Invalid option for --vectorization [1|4|8|16]. Use '--help' for more information."); + throw std::runtime_error("Error (DEPRECATION): -Vx/--vectorization not supported in v0.1.5."); + // CURRENTLY DEPRECATED AND NEEDS UPDATE TO 0.1.5 + // int choice = checkValidIntegerInput(i, argc, argv, "-Vx/--vectorization"); + // if (choice == 0 || choice == 4 || choice == 8 || choice == 16) config.vectorization = choice; + // else throw std::invalid_argument("Error: Invalid option for --vectorization [1|4|8|16]. Use '--help' for more information."); } else if(arg == "-v" || arg == "--version") { From 614b7f08c61c651916206cf340af31dc5c75abbc Mon Sep 17 00:00:00 2001 From: Connor Loi Date: Tue, 1 Oct 2024 13:37:08 -0400 Subject: [PATCH 142/142] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4e487ee..a27664d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

The Caitlyn Renderer :camera:

-

Render1

+

Render1

Caitlyn is an reverse path tracer built for implementing raytracing lighting to 'pixellax' 3D visuals. It is built in C++ by a team of students from the University of Waterloo and Wilfrid Laurier University. @@ -84,4 +84,4 @@ Citation: version = {0.1.5}, year = 2024 } -``` \ No newline at end of file +```