Compare commits
7 Commits
91fd65259c
...
b432e9a6dd
| Author | SHA1 | Date | |
|---|---|---|---|
| b432e9a6dd | |||
| 62317d57ae | |||
| 8d92cc861e | |||
| f2ade1eee2 | |||
| a4baedefec | |||
| 8adf1bcadb | |||
| aea437785a |
@ -1,33 +1,70 @@
|
|||||||
const vec = @import("./vec.zig");
|
const vec = @import("./vec.zig");
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
const Ray = @import("./ray.zig").Ray;
|
const Ray = @import("./ray.zig").Ray;
|
||||||
|
|
||||||
const Point3 = vec.Point3;
|
const Point3 = vec.Point3;
|
||||||
const Vec3 = vec.Vec3;
|
const Vec3 = vec.Vec3;
|
||||||
|
const random_in_unit_disk = vec.random_in_unit_disk;
|
||||||
|
const math = std.math;
|
||||||
|
const pi = math.pi;
|
||||||
|
const tan = math.tan;
|
||||||
|
|
||||||
|
// Utility Functions
|
||||||
|
|
||||||
|
fn degrees_to_radians(degrees: f32) f32 {
|
||||||
|
return degrees * pi / 180.0;
|
||||||
|
}
|
||||||
|
|
||||||
pub const Camera = struct {
|
pub const Camera = struct {
|
||||||
origin: Point3,
|
origin: Point3,
|
||||||
lower_left_corner: Point3,
|
lower_left_corner: Point3,
|
||||||
horizontal: Vec3,
|
horizontal: Vec3,
|
||||||
vertical: Vec3,
|
vertical: Vec3,
|
||||||
|
u: Vec3,
|
||||||
|
v: Vec3,
|
||||||
|
w: Vec3,
|
||||||
|
lens_radius: f32,
|
||||||
|
|
||||||
pub fn init() Camera {
|
pub fn init(
|
||||||
const aspect_ratio = 16.0 / 9.0;
|
lookfrom: Point3,
|
||||||
const viewport_height = 2.0;
|
lookat: Point3,
|
||||||
|
vup: Vec3,
|
||||||
|
vfov: f32, // vertical field-of-view in degrees
|
||||||
|
aspect_ratio: f32,
|
||||||
|
aperature: f32,
|
||||||
|
focus_dist: f32,
|
||||||
|
) Camera {
|
||||||
|
const theta = degrees_to_radians(vfov);
|
||||||
|
const h = tan(theta / 2);
|
||||||
|
const viewport_height = 2 * h;
|
||||||
const viewport_width = aspect_ratio * viewport_height;
|
const viewport_width = aspect_ratio * viewport_height;
|
||||||
const focal_length = 1.0;
|
|
||||||
const origin = Point3.init(0, 0, 0);
|
const w = lookfrom.sub(lookat).unit();
|
||||||
const horizontal = Vec3.init(viewport_width, 0.0, 0.0);
|
const u = vup.cross(w).unit();
|
||||||
const vertical = Vec3.init(0.0, viewport_height, 0.0);
|
const v = w.cross(u);
|
||||||
|
|
||||||
|
const origin = lookfrom;
|
||||||
|
const horizontal = u.scale(focus_dist * viewport_width);
|
||||||
|
const vertical = v.scale(focus_dist * viewport_height);
|
||||||
|
const lower_left_corner = origin.sub(horizontal.scale(0.5)).sub(vertical.scale(0.5)).sub(w.scale(focus_dist));
|
||||||
|
|
||||||
|
const lens_radius = aperature / 2;
|
||||||
|
|
||||||
return Camera{
|
return Camera{
|
||||||
.origin = origin,
|
.origin = origin,
|
||||||
.horizontal = horizontal,
|
.horizontal = horizontal,
|
||||||
.vertical = vertical,
|
.vertical = vertical,
|
||||||
.lower_left_corner = origin.sub(horizontal.scale(0.5)).sub(vertical.scale(0.5)).sub(Vec3.init(0, 0, focal_length)),
|
.lower_left_corner = lower_left_corner,
|
||||||
|
.u = u,
|
||||||
|
.v = v,
|
||||||
|
.w = w,
|
||||||
|
.lens_radius = lens_radius,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
pub fn get_ray(camera: Camera, u: f32, v: f32) Ray {
|
pub fn get_ray(camera: Camera, s: f32, t: f32) Ray {
|
||||||
return Ray.init(camera.origin, camera.lower_left_corner.add(camera.horizontal.scale(u).add(camera.vertical.scale(v).sub(camera.origin))));
|
const rd = random_in_unit_disk().scale(camera.lens_radius);
|
||||||
|
const offset = camera.u.scale(rd.x()).add(camera.v.scale(rd.y()));
|
||||||
|
return Ray.init(camera.origin.add(offset), camera.lower_left_corner.add(camera.horizontal.scale(s).add(camera.vertical.scale(t).sub(camera.origin).sub(offset))));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -16,6 +16,7 @@ const Hittable = hittable.Hittable;
|
|||||||
const HittableList = hittable_list.HittableList;
|
const HittableList = hittable_list.HittableList;
|
||||||
const Labertian = material.Labertian;
|
const Labertian = material.Labertian;
|
||||||
const Metal = material.Metal;
|
const Metal = material.Metal;
|
||||||
|
const Dielectric = material.Dielectric;
|
||||||
const Material = material.Material;
|
const Material = material.Material;
|
||||||
const Point3 = vec.Point3;
|
const Point3 = vec.Point3;
|
||||||
const Ray = ray.Ray;
|
const Ray = ray.Ray;
|
||||||
@ -24,6 +25,12 @@ const Vec3 = vec.Vec3;
|
|||||||
const info = std.log.info;
|
const info = std.log.info;
|
||||||
const random_in_hemisphere = vec.random_in_hemisphere;
|
const random_in_hemisphere = vec.random_in_hemisphere;
|
||||||
const write_color = color.write_color;
|
const write_color = color.write_color;
|
||||||
|
const math = std.math;
|
||||||
|
const pi = math.pi;
|
||||||
|
const cos = math.cos;
|
||||||
|
const create_labertian = material.create_labertian;
|
||||||
|
const create_metal = material.create_metal;
|
||||||
|
const create_dielectric = material.create_dielectric;
|
||||||
|
|
||||||
fn ray_color(r: Ray, world: Hittable, depth: isize) Color {
|
fn ray_color(r: Ray, world: Hittable, depth: isize) Color {
|
||||||
// If we've exceeded the ray bounce limit, no more light is gathered.
|
// If we've exceeded the ray bounce limit, no more light is gathered.
|
||||||
@ -76,31 +83,75 @@ fn render_row(q: *Queue(Task)) void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn random_scene() anyerror!Hittable {
|
||||||
|
var prng = std.rand.DefaultPrng.init(0);
|
||||||
|
const rand = prng.random();
|
||||||
|
var world = HittableList.init();
|
||||||
|
const ground_material = create_labertian(Color.init(0.5, 0.5, 0.5));
|
||||||
|
try world.add(Hittable{ .sphere = Sphere.init(Point3.init(0, -1000, 0), 1000, ground_material) });
|
||||||
|
|
||||||
|
var a: isize = -11;
|
||||||
|
while (a < 11) : (a += 1) {
|
||||||
|
var b: isize = -11;
|
||||||
|
while (b < 11) : (b += 1) {
|
||||||
|
const choose_mat = rand.float(f32);
|
||||||
|
const center = Point3.init(@intToFloat(f32, a) + 0.9 * rand.float(f32), 0.2, @intToFloat(f32, b) + 0.9 * rand.float(f32));
|
||||||
|
if (center.sub(Point3.init(4, 0.2, 0)).length() > 0.9) {
|
||||||
|
if (choose_mat < 0.8) {
|
||||||
|
const albedo = Color.random_min_max(0, 1).mul(Color.random_min_max(0, 1));
|
||||||
|
const sphere_material = create_labertian(albedo);
|
||||||
|
|
||||||
|
try world.add(Hittable{ .sphere = Sphere.init(center, 0.2, sphere_material) });
|
||||||
|
} else if (choose_mat < 0.95) {
|
||||||
|
const albedo = Color.init(
|
||||||
|
0.5 + rand.float(f32) / 2.0,
|
||||||
|
0.5 + rand.float(f32) / 2.0,
|
||||||
|
0.5 + rand.float(f32) / 2.0,
|
||||||
|
);
|
||||||
|
const fuzz = rand.float(f32) / 2.0;
|
||||||
|
const sphere_material = create_metal(albedo, fuzz);
|
||||||
|
|
||||||
|
try world.add(Hittable{ .sphere = Sphere.init(center, 0.2, sphere_material) });
|
||||||
|
} else {
|
||||||
|
const sphere_material = create_dielectric(1.5);
|
||||||
|
|
||||||
|
try world.add(Hittable{ .sphere = Sphere.init(center, 0.2, sphere_material) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const material1 = create_dielectric(1.5);
|
||||||
|
try world.add(Hittable{ .sphere = Sphere.init(Point3.init(0, 1, 0), 1.0, material1) });
|
||||||
|
|
||||||
|
const material2 = create_labertian(Color.init(0.4, 0.2, 0.1));
|
||||||
|
try world.add(Hittable{ .sphere = Sphere.init(Point3.init(-4, 1, 0), 1.0, material2) });
|
||||||
|
|
||||||
|
const material3 = create_metal(Color.init(0.7, 0.6, 0.5), 0.0);
|
||||||
|
try world.add(Hittable{ .sphere = Sphere.init(Point3.init(4, 1, 0), 1.0, material3) });
|
||||||
|
|
||||||
|
return Hittable{ .hittable_list = world };
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main() anyerror!void {
|
pub fn main() anyerror!void {
|
||||||
// Image
|
// Image
|
||||||
const aspect_ratio: f32 = 16.0 / 9.0;
|
const aspect_ratio: f32 = 3.0 / 2.0;
|
||||||
const image_width = 400;
|
const image_width = 1200;
|
||||||
const image_height = @floatToInt(isize, @intToFloat(f32, image_width) / aspect_ratio);
|
const image_height = @floatToInt(isize, @intToFloat(f32, image_width) / aspect_ratio);
|
||||||
const samples_per_pixel = 100;
|
const samples_per_pixel = 500;
|
||||||
const max_depth = 50;
|
const max_depth = 50;
|
||||||
|
|
||||||
// World
|
// World
|
||||||
var tmp_world = HittableList.init();
|
const world = try random_scene();
|
||||||
|
|
||||||
const material_ground = Material{ .labertian = Labertian{ .albedo = Color.init(0.8, 0.8, 0.0) } };
|
|
||||||
const material_center = Material{ .labertian = Labertian{ .albedo = Color.init(0.7, 0.3, 0.3) } };
|
|
||||||
const material_left = Material{ .metal = Metal{ .albedo = Color.init(0.8, 0.8, 0.8), .fuzz = 0.3 } };
|
|
||||||
const material_right = Material{ .metal = Metal{ .albedo = Color.init(0.8, 0.6, 0.2), .fuzz = 1.0 } };
|
|
||||||
|
|
||||||
try tmp_world.add(Hittable{ .sphere = Sphere.init(Point3.init(0, -100.5, -1), 100, material_ground) });
|
|
||||||
try tmp_world.add(Hittable{ .sphere = Sphere.init(Point3.init(0, 0, -1), 0.5, material_center) });
|
|
||||||
try tmp_world.add(Hittable{ .sphere = Sphere.init(Point3.init(-1, 0, -1), 0.5, material_left) });
|
|
||||||
try tmp_world.add(Hittable{ .sphere = Sphere.init(Point3.init(1, 0, -1), 0.5, material_right) });
|
|
||||||
const world = Hittable{ .hittable_list = tmp_world };
|
|
||||||
|
|
||||||
// Camera
|
// Camera
|
||||||
const cam = Camera.init();
|
const lookfrom = Point3.init(13, 2, 3);
|
||||||
|
const lookat = Point3.init(0, 0, 0);
|
||||||
|
const vup = Vec3.init(0, 1, 0);
|
||||||
|
const dist_to_focus = 10;
|
||||||
|
const aperature = 0.1;
|
||||||
|
|
||||||
|
const cam = Camera.init(lookfrom, lookat, vup, 20, aspect_ratio, aperature, dist_to_focus);
|
||||||
|
|
||||||
// Render
|
// Render
|
||||||
const Node = Queue(Task).Node;
|
const Node = Queue(Task).Node;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
const std = @import("std");
|
||||||
const vec = @import("./vec.zig");
|
const vec = @import("./vec.zig");
|
||||||
const Ray = @import("./ray.zig").Ray;
|
const Ray = @import("./ray.zig").Ray;
|
||||||
const HitRecord = @import("./hittable.zig").HitRecord;
|
const HitRecord = @import("./hittable.zig").HitRecord;
|
||||||
@ -6,21 +7,47 @@ const Vec3 = vec.Vec3;
|
|||||||
const Color = vec.Color;
|
const Color = vec.Color;
|
||||||
const random_unit_vector = vec.random_unit_vector;
|
const random_unit_vector = vec.random_unit_vector;
|
||||||
const random_in_unit_sphere = vec.random_in_unit_sphere;
|
const random_in_unit_sphere = vec.random_in_unit_sphere;
|
||||||
|
const pow = std.math.pow;
|
||||||
|
|
||||||
|
pub fn create_labertian(albedo: Vec3) Material {
|
||||||
|
return Material{ .labertian = Labertian{ .albedo = albedo } };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_metal(albedo: Vec3, fuzz: f32) Material {
|
||||||
|
return Material{ .metal = Metal{ .albedo = albedo, .fuzz = fuzz } };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_dielectric(ir: f32) Material {
|
||||||
|
return Material{ .dielectric = Dielectric{ .ir = ir } };
|
||||||
|
}
|
||||||
|
|
||||||
pub const Material = union(enum) {
|
pub const Material = union(enum) {
|
||||||
labertian: Labertian,
|
labertian: Labertian,
|
||||||
metal: Metal,
|
metal: Metal,
|
||||||
|
dielectric: Dielectric,
|
||||||
|
|
||||||
pub fn scatter(material: Material, r_in: Ray, rec: HitRecord) ?ScatterRec {
|
pub fn scatter(material: Material, r_in: Ray, rec: HitRecord) ?ScatterRec {
|
||||||
return switch (material) {
|
return switch (material) {
|
||||||
.labertian => |labertian| labertian.scatter(r_in, rec),
|
.labertian => |labertian| labertian.scatter(r_in, rec),
|
||||||
.metal => |metal| metal.scatter(r_in, rec),
|
.metal => |metal| metal.scatter(r_in, rec),
|
||||||
|
.dielectric => |dielectric| dielectric.scatter(r_in, rec),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ScatterRec = struct { attenuation: Color, scattered: Ray };
|
pub const ScatterRec = struct { attenuation: Color, scattered: Ray };
|
||||||
|
|
||||||
|
fn reflect(v: Vec3, n: Vec3) Vec3 {
|
||||||
|
return v.sub(n.scale(v.dot(n) * 2));
|
||||||
|
}
|
||||||
|
fn refract(uv: Vec3, n: Vec3, etai_over_etat: f32) Vec3 {
|
||||||
|
const cos_theta = @minimum(-uv.dot(n), 1.0);
|
||||||
|
|
||||||
|
const r_out_perp = uv.add(n.scale(cos_theta)).scale(etai_over_etat);
|
||||||
|
const r_out_parallel = n.scale(-@sqrt(@fabs(1.0 - r_out_perp.length_squared())));
|
||||||
|
return r_out_perp.add(r_out_parallel);
|
||||||
|
}
|
||||||
|
|
||||||
pub const Labertian = struct {
|
pub const Labertian = struct {
|
||||||
albedo: Vec3,
|
albedo: Vec3,
|
||||||
pub fn scatter(labertian: Labertian, r_in: Ray, rec: HitRecord) ?ScatterRec {
|
pub fn scatter(labertian: Labertian, r_in: Ray, rec: HitRecord) ?ScatterRec {
|
||||||
@ -53,7 +80,37 @@ pub const Metal = struct {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn reflect(v: Vec3, n: Vec3) Vec3 {
|
};
|
||||||
return v.sub(n.scale(v.dot(n) * 2));
|
|
||||||
|
var prng = std.rand.DefaultPrng.init(0);
|
||||||
|
const rand = prng.random();
|
||||||
|
pub const Dielectric = struct {
|
||||||
|
ir: f32, // Index of Refraction
|
||||||
|
|
||||||
|
pub fn scatter(dielectric: Dielectric, r_in: Ray, rec: HitRecord) ?ScatterRec {
|
||||||
|
const attenuation = Color.init(1, 1, 1);
|
||||||
|
const refraction_ratio = if (rec.front_face) 1.0 / dielectric.ir else dielectric.ir;
|
||||||
|
|
||||||
|
const unit_direction = r_in.direction().unit();
|
||||||
|
const cos_theta = @minimum(-unit_direction.dot(rec.normal), 1.0);
|
||||||
|
const sin_theta = @sqrt(1 - cos_theta * cos_theta);
|
||||||
|
|
||||||
|
const cannot_refract = refraction_ratio * sin_theta > 1;
|
||||||
|
const direction = if (cannot_refract or reflectance(cos_theta, refraction_ratio) > rand.float(f32))
|
||||||
|
reflect(unit_direction, rec.normal)
|
||||||
|
else
|
||||||
|
refract(unit_direction, rec.normal, refraction_ratio);
|
||||||
|
|
||||||
|
const scattered = Ray.init(rec.p, direction);
|
||||||
|
return ScatterRec{
|
||||||
|
.attenuation = attenuation,
|
||||||
|
.scattered = scattered,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
fn reflectance(cosine: f32, ref_idx: f32) f32 {
|
||||||
|
// Use Schlick's approximation for reflectance.
|
||||||
|
var r0 = (1 - ref_idx) / (1 + ref_idx);
|
||||||
|
r0 = r0 * r0;
|
||||||
|
return r0 + (1 - r0) * pow(f32, (1 - cosine), 5);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -47,12 +47,15 @@ pub const Vec3 = struct {
|
|||||||
const t = u.v * v.v;
|
const t = u.v * v.v;
|
||||||
return t[0] + t[1] + t[2];
|
return t[0] + t[1] + t[2];
|
||||||
}
|
}
|
||||||
/// Return value in unit cube from -1, 1
|
pub fn cross(u: Vec3, v: Vec3) Vec3 {
|
||||||
pub fn random() Vec3 {
|
return Vec3.init(u.v[1] * v.v[2] - u.v[2] * v.v[1], u.v[2] * v.v[0] - u.v[0] * v.v[2], u.v[0] * v.v[1] - u.v[1] * v.v[0]);
|
||||||
|
}
|
||||||
|
/// Return value in unit cube from [min, max).
|
||||||
|
pub fn random_min_max(min: f32, max: f32) Vec3 {
|
||||||
return Vec3.init(
|
return Vec3.init(
|
||||||
rand.float(f32) * 2 - 1,
|
min + (max - min) * rand.float(f32),
|
||||||
rand.float(f32) * 2 - 1,
|
min + (max - min) * rand.float(f32),
|
||||||
rand.float(f32) * 2 - 1,
|
min + (max - min) * rand.float(f32),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
pub fn near_zero(vec: Vec3) bool {
|
pub fn near_zero(vec: Vec3) bool {
|
||||||
@ -63,9 +66,16 @@ pub const Vec3 = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn random_in_unit_disk() Vec3 {
|
||||||
|
while (true) {
|
||||||
|
const p = Vec3.init(rand.float(f32) * 2 - 1, rand.float(f32) * 2 - 1, 0);
|
||||||
|
if (p.length_squared() >= 1) continue;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn random_in_unit_sphere() Vec3 {
|
pub fn random_in_unit_sphere() Vec3 {
|
||||||
while (true) {
|
while (true) {
|
||||||
const p = Vec3.random();
|
const p = Vec3.random_min_max(-1, 1);
|
||||||
if (p.length_squared() > 1) continue;
|
if (p.length_squared() > 1) continue;
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user