Skip to content

Commit

Permalink
Create dielectric material #16
Browse files Browse the repository at this point in the history
  • Loading branch information
smercer10 committed Feb 25, 2024
1 parent 38cbb0e commit 0548f83
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 19 deletions.
5 changes: 3 additions & 2 deletions apps/raydiance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ int main() {

auto matGround = std::make_shared<lambertian>(colour(0.8, 0.8, 0.0));
auto matCentre = std::make_shared<lambertian>(colour(0.7, 0.3, 0.3));
auto matLeft = std::make_shared<metal>(colour(0.8, 0.8, 0.8), 0.3);
auto matRight = std::make_shared<metal>(colour(0.8, 0.6, 0.2), 1.0);
auto matLeft = std::make_shared<dielectric>(1.5);
auto matRight = std::make_shared<metal>(colour(0.8, 0.6, 0.2), 0.4);

world.add(std::make_shared<sphere>(point3(0.0, -100.5, -1.0), 100.0, matGround));
world.add(std::make_shared<sphere>(point3(0.0, 0.0, -1.0), 0.5, matCentre));
world.add(std::make_shared<sphere>(point3(-1.0, 0.0, -1.0), 0.5, matLeft));
world.add(std::make_shared<sphere>(point3(-1.0, 0.0, -1.0), -0.4, matLeft));// Negative radius trick for a hollow sphere
world.add(std::make_shared<sphere>(point3(1.0, 0.0, -1.0), 0.5, matRight));

camera cam;
Expand Down
19 changes: 16 additions & 3 deletions include/raydiance/material.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ class material {
virtual bool scatter(const ray &rIn, const intersection &i, colour &attenuation, ray &scattered) const = 0;
};

// Aka matte
class lambertian : public material {
public:
explicit lambertian(const colour &a) : albedo(a) {}

// Always scatters
bool scatter(const ray &rIn, const intersection &i, colour &attenuation, ray &scattered) const override;

private:
Expand All @@ -26,11 +26,24 @@ class lambertian : public material {

class metal : public material {
public:
metal(const colour &a, double f) : albedo(a), fuzz(f < 1 ? f : 1) {}
metal(const colour &a, double f) : albedo(a), fuzz(f < 1.0 ? f : 1.0) {}

bool scatter(const ray &rIn, const intersection &i, colour &attenuation, ray &scattered) const override;

private:
colour albedo;
double fuzz;
double fuzz;// 0.0 = no fuzz, 1.0 = maximum fuzz
};

// E.g. glass, diamond, water
class dielectric : public material {
public:
explicit dielectric(double ri) : refractionIndex(ri) {}

bool scatter(const ray &rIn, const intersection &i, colour &attenuation, ray &scattered) const override;

private:
double refractionIndex;

static double reflectance(double cosine, double refractionIndex);
};
1 change: 0 additions & 1 deletion include/raydiance/scene.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,5 @@ class scene : public object {

void clear() { objects.clear(); }
void add(const std::shared_ptr<object> &object) { objects.emplace_back(object); }

[[nodiscard]] bool isHit(const ray &r, interval tRange, intersection &i) const override;
};
18 changes: 9 additions & 9 deletions include/raydiance/vec3.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,15 @@ inline vec3 randomUnitVector() {
return unitVector(vec3::randomInUnitSphere());
}

inline vec3 randomVecOnHemisphere(const vec3 &normal) {
vec3 onUnitSphere = randomUnitVector();
if (dot(onUnitSphere, normal) > 0.0) {// On the same hemisphere as the normal
return onUnitSphere;
} else {
return -onUnitSphere;
}
}

inline vec3 reflect(const vec3 &v, const vec3 &n) {
return v - 2 * dot(v, n) * n;
}

inline vec3 refract(const vec3 &v, const vec3 &n, double indexRatio) {
auto cosTheta = std::fmin(dot(-v, n), 1.0);

vec3 rayOutPerp = indexRatio * (v + cosTheta * n);
vec3 rayOutParallel = -std::sqrt(std::fabs(1.0 - rayOutPerp.lengthSquared())) * n;

return rayOutPerp + rayOutParallel;
}
48 changes: 48 additions & 0 deletions src/material.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "raydiance/material.h"

bool lambertian::scatter(const ray &rIn, const intersection &i, colour &attenuation, ray &scattered) const {
// Scatter in a random direction
vec3 scatterDirection{i.normal + randomUnitVector()};

// Catch degenerate scatter direction
Expand All @@ -10,12 +11,59 @@ bool lambertian::scatter(const ray &rIn, const intersection &i, colour &attenuat

scattered = ray{i.p, scatterDirection};
attenuation = albedo;

// Always scatters
return true;
}

bool metal::scatter(const ray &rIn, const intersection &i, colour &attenuation, ray &scattered) const {
// Calculate reflected ray
vec3 reflected{reflect(unitVector(rIn.direction()), i.normal)};

// Add random fuzz to the reflected ray
scattered = ray{i.p, reflected + fuzz * randomUnitVector()};

attenuation = albedo;

// Only scatters if the reflected ray is in the same hemisphere as the normal
return dot(scattered.direction(), i.normal) > 0;
}

bool dielectric::scatter(const ray &rIn, const intersection &i, colour &attenuation, ray &scattered) const {
// All colours are equally attenuated
attenuation = colour{1.0, 1.0, 1.0};

double refractionRatio{i.frontFace ? (1.0 / refractionIndex) : refractionIndex};

vec3 unitDirection{unitVector(rIn.direction())};

// Get cosine of the angle between the incident ray and the normal
double cosTheta{fmin(dot(-unitDirection, i.normal), 1.0)};

// Get sine of the same angle using trigonometric identity
double sinTheta{std::sqrt(1.0 - cosTheta * cosTheta)};

// If total internal reflection occurs, refraction isn't possible
bool cannotRefract{refractionRatio * sinTheta > 1.0};

vec3 direction;

if (cannotRefract || reflectance(cosTheta, refractionRatio) > randomDouble()) {
direction = reflect(unitDirection, i.normal);
} else {
direction = refract(unitDirection, i.normal, refractionRatio);
}

scattered = ray{i.p, direction};

// Always scatters
return true;
}

double dielectric::reflectance(double cosine, double refractionIndex) {
// Use Schlick's approximation for reflectance
auto r0{(1 - refractionIndex) / (1 + refractionIndex)};
r0 *= r0;

return r0 + (1 - r0) * std::pow((1 - cosine), 5);
}
2 changes: 0 additions & 2 deletions tests/object_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ TEST(IntersectionTest, SetFaceNormal) {
vec3 on1{0, 0, -1};
intersection i1;
i1.setFaceNormal(r1, on1);

EXPECT_TRUE(i1.frontFace);
EXPECT_DOUBLE_EQ(i1.normal.x(), 0);
EXPECT_DOUBLE_EQ(i1.normal.y(), 0);
Expand All @@ -16,7 +15,6 @@ TEST(IntersectionTest, SetFaceNormal) {
vec3 on2{0, 0, -1};
intersection i2;
i2.setFaceNormal(r2, on2);

EXPECT_FALSE(i2.frontFace);
EXPECT_DOUBLE_EQ(i2.normal.x(), 0);
EXPECT_DOUBLE_EQ(i2.normal.y(), 0);
Expand Down
3 changes: 1 addition & 2 deletions tests/scene_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,13 @@ TEST(SceneTest, IsHit) {
EXPECT_FALSE(s.isHit(r, tRange, i));

auto m = std::make_shared<lambertian>(colour{0.0, 0.0, 0.0});
s.add(std::make_shared<sphere>(point3{0, 0, 1}, 0.5, m));

s.add(std::make_shared<sphere>(point3{0, 0, 1}, 0.5, m));
EXPECT_FALSE(s.isHit(r, tRange, i));

s.add(std::make_shared<sphere>(point3{0, 0, -1}, 0.5, m));
s.add(std::make_shared<sphere>(point3{0, 0, -0.5}, 100, m));
s.add(std::make_shared<sphere>(point3{0, 0, -3}, 1, m));

EXPECT_TRUE(s.isHit(r, tRange, i));
EXPECT_DOUBLE_EQ(i.t, 0.5);
EXPECT_DOUBLE_EQ(i.p.x(), 0);
Expand Down
18 changes: 18 additions & 0 deletions tests/vec3_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -201,4 +201,22 @@ TEST(Vec3HelperTest, Reflect) {
EXPECT_DOUBLE_EQ(r2.x(), 0.5);
EXPECT_DOUBLE_EQ(r2.y(), 0.6);
EXPECT_DOUBLE_EQ(r2.z(), 0.7);
}

TEST(Vec3HelperTest, Refract) {
vec3 v1(0.5, 0.6, 0.7);
vec3 n1(0.1, -0.2, 0.5);
double ir1 = 1.5;
vec3 r1 = refract(v1, n1, ir1);
EXPECT_DOUBLE_EQ(r1.x(), 0.59959704801067448);
EXPECT_DOUBLE_EQ(r1.y(), 1.2008059039786507);
EXPECT_DOUBLE_EQ(r1.z(), 0.29798524005337268);

vec3 v2(0.5, 0.6, 0.7);
vec3 n2(0.8, 0.9, -1.4);
double ir2 = 0.9;
vec3 r2 = refract(v2, n2, ir2);
EXPECT_DOUBLE_EQ(r2.x(), 0.21690213899307964);
EXPECT_DOUBLE_EQ(r2.y(), 0.27776490636721457);
EXPECT_DOUBLE_EQ(r2.z(), 1.0379212567621106);
}

0 comments on commit 0548f83

Please sign in to comment.