tangled
alpha
login
or
join now
altagos.dev
/
rayray
this repo has no description
0
fork
atom
overview
issues
pulls
pipelines
added world
altagos.dev
2 years ago
9763abcd
044c78d3
+143
-13
7 changed files
expand all
collapse all
unified
split
build.zig
src
camera.zig
hittable
sphere.zig
hittable.zig
main.zig
ray.zig
rayray.zig
+1
build.zig
···
24
24
.target = target,
25
25
.optimize = optimize,
26
26
});
27
27
+
addDeps(b, &exe.root_module);
27
28
exe.root_module.addImport("spall", spall.module("spall"));
28
29
exe.root_module.addImport("rayray", rayray);
29
30
+4
src/camera.zig
···
72
72
};
73
73
}
74
74
75
75
+
pub fn deinit(self: *Camera) void {
76
76
+
self.image.deinit();
77
77
+
}
78
78
+
75
79
pub fn setPixel(self: *Camera, x: usize, y: usize, c: color.Rgba32) !void {
76
80
if (x >= self.image_width or y >= self.image_height) return error.OutOfBounds;
77
81
const i = x + self.image_width * y;
+74
src/hittable.zig
···
1
1
+
const std = @import("std");
2
2
+
3
3
+
const zm = @import("zmath");
4
4
+
5
5
+
const Ray = @import("ray.zig");
6
6
+
pub const Sphere = @import("hittable/sphere.zig");
7
7
+
8
8
+
pub const HitRecord = struct {
9
9
+
p: zm.Vec,
10
10
+
normal: zm.Vec = zm.f32x4s(1.0),
11
11
+
t: f32,
12
12
+
front_face: bool = true,
13
13
+
14
14
+
pub fn setFaceNormal(self: *HitRecord, r: *Ray, outward_normal: zm.Vec) void {
15
15
+
self.front_face = zm.dot3(r.dir, outward_normal)[0] < 0.0;
16
16
+
self.normal = if (self.front_face) outward_normal else -outward_normal;
17
17
+
}
18
18
+
};
19
19
+
20
20
+
pub const HittableType = enum {
21
21
+
sphere,
22
22
+
};
23
23
+
24
24
+
pub const Hittable = union(HittableType) {
25
25
+
sphere: Sphere,
26
26
+
27
27
+
pub fn initSphere(sphere: Sphere) Hittable {
28
28
+
return .{ .sphere = sphere };
29
29
+
}
30
30
+
31
31
+
pub fn hit(self: *Hittable, r: *Ray, ray_tmin: f32, ray_tmax: f32) ?HitRecord {
32
32
+
switch (self.*) {
33
33
+
.sphere => |*sphere| {
34
34
+
return sphere.hit(r, ray_tmin, ray_tmax);
35
35
+
},
36
36
+
}
37
37
+
38
38
+
return null;
39
39
+
}
40
40
+
};
41
41
+
42
42
+
pub const HittableList = struct {
43
43
+
list: std.ArrayList(Hittable),
44
44
+
45
45
+
pub fn init(allocator: std.mem.Allocator) HittableList {
46
46
+
const list = std.ArrayList(Hittable).init(allocator);
47
47
+
48
48
+
return .{ .list = list };
49
49
+
}
50
50
+
51
51
+
pub fn deinit(self: *HittableList) void {
52
52
+
self.list.deinit();
53
53
+
}
54
54
+
55
55
+
pub fn add(self: *HittableList, item: Hittable) !void {
56
56
+
try self.list.append(item);
57
57
+
}
58
58
+
59
59
+
pub fn hit(self: *HittableList, r: *Ray, ray_tmin: f32, ray_tmax: f32) ?HitRecord {
60
60
+
var rec: ?HitRecord = null;
61
61
+
var hit_anything = false;
62
62
+
var closest_so_far = ray_tmax;
63
63
+
64
64
+
for (self.list.items) |*object| {
65
65
+
if (object.hit(r, ray_tmin, closest_so_far)) |new_rec| {
66
66
+
rec = new_rec;
67
67
+
hit_anything = true;
68
68
+
closest_so_far = new_rec.t;
69
69
+
}
70
70
+
}
71
71
+
72
72
+
return rec;
73
73
+
}
74
74
+
};
+38
src/hittable/sphere.zig
···
1
1
+
const zm = @import("zmath");
2
2
+
3
3
+
const Ray = @import("../ray.zig");
4
4
+
const HitRecord = @import("../hittable.zig").HitRecord;
5
5
+
6
6
+
const Sphere = @This();
7
7
+
8
8
+
center: zm.Vec,
9
9
+
radius: f32,
10
10
+
11
11
+
pub fn hit(self: *Sphere, r: *Ray, ray_tmin: f32, ray_tmax: f32) ?HitRecord {
12
12
+
const oc = r.orig - self.center;
13
13
+
const a = zm.lengthSq3(r.dir)[0];
14
14
+
const half_b = zm.dot3(oc, r.dir)[0];
15
15
+
const c = zm.dot3(oc, oc)[0] - self.radius * self.radius;
16
16
+
17
17
+
const discriminant = half_b * half_b - a * c;
18
18
+
if (discriminant < 0) return null;
19
19
+
20
20
+
const sqrtd = @sqrt(discriminant);
21
21
+
22
22
+
// Find the nearest root that lies in the acceptable range
23
23
+
var root = (-half_b - sqrtd) / a;
24
24
+
if (root <= ray_tmin or ray_tmax <= root) {
25
25
+
root = (-half_b + sqrtd) / a;
26
26
+
if (root <= ray_tmin or ray_tmax <= root) return null;
27
27
+
}
28
28
+
29
29
+
var rec = HitRecord{
30
30
+
.t = root,
31
31
+
.p = r.at(root),
32
32
+
};
33
33
+
34
34
+
const outward_normal = (rec.p - self.center) / zm.f32x4s(self.radius);
35
35
+
rec.setFaceNormal(r, outward_normal);
36
36
+
37
37
+
return rec;
38
38
+
}
+10
-2
src/main.zig
···
2
2
3
3
const a = @import("a");
4
4
const spall = @import("spall");
5
5
+
const zm = @import("zmath");
5
6
6
6
-
const Raytracer = @import("rayray").Raytracer;
7
7
+
const rayray = @import("rayray");
8
8
+
const Hittable = rayray.hittable.Hittable;
9
9
+
const HittableList = rayray.hittable.HittableList;
10
10
+
const Sphere = rayray.hittable.Sphere;
7
11
8
12
pub const std_options = .{
9
13
.log_level = .debug,
···
21
25
spall.init_thread();
22
26
defer spall.deinit_thread();
23
27
28
28
+
var world = HittableList.init(allocator);
29
29
+
try world.add(Hittable.initSphere(Sphere{ .center = zm.f32x4(0, 0, -1, 0), .radius = 0.5 }));
30
30
+
try world.add(Hittable.initSphere(Sphere{ .center = zm.f32x4(0, -100.5, -1, 0), .radius = 100 }));
31
31
+
24
32
const s = spall.trace(@src(), "Main", .{});
25
33
26
26
-
var raytracer = try Raytracer.init(allocator);
34
34
+
var raytracer = try rayray.Raytracer.init(allocator, world);
27
35
defer raytracer.deinit();
28
36
29
37
const img = try raytracer.render();
+6
-5
src/ray.zig
···
1
1
+
const std = @import("std");
2
2
+
1
3
const zm = @import("zmath");
2
4
5
5
+
const hittable = @import("hittable.zig");
3
6
const Ray = @This();
4
7
5
8
orig: zm.Vec,
···
16
19
return self.orig + zm.f32x4s(t) * self.dir;
17
20
}
18
21
19
19
-
pub fn color(r: *Ray) zm.Vec {
20
20
-
const t = hitSphere(zm.f32x4(0, 0, -1, 0), 0.5, r);
21
21
-
if (t > 0.0) {
22
22
-
const N = zm.normalize3(r.at(t) - zm.f32x4(0, 0, -1, 0));
23
23
-
return zm.f32x4s(0.5) * zm.f32x4(N[0] + 1, N[1] + 1, N[2] + 1, 1);
22
22
+
pub fn color(r: *Ray, world: *hittable.HittableList) zm.Vec {
23
23
+
if (world.hit(r, 0, std.math.inf(f32))) |rec| {
24
24
+
return zm.f32x4s(0.5) * (rec.normal + zm.f32x4(1, 1, 1, 1));
24
25
}
25
26
26
27
const unit_direction = zm.normalize3(r.dir);
+10
-6
src/rayray.zig
···
6
6
const zm = @import("zmath");
7
7
8
8
const Camera = @import("camera.zig");
9
9
+
pub const hittable = @import("hittable.zig");
9
10
const Ray = @import("ray.zig");
10
11
11
12
const log = std.log.scoped(.rayray);
···
16
17
allocator: std.mem.Allocator,
17
18
18
19
camera: Camera,
20
20
+
world: hittable.HittableList,
19
21
20
20
-
pub fn init(allocator: std.mem.Allocator) !Self {
22
22
+
pub fn init(allocator: std.mem.Allocator, world: hittable.HittableList) !Self {
21
23
return .{
22
24
.allocator = allocator,
23
25
.camera = try Camera.init(allocator, 400, 16.0 / 9.0),
26
26
+
.world = world,
24
27
};
25
28
}
26
29
27
27
-
pub fn deinit(self: *const Self) void {
28
28
-
_ = self;
30
30
+
pub fn deinit(self: *Self) void {
31
31
+
self.camera.deinit();
32
32
+
self.world.deinit();
29
33
}
30
34
31
35
// TODO: Render in cubes not in rows
···
50
54
const finished_threads = try self.allocator.alloc(bool, num_threads);
51
55
52
56
for (0..num_threads) |row| {
53
53
-
const t = try std.Thread.spawn(.{}, render_thread, .{ &self.camera, row, row_height, &threads[row].done });
57
57
+
const t = try std.Thread.spawn(.{}, render_thread, .{ &self.camera, &self.world, row, row_height, &threads[row].done });
54
58
threads[row].thread = t;
55
59
}
56
60
···
85
89
return self.camera.image;
86
90
}
87
91
88
88
-
fn render_thread(cam: *Camera, row: usize, height: usize, done: *bool) void {
92
92
+
fn render_thread(cam: *Camera, world: *hittable.HittableList, row: usize, height: usize, done: *bool) void {
89
93
spall.init_thread();
90
94
defer spall.deinit_thread();
91
95
···
102
106
const pixel_center = cam.pixel00_loc + (zm.f32x4s(@as(f32, @floatFromInt(i))) * cam.pixel_delta_u) + (zm.f32x4s(@as(f32, @floatFromInt(j))) * cam.pixel_delta_v);
103
107
const ray_direction = pixel_center - cam.camera_center;
104
108
var ray = Ray.init(cam.camera_center, ray_direction);
105
105
-
const col = vecToRgba(ray.color());
109
109
+
const col = vecToRgba(ray.color(world));
106
110
107
111
cam.setPixel(i, j, col) catch break;
108
112
}