Vector3.h
inline Vector3 Reflect(const Vector3& direction, const Vector3& normal)
{
return direction - 2 * Dot(direction, normal) * normal;
}
inline Vector3 Refract(const Vector3& unitVector, const Vector3& normal, double refractionRatio)
{
double cosTheta = fmin(Dot(-unitVector, normal), 1.0);
Vector3 rayOutPerpendicular = refractionRatio * (unitVector + cosTheta * normal);
Vector3 rayOutParallel = -sqrt(fabs(1.0 - rayOutPerpendicular.SquaredLength())) * normal;
return rayOutPerpendicular + rayOutParallel;
}
Dielectric.h
#pragma once
#include "Material.h"
class DielectricMaterial : public Material
{
private:
double refractionIndex;
public:
DielectricMaterial(double refIndex): refractionIndex(refIndex){}
bool Scatter(const Ray& rRayIn, const HitInfo& hitInfo, Color& attenuation, Ray& scattered) const override;
};
Dielectric.cpp
#include "DielectricMaterial.h"
#include "Hittable.h"
bool DielectricMaterial::Scatter(const Ray& rRayIn, const HitInfo& hitInfo, Color& attenuation, Ray& scattered) const
{
attenuation = Color(1.0, 1.0, 1.0);
double refractionRatio = hitInfo.frontFace ? (1.0 / refractionIndex) : refractionIndex;
Vector3 unitDirection = Unit(rRayIn.GetDirection());
double cosTheta = fmin(Dot(-unitDirection, hitInfo.normal), 1.0);
double sinTheta = sqrt(1.0 - cosTheta * cosTheta);
bool cannotRefract = refractionRatio * sinTheta > 1.0;
Vector3 direction;
if(cannotRefract) direction = Reflect(unitDirection, hitInfo.normal);
else direction = Refract(unitDirection, hitInfo.normal, refractionRatio);
scattered = Ray(hitInfo.coordinates, direction);
return true;
}
Raytracing.cpp
#include "Camera.h"
#include "DielectricMaterial.h"
#include "HittableCollection.h"
#include "LambertianMaterial.h"
#include "MetalMaterial.h"
#include "Sphere.h"
using namespace std;
int main(int argc, char* argv[])
{
// World
HittableCollection world;
shared_ptr<Material> groundMat = make_shared<LambertianMaterial>(Color(0.8, 0.8, 0.0));
shared_ptr<Material> leftMat = make_shared<DielectricMaterial>(1.5);
shared_ptr<Material> centerMat = make_shared<LambertianMaterial>(Color(0.1, 0.2, 0.5));
shared_ptr<Material> rightMat = make_shared<MetalMaterial>(Color(0.8, 0.6, 0.2), 0.0);
world.Add(make_shared<Sphere>(Position(0,-100.5,-1), 100, groundMat));
world.Add(make_shared<Sphere>(Position(0,0,-1), 0.5, centerMat));
world.Add(make_shared<Sphere>(Position(-1,0,-1), 0.5, leftMat));
world.Add(make_shared<Sphere>(Position(1, 0,-1), 0.5, rightMat));
Camera camera(400, 16.0/9.0, 100, 50);
camera.Render(world);
return 0;
}
Dielectric.h
#pragma once
#include "Material.h"
class DielectricMaterial : public Material
{
private:
double refractionIndex;
static double Reflectance(double cosine, double pRefractionIndex);
public:
DielectricMaterial(double refIndex): refractionIndex(refIndex){}
bool Scatter(const Ray& rRayIn, const HitInfo& hitInfo, Color& attenuation, Ray& scattered) const override;
};
Dielectric.cpp
#include "DielectricMaterial.h"
#include "Hittable.h"
double DielectricMaterial::Reflectance(double cosine, double pRefractionIndex)
{
// Schlick approximation of reflectance
double reflectance = (1 - pRefractionIndex) / (1 + pRefractionIndex);
reflectance *= reflectance;
return reflectance + (1 - reflectance)*pow((1 - cosine), 5);
}
bool DielectricMaterial::Scatter(const Ray& rRayIn, const HitInfo& hitInfo, Color& attenuation, Ray& scattered) const
{
attenuation = Color(1.0, 1.0, 1.0);
double refractionRatio = hitInfo.frontFace ? (1.0 / refractionIndex) : refractionIndex;
Vector3 unitDirection = Unit(rRayIn.GetDirection());
double cosTheta = fmin(Dot(-unitDirection, hitInfo.normal), 1.0);
double sinTheta = sqrt(1.0 - cosTheta * cosTheta);
bool cannotRefract = refractionRatio * sinTheta > 1.0;
Vector3 direction;
if(cannotRefract || Reflectance(cosTheta, refractionRatio) > RandomDouble()) direction = Reflect(unitDirection, hitInfo.normal);
else direction = Refract(unitDirection, hitInfo.normal, refractionRatio);
scattered = Ray(hitInfo.coordinates, direction);
return true;
}
Raytracing.cpp
world.Add(make_shared<Sphere>(Position(0,-100.5,-1), 100, groundMat));
world.Add(make_shared<Sphere>(Position(0,0,-1), 0.5, centerMat));
world.Add(make_shared<Sphere>(Position(-1,0,-1), 0.5, leftMat));
world.Add(make_shared<Sphere>(Position(-1,0,-1), -0.4, leftMat)); // Hollow
world.Add(make_shared<Sphere>(Position(1, 0,-1), 0.5, rightMat));