Skip to content

Commit

Permalink
refractions
Browse files Browse the repository at this point in the history
  • Loading branch information
ssloy committed Jan 20, 2019
1 parent c80479d commit b69793b
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 8 deletions.
Binary file modified out.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 26 additions & 8 deletions tinyraytracer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down Expand Up @@ -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

This comment has been minimized.

Copy link
@RoastMyCode

RoastMyCode Jan 8, 2020

Another question.
So I am currently trying to implement two-sided triangles and based my code on your tutorial.
I am facing the issue that the refraction indices get swapped when the triangle is backfacing and the refraction doesn't get calculated correctly anymore.
I basically want to get the same result no matter whether the triangles are inserted with the vertices in (counter-)clockwise order.
I think this boils down to a software architectural question but maybe you have an idea on how to easily implementing this.
I also took a look at scratchapixel.com but they are also online using one-sided triangles.

cosi = -cosi;
std::swap(etai, etat); n = -N;
}
float eta = etai / etat;
float k = 1 - eta*eta*(1 - cosi*cosi);

This comment has been minimized.

Copy link
@RoastMyCode

RoastMyCode Dec 6, 2019

Hey, great Tutorial. I think I figured almost everything of the Ray Tracer out but I cant get behind the float k = 1 - eta*eta*(1 - cosi*cosi);
How does this calculation work?
So k is obviously related to the angle of the refraction. If the refraction is greater than 90°, k becomes negative and then we only have reflection but I still dont understand how it works exactly
Edit. I just realized that the 1 - cosi * cosi calculates the sine

This comment has been minimized.

Copy link
@ssloy

ssloy Dec 7, 2019

Author Owner

Check the "vector form" section from the Snell's law wiki article:
https://en.wikipedia.org/wiki/Snell%27s_law

in particular, this formula:

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<Sphere> &spheres, Vec3f &hit, Vec3f &N, Material &material) {
float spheres_dist = std::numeric_limits<float>::max();
for (size_t i=0; i < spheres.size(); i++) {
Expand All @@ -67,8 +81,11 @@ Vec3f cast_ray(const Vec3f &orig, const Vec3f &dir, const std::vector<Sphere> &s
}

Vec3f reflect_dir = reflect(dir, N).normalize();
Vec3f refract_dir = refract(dir, N, material.refractive_index).normalize();

This comment has been minimized.

Copy link
@schoolmeister

schoolmeister Jan 28, 2019

There's a possible bug here. If k < 0 in the refract() function, then a (0, 0, 0) vector will be returned. When normalizing this vector a division by zero happens.

This comment has been minimized.

Copy link
@ssloy

ssloy Jan 28, 2019

Author Owner

yup, already fixed by this commit

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<lights.size(); i++) {
Expand All @@ -84,7 +101,7 @@ Vec3f cast_ray(const Vec3f &orig, const Vec3f &dir, const std::vector<Sphere> &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<Sphere> &spheres, const std::vector<Light> &lights) {
Expand Down Expand Up @@ -118,13 +135,14 @@ void render(const std::vector<Sphere> &spheres, const std::vector<Light> &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<Sphere> 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));

Expand Down

0 comments on commit b69793b

Please sign in to comment.