Clean up
This commit is contained in:
parent
61f9be5939
commit
8113ff8a01
4
dub.json
4
dub.json
|
@ -5,7 +5,7 @@
|
|||
"configurations": [
|
||||
{
|
||||
"name": "executable",
|
||||
"targetName": "owe",
|
||||
"targetName": "rtow",
|
||||
"targetType": "executable"
|
||||
},
|
||||
{
|
||||
|
@ -23,4 +23,4 @@
|
|||
"description": "Ray Tracing in One Weekend",
|
||||
"license": "MIT",
|
||||
"name": "oneweekend-d"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,20 +6,13 @@ import vec3;
|
|||
|
||||
class Camera
|
||||
{
|
||||
this(
|
||||
Point lookFrom,
|
||||
Point lookAt,
|
||||
Vec3 vup,
|
||||
double vFov,
|
||||
double aspectRatio,
|
||||
double aperture,
|
||||
double focusDist
|
||||
)
|
||||
this(Point lookFrom, Point lookAt, Vec3 vup, double vFov, double aspectRatio,
|
||||
double aperture, double focusDist)
|
||||
{
|
||||
auto theta = vFov.degreesToRadians;
|
||||
auto h = (theta / 2).tan;
|
||||
auto viewportHeight = h * 2.0;
|
||||
auto viewportWidth = aspectRatio * viewportHeight;
|
||||
const auto theta = vFov.degreesToRadians;
|
||||
const auto h = (theta / 2).tan;
|
||||
const auto viewportHeight = h * 2.0;
|
||||
const auto viewportWidth = aspectRatio * viewportHeight;
|
||||
|
||||
w = (lookFrom - lookAt).unitVector;
|
||||
u = vup.cross(w).unitVector;
|
||||
|
@ -38,11 +31,11 @@ class Camera
|
|||
Vec3 rd = lensRadius * Vec3.randomInUnitDisk;
|
||||
Vec3 offset = u * rd.x + v * rd.y;
|
||||
|
||||
return new Ray(origin + offset, lowerLeftCorner + s * horizontal +
|
||||
t * vertical - origin - offset);
|
||||
return new Ray(origin + offset, lowerLeftCorner + s * horizontal + t
|
||||
* vertical - origin - offset);
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
Point origin;
|
||||
Point lowerLeftCorner;
|
||||
Vec3 horizontal;
|
||||
|
|
|
@ -5,7 +5,6 @@ import std.math : sqrt;
|
|||
import std.stdio : writeln;
|
||||
|
||||
import hittable;
|
||||
import hittableList;
|
||||
import ray;
|
||||
import sphere;
|
||||
import util;
|
||||
|
@ -24,12 +23,15 @@ void writeColor(Color pixelColor, int samplesPerPixel)
|
|||
writeln(format("%d %d %d", r.to!int, g.to!int, b.to!int));
|
||||
}
|
||||
|
||||
Color rayColor(Ray r, ref HittableList world, int depth)
|
||||
Color rayColor(ref Ray r, ref HittableList world, int depth)
|
||||
{
|
||||
HitRecord rec;
|
||||
|
||||
// If we've exceeded the ray bounce limit, no more light is gathered.
|
||||
if (depth <= 0) { return new Color(0, 0, 0); }
|
||||
if (depth <= 0)
|
||||
{
|
||||
return new Color(0, 0, 0);
|
||||
}
|
||||
|
||||
if (world.hit(r, 0.001, infinity, rec))
|
||||
{
|
||||
|
@ -42,7 +44,7 @@ Color rayColor(Ray r, ref HittableList world, int depth)
|
|||
}
|
||||
}
|
||||
|
||||
auto unitDirection = r.direction.unitVector;
|
||||
const auto unitDirection = r.direction.unitVector;
|
||||
auto t = 0.5 * (unitDirection.y + 1.0);
|
||||
// linearly blend white and blue
|
||||
return (1.0 - t) * new Color(1.0, 1.0, 1.0) + t * new Color(0.5, 0.7, 1.0);
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
import std.math : fmin, pow, sqrt;
|
||||
|
||||
import color;
|
||||
import hittable;
|
||||
import material;
|
||||
import ray;
|
||||
import util;
|
||||
import vec3;
|
||||
|
||||
class Dielectric : Material
|
||||
{
|
||||
this(double refractionIndex)
|
||||
{
|
||||
_ri = refractionIndex;
|
||||
}
|
||||
|
||||
override bool scatter(Ray rIn, HitRecord rec, ref Color attenuation, ref Ray scattered)
|
||||
{
|
||||
attenuation = new Color(1, 1, 1);
|
||||
double refractionRatio = rec.frontFace ? (1.0 / _ri) : _ri;
|
||||
|
||||
Vec3 unitDirection = rIn.direction.unitVector;
|
||||
double cosTheta = (-unitDirection).dot(rec.normal).fmin(1.0);
|
||||
double sinTheta = (1.0 - cosTheta * cosTheta).sqrt;
|
||||
|
||||
bool cannotRefract = refractionRatio * sinTheta > 1.0;
|
||||
Vec3 direction;
|
||||
|
||||
if (cannotRefract || reflectance(cosTheta, refractionRatio) > randomDouble)
|
||||
{
|
||||
direction = unitDirection.reflect(rec.normal);
|
||||
}
|
||||
else
|
||||
{
|
||||
direction = unitDirection.refract(rec.normal, refractionRatio);
|
||||
}
|
||||
|
||||
scattered = new Ray(rec.p, direction);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
double _ri;
|
||||
|
||||
static double reflectance(double cosine, double refIdx)
|
||||
{
|
||||
// Use Schlick's approximation for reflectance
|
||||
auto r0 = (1 - refIdx) / (1 + refIdx);
|
||||
r0 = r0 * r0;
|
||||
return r0 + (1 - r0) * (1 - cosine).pow(5);
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
import std.array;
|
||||
|
||||
import material;
|
||||
import ray;
|
||||
import vec3;
|
||||
|
@ -20,5 +22,51 @@ pragma(inline):
|
|||
|
||||
interface Hittable
|
||||
{
|
||||
bool hit(Ray r, double tMin, double tMax, ref HitRecord rec);
|
||||
bool hit(ref Ray r, double tMin, double tMax, ref HitRecord rec);
|
||||
}
|
||||
|
||||
class HittableList : Hittable
|
||||
{
|
||||
this()
|
||||
{
|
||||
objects = [];
|
||||
}
|
||||
|
||||
this(Hittable object)
|
||||
{
|
||||
this();
|
||||
this.add(object);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
objects = [];
|
||||
}
|
||||
|
||||
void add(Hittable object)
|
||||
{
|
||||
objects ~= object;
|
||||
}
|
||||
|
||||
override bool hit(ref Ray r, double tMin, double tMax, ref HitRecord rec)
|
||||
{
|
||||
HitRecord tempRec;
|
||||
bool hitAnything = false;
|
||||
auto closestSoFar = tMax;
|
||||
|
||||
foreach (object; objects)
|
||||
{
|
||||
if (object.hit(r, tMin, closestSoFar, tempRec))
|
||||
{
|
||||
hitAnything = true;
|
||||
closestSoFar = tempRec.t;
|
||||
rec = tempRec;
|
||||
}
|
||||
}
|
||||
|
||||
return hitAnything;
|
||||
}
|
||||
|
||||
private:
|
||||
Hittable[] objects;
|
||||
}
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
import std.array;
|
||||
|
||||
import hittable;
|
||||
import ray;
|
||||
|
||||
class HittableList : Hittable
|
||||
{
|
||||
this()
|
||||
{
|
||||
_objects = [];
|
||||
}
|
||||
|
||||
this(Hittable object)
|
||||
{
|
||||
this();
|
||||
add(object);
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
_objects = [];
|
||||
}
|
||||
|
||||
void add(Hittable object)
|
||||
{
|
||||
_objects ~= object;
|
||||
}
|
||||
|
||||
override bool hit(Ray r, double tMin, double tMax, ref HitRecord rec)
|
||||
{
|
||||
HitRecord tempRec;
|
||||
bool hitAnything = false;
|
||||
auto closestSoFar = tMax;
|
||||
|
||||
foreach (object; _objects)
|
||||
{
|
||||
if (object.hit(r, tMin, closestSoFar, tempRec))
|
||||
{
|
||||
hitAnything = true;
|
||||
closestSoFar = tempRec.t;
|
||||
rec = tempRec;
|
||||
}
|
||||
}
|
||||
|
||||
return hitAnything;
|
||||
}
|
||||
|
||||
private:
|
||||
Hittable[] _objects;
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
import color;
|
||||
import hittable;
|
||||
import material;
|
||||
import ray;
|
||||
import vec3;
|
||||
|
||||
class Lambertian : Material
|
||||
{
|
||||
this(Color a)
|
||||
{
|
||||
_albedo = a;
|
||||
}
|
||||
|
||||
override bool scatter(Ray rIn, HitRecord rec, ref Color attenuation, ref Ray scattered)
|
||||
{
|
||||
auto scatterDirection = rec.normal + Vec3.randomUnitVector;
|
||||
|
||||
// Catch degenerate scatter direction
|
||||
if (scatterDirection.nearZero) { scatterDirection = rec.normal; }
|
||||
|
||||
scattered = new Ray(rec.p, scatterDirection);
|
||||
attenuation = _albedo;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Color _albedo;
|
||||
}
|
|
@ -1,8 +1,104 @@
|
|||
import std.math : fmin, pow, sqrt;
|
||||
|
||||
import color;
|
||||
import hittable;
|
||||
import material;
|
||||
import ray;
|
||||
import util;
|
||||
import vec3;
|
||||
|
||||
abstract class Material
|
||||
{
|
||||
abstract bool scatter(Ray rIn, HitRecord rec, ref Color attenuation, ref Ray scattered);
|
||||
abstract bool scatter(ref Ray rIn, ref HitRecord rec, ref Color attenuation, ref Ray scattered);
|
||||
}
|
||||
|
||||
class Dielectric : Material
|
||||
{
|
||||
this(double refractionIndex)
|
||||
{
|
||||
ri = refractionIndex;
|
||||
}
|
||||
|
||||
override bool scatter(ref Ray rIn, ref HitRecord rec, ref Color attenuation, ref Ray scattered)
|
||||
{
|
||||
attenuation = new Color(1, 1, 1);
|
||||
double refractionRatio = rec.frontFace ? (1.0 / ri) : ri;
|
||||
|
||||
Vec3 unitDirection = rIn.direction.unitVector;
|
||||
const double cosTheta = (-unitDirection).dot(rec.normal).fmin(1.0);
|
||||
const double sinTheta = (1.0 - cosTheta * cosTheta).sqrt;
|
||||
|
||||
const bool cannotRefract = refractionRatio * sinTheta > 1.0;
|
||||
Vec3 direction;
|
||||
|
||||
if (cannotRefract || reflectance(cosTheta, refractionRatio) > randomDouble)
|
||||
{
|
||||
direction = unitDirection.reflect(rec.normal);
|
||||
}
|
||||
else
|
||||
{
|
||||
direction = unitDirection.refract(rec.normal, refractionRatio);
|
||||
}
|
||||
|
||||
scattered = new Ray(rec.p, direction);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
double ri;
|
||||
|
||||
static double reflectance(double cosine, double refIdx)
|
||||
{
|
||||
// Use Schlick's approximation for reflectance
|
||||
auto r0 = (1 - refIdx) / (1 + refIdx);
|
||||
r0 = r0 * r0;
|
||||
return r0 + (1 - r0) * (1 - cosine).pow(5);
|
||||
}
|
||||
}
|
||||
|
||||
class Lambertian : Material
|
||||
{
|
||||
this(Color a)
|
||||
{
|
||||
albedo = a;
|
||||
}
|
||||
|
||||
override bool scatter(ref Ray rIn, ref HitRecord rec, ref Color attenuation, ref Ray scattered)
|
||||
{
|
||||
auto scatterDirection = rec.normal + Vec3.randomUnitVector;
|
||||
|
||||
// catch degenerate scatter direction
|
||||
if (scatterDirection.nearZero)
|
||||
{
|
||||
scatterDirection = rec.normal;
|
||||
}
|
||||
|
||||
scattered = new Ray(rec.p, scatterDirection);
|
||||
attenuation = albedo;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
Color albedo;
|
||||
}
|
||||
|
||||
class Metal : Material
|
||||
{
|
||||
this(Color a, double f)
|
||||
{
|
||||
albedo = a;
|
||||
fuzz = f < 1 ? f : 1;
|
||||
}
|
||||
|
||||
override bool scatter(ref Ray rIn, ref HitRecord rec, ref Color attenuation, ref Ray scattered)
|
||||
{
|
||||
Vec3 reflected = rIn.direction.unitVector.reflect(rec.normal);
|
||||
scattered = new Ray(rec.p, reflected + fuzz * Vec3.randomInUnitSphere);
|
||||
attenuation = albedo;
|
||||
return scattered.direction.dot(rec.normal) > 0;
|
||||
}
|
||||
|
||||
private:
|
||||
Color albedo;
|
||||
double fuzz;
|
||||
}
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
import color;
|
||||
import hittable;
|
||||
import material;
|
||||
import ray;
|
||||
import vec3;
|
||||
|
||||
class Metal : Material
|
||||
{
|
||||
this(Color a, double f)
|
||||
{
|
||||
_albedo = a;
|
||||
_fuzz = f < 1 ? f : 1;
|
||||
}
|
||||
|
||||
override bool scatter(Ray rIn, HitRecord rec, ref Color attenuation, ref Ray scattered)
|
||||
{
|
||||
Vec3 reflected = rIn.direction.unitVector.reflect(rec.normal);
|
||||
scattered = new Ray(rec.p, reflected + _fuzz * Vec3.randomInUnitSphere);
|
||||
attenuation = _albedo;
|
||||
return scattered.direction.dot(rec.normal) > 0;
|
||||
}
|
||||
|
||||
private:
|
||||
Color _albedo;
|
||||
double _fuzz;
|
||||
}
|
58
source/ray.d
58
source/ray.d
|
@ -4,43 +4,43 @@ alias Point = Vec3;
|
|||
|
||||
class Ray
|
||||
{
|
||||
this()
|
||||
{
|
||||
this(new Point(0, 0, 0), new Vec3(0, 0, 0));
|
||||
}
|
||||
this()
|
||||
{
|
||||
this(new Point(0, 0, 0), new Vec3(0, 0, 0));
|
||||
}
|
||||
|
||||
this(Point origin, Vec3 direction)
|
||||
{
|
||||
_origin = origin;
|
||||
_direction = direction;
|
||||
}
|
||||
this(Point origin, Vec3 direction)
|
||||
{
|
||||
_origin = origin;
|
||||
_direction = direction;
|
||||
}
|
||||
|
||||
Point origin()
|
||||
{
|
||||
return _origin;
|
||||
}
|
||||
Point origin()
|
||||
{
|
||||
return _origin;
|
||||
}
|
||||
|
||||
Vec3 direction()
|
||||
{
|
||||
return _direction;
|
||||
}
|
||||
Vec3 direction()
|
||||
{
|
||||
return _direction;
|
||||
}
|
||||
|
||||
Point at(double t)
|
||||
{
|
||||
return origin + t * direction;
|
||||
}
|
||||
Point at(double t)
|
||||
{
|
||||
return origin + t * direction;
|
||||
}
|
||||
|
||||
private:
|
||||
Point _origin;
|
||||
Vec3 _direction;
|
||||
Point _origin;
|
||||
Vec3 _direction;
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
auto origin = new Point(1, 2, 3);
|
||||
auto direction = new Point(1, 2, 3);
|
||||
auto ray = new Ray(origin, direction);
|
||||
assert(ray.at(1) == new Point(2, 4, 6));
|
||||
assert(ray.at(-1) == new Point(0, 0, 0));
|
||||
assert(ray.at(2) == new Point(3, 6, 9));
|
||||
auto origin = new Point(1, 2, 3);
|
||||
auto direction = new Point(1, 2, 3);
|
||||
auto ray = new Ray(origin, direction);
|
||||
assert(ray.at(1) == new Point(2, 4, 6));
|
||||
assert(ray.at(-1) == new Point(0, 0, 0));
|
||||
assert(ray.at(2) == new Point(3, 6, 9));
|
||||
}
|
||||
|
|
|
@ -5,11 +5,8 @@ import std.stdio;
|
|||
|
||||
import camera;
|
||||
import color;
|
||||
import dielectric;
|
||||
import hittableList;
|
||||
import lambertian;
|
||||
import hittable;
|
||||
import material;
|
||||
import metal;
|
||||
import ray;
|
||||
import sphere;
|
||||
import util;
|
||||
|
@ -28,11 +25,11 @@ else
|
|||
auto groudMaterial = new Lambertian(new Color(0.5, 0.5, 0.5));
|
||||
world.add(new Sphere(new Point(0, -1000, 0), 1000, groudMaterial));
|
||||
|
||||
foreach(a; -11 .. 11)
|
||||
foreach (a; -11 .. 11)
|
||||
{
|
||||
foreach(b; -11 .. 11)
|
||||
foreach (b; -11 .. 11)
|
||||
{
|
||||
auto chooseMat = randomDouble;
|
||||
const auto chooseMat = randomDouble;
|
||||
auto center = new Point(a + 0.9 * randomDouble, 0.2, b + 0.9 * randomDouble);
|
||||
|
||||
if ((center - new Point(4, 0.2, 0)).length > 0.9)
|
|
@ -14,19 +14,19 @@ class Sphere : Hittable
|
|||
_mat = mat;
|
||||
}
|
||||
|
||||
override bool hit(Ray r, double tMin, double tMax, ref HitRecord rec)
|
||||
override bool hit(ref Ray r, double tMin, double tMax, ref HitRecord rec)
|
||||
{
|
||||
auto oc = r.origin - _center;
|
||||
auto a = r.direction.lengthSquared;
|
||||
auto halfB = oc.dot(r.direction);
|
||||
auto c = oc.lengthSquared - _radius * _radius;
|
||||
const auto a = r.direction.lengthSquared;
|
||||
const auto halfB = oc.dot(r.direction);
|
||||
const auto c = oc.lengthSquared - _radius * _radius;
|
||||
|
||||
auto discriminant = halfB * halfB - a * c;
|
||||
const auto discriminant = halfB * halfB - a * c;
|
||||
if (discriminant < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto sqrtd = discriminant.sqrt;
|
||||
immutable auto sqrtd = discriminant.sqrt;
|
||||
|
||||
// Find the nearest root within acceptable range
|
||||
auto root = (-halfB - sqrtd) / a;
|
||||
|
@ -48,7 +48,7 @@ class Sphere : Hittable
|
|||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
Point _center;
|
||||
double _radius;
|
||||
Material _mat;
|
||||
|
|
|
@ -5,17 +5,16 @@ import util;
|
|||
|
||||
class Vec3
|
||||
{
|
||||
pragma(inline):
|
||||
static Vec3 random()
|
||||
pragma(inline):
|
||||
static Vec3 random()
|
||||
{
|
||||
return new Vec3(randomDouble, randomDouble, randomDouble);
|
||||
}
|
||||
|
||||
pragma(inline):
|
||||
static Vec3 random(double min, double max)
|
||||
pragma(inline):
|
||||
static Vec3 random(double min, double max)
|
||||
{
|
||||
return new Vec3(randomDouble(min, max), randomDouble(min, max),
|
||||
randomDouble(min, max));
|
||||
return new Vec3(randomDouble(min, max), randomDouble(min, max), randomDouble(min, max));
|
||||
}
|
||||
|
||||
static randomInUnitSphere()
|
||||
|
@ -23,7 +22,10 @@ class Vec3
|
|||
while (true)
|
||||
{
|
||||
auto p = Vec3.random(-1, 1);
|
||||
if (p.lengthSquared >= 1) { continue; }
|
||||
if (p.lengthSquared >= 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +40,10 @@ class Vec3
|
|||
while (true)
|
||||
{
|
||||
auto p = new Vec3(randomDouble(-1, 1), randomDouble(-1, 1), 0);
|
||||
if (p.lengthSquared >= 1) { continue; }
|
||||
if (p.lengthSquared >= 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +169,7 @@ class Vec3
|
|||
|
||||
Vec3 refract(Vec3 n, double etaiOverEtat)
|
||||
{
|
||||
auto cosTheta = (-this).dot(n).fmin(1.0);
|
||||
const auto cosTheta = (-this).dot(n).fmin(1.0);
|
||||
Vec3 rOutperp = etaiOverEtat * (this + cosTheta * n);
|
||||
Vec3 rOutParallel = -sqrt((1.0 - rOutperp.lengthSquared).fabs) * n;
|
||||
return rOutperp + rOutParallel;
|
||||
|
@ -175,7 +180,7 @@ class Vec3
|
|||
return format("{X: %s, Y: %s, Z: %s}", x, y, z);
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
double _x;
|
||||
double _y;
|
||||
double _z;
|
||||
|
@ -210,6 +215,6 @@ unittest
|
|||
assert(v1.dot(v2) == 10);
|
||||
assert(v1.cross(v2).toString == "{X: -4, Y: 8, Z: -4}");
|
||||
auto vec3 = new Vec3(1, 5, 1);
|
||||
auto expected = vec3 / vec3.length;
|
||||
const auto expected = vec3 / vec3.length;
|
||||
assert(vec3.unitVector.toString == expected.toString);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue