Compare commits
8 Commits
f0da916a22
...
f4d3129d5a
| Author | SHA1 | Date | |
|---|---|---|---|
| f4d3129d5a | |||
| e31f5e0a3a | |||
| 84a0ba2ec6 | |||
| 5043a7e526 | |||
| 622c23d5ed | |||
| f2c68e0b6f | |||
| 287344c272 | |||
| 8bc5e347cc |
@ -5,3 +5,5 @@ Raytracers
|
||||
|
||||
Random collection of ray tracing experiments.
|
||||
|
||||
# TODO
|
||||
Implement http://www.kevinbeason.com/smallpt/
|
||||
|
||||
33
zigrtiow/src/camera.zig
Normal file
33
zigrtiow/src/camera.zig
Normal file
@ -0,0 +1,33 @@
|
||||
const vec = @import("./vec.zig");
|
||||
|
||||
const Ray = @import("./ray.zig").Ray;
|
||||
|
||||
const Point3 = vec.Point3;
|
||||
const Vec3 = vec.Vec3;
|
||||
|
||||
pub const Camera = struct {
|
||||
origin: Point3,
|
||||
lower_left_corner: Point3,
|
||||
horizontal: Vec3,
|
||||
vertical: Vec3,
|
||||
|
||||
pub fn init() Camera {
|
||||
const aspect_ratio = 16.0 / 9.0;
|
||||
const viewport_height = 2.0;
|
||||
const viewport_width = aspect_ratio * viewport_height;
|
||||
const focal_length = 1.0;
|
||||
const origin = Point3.init(0, 0, 0);
|
||||
const horizontal = Vec3.init(viewport_width, 0.0, 0.0);
|
||||
const vertical = Vec3.init(0.0, viewport_height, 0.0);
|
||||
|
||||
return Camera{
|
||||
.origin = origin,
|
||||
.horizontal = horizontal,
|
||||
.vertical = vertical,
|
||||
.lower_left_corner = origin.sub(horizontal.scale(0.5)).sub(vertical.scale(0.5)).sub(Vec3.init(0, 0, focal_length)),
|
||||
};
|
||||
}
|
||||
pub fn get_ray(camera: Camera, u: f32, v: f32) Ray {
|
||||
return Ray.init(camera.origin, camera.lower_left_corner.add(camera.horizontal.scale(u).add(camera.vertical.scale(v).sub(camera.origin))));
|
||||
}
|
||||
};
|
||||
@ -1,10 +1,24 @@
|
||||
const std = @import("std");
|
||||
|
||||
const clamp = @import("./utils.zig").clamp;
|
||||
const Color = @import("./vec.zig").Color;
|
||||
|
||||
pub fn write_color(c: Color) anyerror!void {
|
||||
var ir = @floatToInt(u8, 255.999 * c.x());
|
||||
var ig = @floatToInt(u8, 255.999 * c.y());
|
||||
var ib = @floatToInt(u8, 255.999 * c.z());
|
||||
pub fn write_color(c: Color, samples_per_pixel: isize) anyerror!void {
|
||||
var r = c.x();
|
||||
var g = c.y();
|
||||
var b = c.z();
|
||||
|
||||
// Divide the color by the number of samples.
|
||||
var scale = 1.0 / @intToFloat(f32, samples_per_pixel);
|
||||
r *= scale;
|
||||
g *= scale;
|
||||
b *= scale;
|
||||
|
||||
// Write the translated [0,255] value of each color component.
|
||||
var ir = @floatToInt(u8, 256 * clamp(r, 0, 0.999));
|
||||
var ig = @floatToInt(u8, 256 * clamp(g, 0, 0.999));
|
||||
var ib = @floatToInt(u8, 256 * clamp(b, 0, 0.999));
|
||||
|
||||
const stdout = std.io.getStdOut();
|
||||
try stdout.writer().print("{d} {d} {d}\n", .{ ir, ig, ib });
|
||||
}
|
||||
|
||||
36
zigrtiow/src/hittable.zig
Normal file
36
zigrtiow/src/hittable.zig
Normal file
@ -0,0 +1,36 @@
|
||||
const vec = @import("./vec.zig");
|
||||
const ray = @import("./ray.zig");
|
||||
|
||||
const Sphere = @import("./sphere.zig").Sphere;
|
||||
const HittableList = @import("./hittable_list.zig").HittableList;
|
||||
|
||||
const Vec3 = vec.Vec3;
|
||||
const Point3 = vec.Point3;
|
||||
const Ray = ray.Ray;
|
||||
|
||||
pub const Hittable = union(enum) {
|
||||
sphere: Sphere,
|
||||
hittable_list: HittableList,
|
||||
|
||||
pub fn hit(hittable: Hittable, r: Ray, t_min: f32, t_max: f32) ?HitRecord {
|
||||
return switch (hittable) {
|
||||
.sphere => |sphere| sphere.hit(r, t_min, t_max),
|
||||
.hittable_list => |hittable_list| hittable_list.hit(r, t_min, t_max),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const HitRecord = struct {
|
||||
p: Point3,
|
||||
normal: Vec3,
|
||||
t: f32,
|
||||
front_face: bool,
|
||||
|
||||
pub fn set_face_normal(hr: *HitRecord, r: Ray, outward_normal: Vec3) void {
|
||||
hr.front_face = r.direction().dot(outward_normal) < 0;
|
||||
hr.normal = if (hr.front_face)
|
||||
outward_normal
|
||||
else
|
||||
outward_normal.scale(-1);
|
||||
}
|
||||
};
|
||||
35
zigrtiow/src/hittable_list.zig
Normal file
35
zigrtiow/src/hittable_list.zig
Normal file
@ -0,0 +1,35 @@
|
||||
const std = @import("std");
|
||||
const ray = @import("./ray.zig");
|
||||
const hittable = @import("./hittable.zig");
|
||||
|
||||
const info = std.log.info;
|
||||
const ArrayList = std.ArrayList;
|
||||
const Ray = ray.Ray;
|
||||
const HitRecord = hittable.HitRecord;
|
||||
const Hittable = hittable.Hittable;
|
||||
|
||||
pub const HittableList = struct {
|
||||
objects: ArrayList(Hittable),
|
||||
|
||||
pub fn init() HittableList {
|
||||
const allocator = std.heap.page_allocator;
|
||||
|
||||
return HittableList{
|
||||
.objects = ArrayList(Hittable).init(allocator),
|
||||
};
|
||||
}
|
||||
pub fn hit(hittable_list: HittableList, r: Ray, t_min: f32, t_max: f32) ?HitRecord {
|
||||
var temp_rec: ?HitRecord = null;
|
||||
var closest_so_far = t_max;
|
||||
for (hittable_list.objects.items) |object| {
|
||||
if (object.hit(r, t_min, closest_so_far)) |rec| {
|
||||
temp_rec = rec;
|
||||
closest_so_far = rec.t;
|
||||
}
|
||||
}
|
||||
return temp_rec;
|
||||
}
|
||||
pub fn add(hittable_list: *HittableList, object: Hittable) anyerror!void {
|
||||
try hittable_list.objects.append(object);
|
||||
}
|
||||
};
|
||||
@ -1,31 +1,83 @@
|
||||
const std = @import("std");
|
||||
const info = std.log.info;
|
||||
const camera = @import("./camera.zig");
|
||||
const color = @import("./color.zig");
|
||||
const ray = @import("./ray.zig");
|
||||
const vec = @import("./vec.zig");
|
||||
const Vec3 = vec.Vec3;
|
||||
const sphere = @import("./sphere.zig");
|
||||
const hittable = @import("./hittable.zig");
|
||||
const hittable_list = @import("./hittable_list.zig");
|
||||
|
||||
const Camera = camera.Camera;
|
||||
const Color = vec.Color;
|
||||
const write_color = @import("./color.zig").write_color;
|
||||
const Hittable = hittable.Hittable;
|
||||
const HittableList = hittable_list.HittableList;
|
||||
const Point3 = vec.Point3;
|
||||
const Ray = ray.Ray;
|
||||
const Sphere = sphere.Sphere;
|
||||
const Vec3 = vec.Vec3;
|
||||
const info = std.log.info;
|
||||
const write_color = color.write_color;
|
||||
|
||||
fn ray_color(r: Ray, world: Hittable) Color {
|
||||
var hit = world.hit(r, 0, std.math.inf(f32));
|
||||
if (hit) |rec| {
|
||||
const n = rec.normal;
|
||||
return Color.init(
|
||||
n.x() + 1,
|
||||
n.y() + 1,
|
||||
n.z() + 1,
|
||||
).scale(0.5);
|
||||
}
|
||||
var unit_direction = r.direction().unit();
|
||||
const t = 0.5 * (unit_direction.y() + 1);
|
||||
return Color.init(1, 1, 1).scale(1 - t).add(Color.init(0.5, 0.7, 1.0).scale(t));
|
||||
}
|
||||
|
||||
pub fn main() anyerror!void {
|
||||
const image_width: isize = 256;
|
||||
const image_height: isize = 256;
|
||||
// Image
|
||||
const aspect_ratio: f32 = 16.0 / 9.0;
|
||||
const image_width = 400;
|
||||
const image_height = @floatToInt(isize, @intToFloat(f32, image_width) / aspect_ratio);
|
||||
const samples_per_pixel = 100;
|
||||
|
||||
// World
|
||||
var world = HittableList.init();
|
||||
try world.add(Hittable{ .sphere = Sphere.init(Point3.init(0, 0, -1), 0.5) });
|
||||
try world.add(Hittable{ .sphere = Sphere.init(Point3.init(0, -100.5, -1), 100) });
|
||||
|
||||
// Camera
|
||||
const cam = Camera.init();
|
||||
|
||||
// Render
|
||||
const stdout = std.io.getStdOut();
|
||||
|
||||
info("Writing image {d}x{d}\n", .{ image_width, image_height });
|
||||
info("Writing image {d}x{d}", .{ image_width, image_height });
|
||||
try stdout.writer().print("P3\n{d} {d}\n255\n", .{ image_width, image_height });
|
||||
|
||||
var prng = std.rand.DefaultPrng.init(0);
|
||||
const rand = prng.random();
|
||||
var j: isize = image_height - 1;
|
||||
while (j >= 0) : (j -= 1) {
|
||||
info("Scanlines remaining: {d}", .{j});
|
||||
//info("Scanlines remaining: {d}", .{j});
|
||||
|
||||
var i: isize = 0;
|
||||
while (i < image_width) : (i += 1) {
|
||||
const pixel_color = Color.init(@intToFloat(f32, i) / @intToFloat(f32, image_width - 1), @intToFloat(f32, j) / @intToFloat(f32, image_height - 1), 0.25);
|
||||
try write_color(pixel_color);
|
||||
var pixel_color = Color.init(0, 0, 0);
|
||||
var s: isize = 0;
|
||||
while (s < samples_per_pixel) : (s += 1) {
|
||||
const u = (@intToFloat(f32, i) + rand.float(f32)) / @intToFloat(f32, image_width - 1);
|
||||
const v = (@intToFloat(f32, j) + rand.float(f32)) / @intToFloat(f32, image_height - 1);
|
||||
const r = cam.get_ray(u, v);
|
||||
//Ray.init(origin, lower_left_corner.add(horizontal.scale(u)).add(vertical.scale(v)).sub(origin));
|
||||
pixel_color = pixel_color.add(ray_color(r, Hittable{ .hittable_list = world }));
|
||||
}
|
||||
try write_color(pixel_color, samples_per_pixel);
|
||||
}
|
||||
}
|
||||
info("Done", .{});
|
||||
}
|
||||
|
||||
test "basic test" {
|
||||
try std.testing.expectEqual(10, 3 + 7);
|
||||
test {
|
||||
// Run tests in imported source.
|
||||
std.testing.refAllDecls(@This());
|
||||
}
|
||||
|
||||
33
zigrtiow/src/ray.zig
Normal file
33
zigrtiow/src/ray.zig
Normal file
@ -0,0 +1,33 @@
|
||||
const std = @import("std");
|
||||
const vec = @import("./vec.zig");
|
||||
|
||||
const Vec3 = vec.Vec3;
|
||||
const Point3 = vec.Point3;
|
||||
|
||||
pub const Ray = struct {
|
||||
orig: Vec3,
|
||||
dir: Vec3,
|
||||
|
||||
pub fn init(orig: Point3, dir: Vec3) Ray {
|
||||
return Ray{
|
||||
.orig = orig,
|
||||
.dir = dir,
|
||||
};
|
||||
}
|
||||
pub fn origin(ray: Ray) Point3 {
|
||||
return ray.orig;
|
||||
}
|
||||
pub fn direction(ray: Ray) Vec3 {
|
||||
return ray.dir;
|
||||
}
|
||||
pub fn at(ray: Ray, t: f32) Point3 {
|
||||
return ray.orig.add(ray.dir.scale(t));
|
||||
}
|
||||
};
|
||||
|
||||
test "ray_at" {
|
||||
const want = Point3.init(0, 0, 1.5);
|
||||
const r = Ray.init(Point3.init(0, 0, 0), Vec3.init(0, 0, 1));
|
||||
const got = r.at(1.5);
|
||||
try std.testing.expectEqual(want, got);
|
||||
}
|
||||
51
zigrtiow/src/sphere.zig
Normal file
51
zigrtiow/src/sphere.zig
Normal file
@ -0,0 +1,51 @@
|
||||
const vec = @import("./vec.zig");
|
||||
const ray = @import("./ray.zig");
|
||||
const hittable = @import("./hittable.zig");
|
||||
|
||||
const Vec3 = vec.Vec3;
|
||||
const Point3 = vec.Point3;
|
||||
const Ray = ray.Ray;
|
||||
const HitRecord = hittable.HitRecord;
|
||||
|
||||
pub const Sphere = struct {
|
||||
center: Point3,
|
||||
radius: f32,
|
||||
|
||||
pub fn init(center: Point3, radius: f32) Sphere {
|
||||
return Sphere{
|
||||
.center = center,
|
||||
.radius = radius,
|
||||
};
|
||||
}
|
||||
pub fn hit(sphere: Sphere, r: Ray, t_min: f32, t_max: f32) ?HitRecord {
|
||||
const center = sphere.center;
|
||||
const radius = sphere.radius;
|
||||
const oc = r.origin().sub(center);
|
||||
const a = r.direction().length_squared();
|
||||
const half_b = Vec3.dot(oc, r.direction());
|
||||
const c = oc.length_squared() - radius * radius;
|
||||
|
||||
const discriminant = half_b * half_b - a * c;
|
||||
if (discriminant < 0) return null;
|
||||
|
||||
const sqrtd = @sqrt(discriminant);
|
||||
|
||||
// Find the nearest root that lies in the acceptable range.
|
||||
var root = (-half_b - sqrtd) / a;
|
||||
|
||||
if ((root < t_min) or (t_max < root)) {
|
||||
root = (-half_b + sqrtd) / a;
|
||||
if ((root < t_min) or (t_max < root)) return null;
|
||||
}
|
||||
const p = r.at(root);
|
||||
const outward_normal = p.sub(center).scale(1 / radius);
|
||||
var hr = HitRecord{
|
||||
.t = root,
|
||||
.p = p,
|
||||
.normal = Vec3.init(0, 0, 0),
|
||||
.front_face = false,
|
||||
};
|
||||
hr.set_face_normal(r, outward_normal);
|
||||
return hr;
|
||||
}
|
||||
};
|
||||
5
zigrtiow/src/utils.zig
Normal file
5
zigrtiow/src/utils.zig
Normal file
@ -0,0 +1,5 @@
|
||||
pub fn clamp(x: f32, min: f32, max: f32) f32 {
|
||||
if (x < min) return min;
|
||||
if (x > max) return max;
|
||||
return x;
|
||||
}
|
||||
@ -14,6 +14,28 @@ pub const Vec3 = struct {
|
||||
pub fn z(vec: Vec3) f32 {
|
||||
return vec.v[2];
|
||||
}
|
||||
pub fn length(vec: Vec3) f32 {
|
||||
return @sqrt(vec.length_squared());
|
||||
}
|
||||
pub fn length_squared(vec: Vec3) f32 {
|
||||
return vec.v[0] * vec.v[0] + vec.v[1] * vec.v[1] + vec.v[2] * vec.v[2];
|
||||
}
|
||||
pub fn add(lhs: Vec3, rhs: Vec3) Vec3 {
|
||||
return Vec3{ .v = lhs.v + rhs.v };
|
||||
}
|
||||
pub fn sub(lhs: Vec3, rhs: Vec3) Vec3 {
|
||||
return Vec3{ .v = lhs.v - rhs.v };
|
||||
}
|
||||
pub fn scale(vec: Vec3, t: f32) Vec3 {
|
||||
return Vec3{ .v = vec.v * @splat(3, t) };
|
||||
}
|
||||
pub fn unit(vec: Vec3) Vec3 {
|
||||
return vec.scale(1 / vec.length());
|
||||
}
|
||||
pub fn dot(u: Vec3, v: Vec3) f32 {
|
||||
const t = u.v * v.v;
|
||||
return t[0] + t[1] + t[2];
|
||||
}
|
||||
};
|
||||
pub const Color = Vec3;
|
||||
pub const Point3 = Vec3;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user