aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/camera.cpp22
-rw-r--r--src/dielectric.cpp32
-rw-r--r--src/main.cpp5
-rw-r--r--src/metal.cpp2
-rw-r--r--src/vec3.cpp10
6 files changed, 61 insertions, 11 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 691d221..bac7a7a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -11,4 +11,5 @@ add_executable(
camera.cpp
lambertian.cpp
metal.cpp
+ dielectric.cpp
)
diff --git a/src/camera.cpp b/src/camera.cpp
index 21cf506..520cc1e 100644
--- a/src/camera.cpp
+++ b/src/camera.cpp
@@ -1,14 +1,22 @@
#include "camera.hpp"
-camera::camera(point3 position, double aspect_ratio, double focal_length)
- : position(position), focal_length(focal_length) {
- auto viewport_height = 2.0;
- auto viewport_width = viewport_height * aspect_ratio;
+#include "rt.hpp"
- horizontal = vec3 { viewport_width, 0.0, 0.0 };
- vertical = vec3 { 0.0, viewport_height, 0.0 };
+camera::camera(point3 position, point3 target, double v_fov, double aspect_ratio)
+ : position(position) {
- bottom_left_pixel = position - horizontal / 2.0 - vertical / 2.0 - vec3 { 0.0, 0.0, focal_length };
+ auto forward = (position - target).unit();
+ auto right = vec3{ 0.0, 1.0, 0.0 }.cross(forward).unit();
+ auto up = forward.cross(right).unit();
+
+ auto h = tan(deg_to_rad(v_fov) / 2.0);
+ auto viewport_height = 2.0 * h;
+ auto viewport_width = viewport_height * aspect_ratio;
+
+ horizontal = viewport_width * right;
+ vertical = viewport_height * up;
+
+ bottom_left_pixel = position - horizontal / 2.0 - vertical / 2.0 - forward;
}
ray camera::get_ray(double u, double v) const {
diff --git a/src/dielectric.cpp b/src/dielectric.cpp
new file mode 100644
index 0000000..80ca069
--- /dev/null
+++ b/src/dielectric.cpp
@@ -0,0 +1,32 @@
+#include "dielectric.hpp"
+#include "rt.hpp"
+#include "hittable.hpp"
+
+dielectric::dielectric(double index_of_refraction)
+ : ior(index_of_refraction) {}
+
+bool dielectric::scatter(const ray& r, const struct hit_info& info, color& attenuation, ray& scattered) const {
+ auto unit_direction = r.direction.unit();
+ auto refraction_ratio = info.front_face ? (1.0 / ior) : ior;
+ double cos_theta = fmin(info.normal.dot(-unit_direction), 1.0);
+ double sin_theta = sqrt(1.0 - cos_theta * cos_theta);
+
+ vec3 direction;
+ if (refraction_ratio * sin_theta > 1.0 || reflectance(cos_theta, refraction_ratio) > randd()) {
+ direction = reflect(unit_direction, info.normal);
+ } else {
+ direction = refract(unit_direction, info.normal, refraction_ratio);
+ }
+
+ scattered = ray { info.point, direction };
+ attenuation = { 1.0, 1.0, 1.0 };
+
+ return true;
+}
+
+double dielectric::reflectance(double cosine, double ref_idx) {
+ // Use Schlick's approximation for reflectance.
+ auto r0 = (1 - ref_idx) / (1 + ref_idx);
+ r0 = r0 * r0;
+ return r0 + (1 - r0) * pow ((1 - cosine), 5);
+}
diff --git a/src/main.cpp b/src/main.cpp
index 9718d3e..92dc01f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -35,19 +35,20 @@ int main() {
const uint32_t samples_per_pixel = 100;
const uint32_t max_depth = 100;
- camera camera { {}, aspect_ratio, 1.0 };
+ camera camera { { /*-2.0, 2.0, 1.0*/ }, { 0.0, 0.0, -1.0 }, 90.0, aspect_ratio };
// World hittable objects
hittable_list world {};
auto material_ground = std::make_shared<lambertian>(color(0.8, 0.8, 0.0));
auto material_center = std::make_shared<lambertian>(color(0.7, 0.3, 0.3));
- auto material_left = std::make_shared<metal>(color(0.8, 0.8, 0.8), 0.3);
+ auto material_left = std::make_shared<dielectric>(1.5);
auto material_right = std::make_shared<metal>(color(0.8, 0.6, 0.2), 1.0);
world.add(std::make_shared<sphere>(point3( 0.0, -100.5, -1.0), 100.0, material_ground));
world.add(std::make_shared<sphere>(point3( 0.0, 0.0, -1.0), 0.5, material_center));
world.add(std::make_shared<sphere>(point3(-1.0, 0.0, -1.0), 0.5, material_left));
+ world.add(std::make_shared<sphere>(point3(-1.0, 0.0, -1.0), -0.4, material_left));
world.add(std::make_shared<sphere>(point3( 1.0, 0.0, -1.0), 0.5, material_right));
// PPM format header
diff --git a/src/metal.cpp b/src/metal.cpp
index d02a2f7..196c95d 100644
--- a/src/metal.cpp
+++ b/src/metal.cpp
@@ -6,7 +6,7 @@ metal::metal(const color& albedo, double f)
: albedo(albedo), fuzz(f < 1 ? f : 1) {}
bool metal::scatter(const ray& r, const struct hit_info& info, color& attenuation, ray& scattered) const {
- auto reflected = reflect(r.direction, info.normal).unit();
+ auto reflected = reflect(r.direction.unit(), info.normal);
scattered = ray { info.point, reflected + fuzz * random_in_unit_sphere() };
attenuation = albedo;
diff --git a/src/vec3.cpp b/src/vec3.cpp
index 92ccae4..3346973 100644
--- a/src/vec3.cpp
+++ b/src/vec3.cpp
@@ -53,7 +53,7 @@ vec3 vec3::cross(const vec3& vec) const {
return vec3(
y * vec.z - z * vec.y,
z * vec.x - x * vec.z,
- x * vec.y - y * vec.y
+ x * vec.y - y * vec.x
);
}
@@ -111,6 +111,14 @@ vec3 reflect(vec3 v, vec3 n) {
return v - 2 * v.dot(n) * n;
}
+vec3 refract(const vec3& uv, const vec3& n, double etai_over_etat) {
+ auto cos_theta = fmin(n.dot(-uv), 1.0);
+ vec3 r_out_perp = etai_over_etat * (uv + cos_theta*n);
+ vec3 r_out_parallel = -sqrt(fabs(1.0 - r_out_perp.length_sq())) * n;
+
+ return r_out_perp + r_out_parallel;
+}
+
std::ostream& operator<<(std::ostream& out, vec3 vec) {
return out << vec.x << ' ' << vec.y << ' ' << vec.z << std::endl;
}