diff --git a/out.jpg b/out.jpg index 609e5c6..1e85428 100644 Binary files a/out.jpg and b/out.jpg differ diff --git a/tinyraytracer.cpp b/tinyraytracer.cpp index f2994a0..5e5806d 100644 --- a/tinyraytracer.cpp +++ b/tinyraytracer.cpp @@ -12,9 +12,10 @@ struct Light { }; struct Material { - Material(const Vec3f &a, const Vec3f &color, const float &spec) : albedo(a), diffuse_color(color), specular_exponent(spec) {} - Material() : albedo(1,0,0), diffuse_color(), specular_exponent() {} - Vec3f albedo; + Material(const float &r, const Vec4f &a, const Vec3f &color, const float &spec) : refractive_index(r), albedo(a), diffuse_color(color), specular_exponent(spec) {} + Material() : refractive_index(1), albedo(1,0,0,0), diffuse_color(), specular_exponent() {} + float refractive_index; + Vec4f albedo; Vec3f diffuse_color; float specular_exponent; }; @@ -44,6 +45,19 @@ Vec3f reflect(const Vec3f &I, const Vec3f &N) { return I - N*2.f*(I*N); } +Vec3f refract(const Vec3f &I, const Vec3f &N, const float &refractive_index) { // Snell's law + float cosi = - std::max(-1.f, std::min(1.f, I*N)); + float etai = 1, etat = refractive_index; + Vec3f n = N; + if (cosi < 0) { // if the ray is inside the object, swap the indices and invert the normal to get the correct result + cosi = -cosi; + std::swap(etai, etat); n = -N; + } + float eta = etai / etat; + float k = 1 - eta*eta*(1 - cosi*cosi); + return k < 0 ? Vec3f(0,0,0) : I*eta + n*(eta * cosi - sqrtf(k)); +} + bool scene_intersect(const Vec3f &orig, const Vec3f &dir, const std::vector &spheres, Vec3f &hit, Vec3f &N, Material &material) { float spheres_dist = std::numeric_limits::max(); for (size_t i=0; i < spheres.size(); i++) { @@ -67,8 +81,11 @@ Vec3f cast_ray(const Vec3f &orig, const Vec3f &dir, const std::vector &s } Vec3f reflect_dir = reflect(dir, N).normalize(); + Vec3f refract_dir = refract(dir, N, material.refractive_index).normalize(); Vec3f reflect_orig = reflect_dir*N < 0 ? point - N*1e-3 : point + N*1e-3; // offset the original point to avoid occlusion by the object itself + Vec3f refract_orig = refract_dir*N < 0 ? point - N*1e-3 : point + N*1e-3; Vec3f reflect_color = cast_ray(reflect_orig, reflect_dir, spheres, lights, depth + 1); + Vec3f refract_color = cast_ray(refract_orig, refract_dir, spheres, lights, depth + 1); float diffuse_light_intensity = 0, specular_light_intensity = 0; for (size_t i=0; i &s diffuse_light_intensity += lights[i].intensity * std::max(0.f, light_dir*N); specular_light_intensity += powf(std::max(0.f, -reflect(-light_dir, N)*dir), material.specular_exponent)*lights[i].intensity; } - return material.diffuse_color * diffuse_light_intensity * material.albedo[0] + Vec3f(1., 1., 1.)*specular_light_intensity * material.albedo[1] + reflect_color*material.albedo[2]; + return material.diffuse_color * diffuse_light_intensity * material.albedo[0] + Vec3f(1., 1., 1.)*specular_light_intensity * material.albedo[1] + reflect_color*material.albedo[2] + refract_color*material.albedo[3]; } void render(const std::vector &spheres, const std::vector &lights) { @@ -118,13 +135,14 @@ void render(const std::vector &spheres, const std::vector &lights } int main() { - Material ivory(Vec3f(0.6, 0.3, 0.1), Vec3f(0.4, 0.4, 0.3), 50.); - Material red_rubber(Vec3f(0.9, 0.1, 0.0), Vec3f(0.3, 0.1, 0.1), 10.); - Material mirror(Vec3f(0.0, 10.0, 0.8), Vec3f(1.0, 1.0, 1.0), 1425.); + Material ivory(1.0, Vec4f(0.6, 0.3, 0.1, 0.0), Vec3f(0.4, 0.4, 0.3), 50.); + Material glass(1.5, Vec4f(0.0, 0.5, 0.1, 0.8), Vec3f(0.6, 0.7, 0.8), 125.); + Material red_rubber(1.0, Vec4f(0.9, 0.1, 0.0, 0.0), Vec3f(0.3, 0.1, 0.1), 10.); + Material mirror(1.0, Vec4f(0.0, 10.0, 0.8, 0.0), Vec3f(1.0, 1.0, 1.0), 1425.); std::vector spheres; spheres.push_back(Sphere(Vec3f(-3, 0, -16), 2, ivory)); - spheres.push_back(Sphere(Vec3f(-1.0, -1.5, -12), 2, mirror)); + spheres.push_back(Sphere(Vec3f(-1.0, -1.5, -12), 2, glass)); spheres.push_back(Sphere(Vec3f( 1.5, -0.5, -18), 3, red_rubber)); spheres.push_back(Sphere(Vec3f( 7, 5, -18), 4, mirror));