This commit is contained in:
Michael Kohl 2021-03-10 23:23:56 +07:00
parent 61f9be5939
commit 8113ff8a01
15 changed files with 220 additions and 235 deletions

View File

@ -2,7 +2,7 @@
.PHONY: build clean
prog = owe
prog = rtow
build:
dub build

View File

@ -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"
}
}

BIN
rtow Executable file

Binary file not shown.

View File

@ -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;

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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));
}

View File

@ -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)

View File

@ -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;

View File

@ -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);
}