tangled
alpha
login
or
join now
altagos.dev
/
rayray
0
fork
atom
this repo has no description
0
fork
atom
overview
issues
pulls
pipelines
restructured the code
altagos.dev
2 years ago
23848442
9763abcd
+222
-92
6 changed files
expand all
collapse all
unified
split
src
hittable
sphere.zig
hittable.zig
interval.zig
ray.zig
rayray.zig
renderer.zig
+6
-5
src/hittable.zig
···
2
2
3
3
const zm = @import("zmath");
4
4
5
5
+
const IntervalF32 = @import("interval.zig").IntervalF32;
5
6
const Ray = @import("ray.zig");
6
7
pub const Sphere = @import("hittable/sphere.zig");
7
8
···
28
29
return .{ .sphere = sphere };
29
30
}
30
31
31
31
-
pub fn hit(self: *Hittable, r: *Ray, ray_tmin: f32, ray_tmax: f32) ?HitRecord {
32
32
+
pub fn hit(self: *Hittable, r: *Ray, ray_t: IntervalF32) ?HitRecord {
32
33
switch (self.*) {
33
34
.sphere => |*sphere| {
34
34
-
return sphere.hit(r, ray_tmin, ray_tmax);
35
35
+
return sphere.hit(r, ray_t);
35
36
},
36
37
}
37
38
···
56
57
try self.list.append(item);
57
58
}
58
59
59
59
-
pub fn hit(self: *HittableList, r: *Ray, ray_tmin: f32, ray_tmax: f32) ?HitRecord {
60
60
+
pub fn hit(self: *HittableList, r: *Ray, ray_t: IntervalF32) ?HitRecord {
60
61
var rec: ?HitRecord = null;
61
62
var hit_anything = false;
62
62
-
var closest_so_far = ray_tmax;
63
63
+
var closest_so_far = ray_t.max;
63
64
64
65
for (self.list.items) |*object| {
65
65
-
if (object.hit(r, ray_tmin, closest_so_far)) |new_rec| {
66
66
+
if (object.hit(r, IntervalF32.init(ray_t.min, closest_so_far))) |new_rec| {
66
67
rec = new_rec;
67
68
hit_anything = true;
68
69
closest_so_far = new_rec.t;
+4
-3
src/hittable/sphere.zig
···
1
1
const zm = @import("zmath");
2
2
3
3
+
const IntervalF32 = @import("../interval.zig").IntervalF32;
3
4
const Ray = @import("../ray.zig");
4
5
const HitRecord = @import("../hittable.zig").HitRecord;
5
6
···
8
9
center: zm.Vec,
9
10
radius: f32,
10
11
11
11
-
pub fn hit(self: *Sphere, r: *Ray, ray_tmin: f32, ray_tmax: f32) ?HitRecord {
12
12
+
pub fn hit(self: *Sphere, r: *Ray, ray_t: IntervalF32) ?HitRecord {
12
13
const oc = r.orig - self.center;
13
14
const a = zm.lengthSq3(r.dir)[0];
14
15
const half_b = zm.dot3(oc, r.dir)[0];
···
21
22
22
23
// Find the nearest root that lies in the acceptable range
23
24
var root = (-half_b - sqrtd) / a;
24
24
-
if (root <= ray_tmin or ray_tmax <= root) {
25
25
+
if (!ray_t.surrounds(root)) {
25
26
root = (-half_b + sqrtd) / a;
26
26
-
if (root <= ray_tmin or ray_tmax <= root) return null;
27
27
+
if (!ray_t.surrounds(root)) return null;
27
28
}
28
29
29
30
var rec = HitRecord{
+106
src/interval.zig
···
1
1
+
const std = @import("std");
2
2
+
3
3
+
pub const IntervalU8 = Interval(u8);
4
4
+
pub const IntervalU16 = Interval(u16);
5
5
+
pub const IntervalU32 = Interval(u32);
6
6
+
pub const IntervalU64 = Interval(u64);
7
7
+
pub const IntervalUsize = Interval(usize);
8
8
+
9
9
+
pub const IntervalI8 = Interval(i8);
10
10
+
pub const IntervalI16 = Interval(i16);
11
11
+
pub const IntervalI32 = Interval(i32);
12
12
+
pub const IntervalI64 = Interval(i64);
13
13
+
pub const IntervalIsize = Interval(isize);
14
14
+
15
15
+
pub const IntervalF32 = Interval(f32);
16
16
+
pub const IntervalF64 = Interval(f64);
17
17
+
18
18
+
pub const IntervalIteratorType = enum {
19
19
+
inclusive,
20
20
+
exclusive,
21
21
+
};
22
22
+
23
23
+
pub fn Interval(comptime T: type) type {
24
24
+
if (@typeInfo(T) == .Int) {
25
25
+
return struct {
26
26
+
const Self = @This();
27
27
+
28
28
+
pub const empty: Self = .{ .min = std.math.inf(T), .max = -std.math.inf(T) };
29
29
+
pub const universe: Self = .{ .min = -std.math.inf(T), .max = std.math.inf(T) };
30
30
+
31
31
+
pub const Iterator = struct {
32
32
+
interval: Self,
33
33
+
current: T,
34
34
+
35
35
+
lower_boundry: IntervalIteratorType = .inclusive,
36
36
+
upper_boundry: IntervalIteratorType = .exclusive,
37
37
+
38
38
+
pub fn init(
39
39
+
interval: Self,
40
40
+
lower_boundry: IntervalIteratorType,
41
41
+
upper_boundry: IntervalIteratorType,
42
42
+
) Iterator {
43
43
+
return .{
44
44
+
.interval = interval,
45
45
+
.current = if (lower_boundry == .inclusive) interval.min else interval.min + 1,
46
46
+
.lower_boundry = lower_boundry,
47
47
+
.upper_boundry = upper_boundry,
48
48
+
};
49
49
+
}
50
50
+
51
51
+
pub fn next(self: *Iterator) ?T {
52
52
+
self.current += 1;
53
53
+
if (self.current < self.interval.max or (self.current == self.interval.max and self.upper_boundry == .inclusive)) {
54
54
+
return self.current;
55
55
+
} else return null;
56
56
+
}
57
57
+
};
58
58
+
59
59
+
min: T,
60
60
+
max: T,
61
61
+
62
62
+
pub fn init(min: T, max: T) Interval {
63
63
+
return .{ .min = min, .max = max };
64
64
+
}
65
65
+
66
66
+
pub fn contains(self: *const Self, x: T) bool {
67
67
+
return self.min <= x and x <= self.max;
68
68
+
}
69
69
+
70
70
+
pub fn surrounds(self: *const Self, x: T) bool {
71
71
+
return self.min < x and x < self.max;
72
72
+
}
73
73
+
74
74
+
pub fn iter(self: *const Self) Iterator {
75
75
+
return Iterator{
76
76
+
.interval = self.*,
77
77
+
.current = self.min,
78
78
+
};
79
79
+
}
80
80
+
};
81
81
+
} else if (@typeInfo(T) == .Float) {
82
82
+
return struct {
83
83
+
pub const empty: @This() = .{ .min = std.math.inf(T), .max = -std.math.inf(T) };
84
84
+
pub const universe: @This() = .{ .min = -std.math.inf(T), .max = std.math.inf(T) };
85
85
+
86
86
+
const Self = @This();
87
87
+
88
88
+
min: T,
89
89
+
max: T,
90
90
+
91
91
+
pub fn init(min: T, max: T) Self {
92
92
+
return .{ .min = min, .max = max };
93
93
+
}
94
94
+
95
95
+
pub fn contains(self: *const Self, x: T) bool {
96
96
+
return self.min <= x and x <= self.max;
97
97
+
}
98
98
+
99
99
+
pub fn surrounds(self: *const Self, x: T) bool {
100
100
+
return self.min < x and x < self.max;
101
101
+
}
102
102
+
};
103
103
+
} else {
104
104
+
@compileError("Interval only supports Int and Float Types!");
105
105
+
}
106
106
+
}
-25
src/ray.zig
···
2
2
3
3
const zm = @import("zmath");
4
4
5
5
-
const hittable = @import("hittable.zig");
6
5
const Ray = @This();
7
6
8
7
orig: zm.Vec,
···
18
17
pub fn at(self: *Ray, t: f32) zm.Vec {
19
18
return self.orig + zm.f32x4s(t) * self.dir;
20
19
}
21
21
-
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));
25
25
-
}
26
26
-
27
27
-
const unit_direction = zm.normalize3(r.dir);
28
28
-
const a = 0.5 * (unit_direction[1] + 1.0);
29
29
-
return zm.f32x4s(1.0 - a) * zm.f32x4s(1.0) + zm.f32x4s(a) * zm.f32x4(0.5, 0.7, 1.0, 1.0);
30
30
-
}
31
31
-
32
32
-
fn hitSphere(center: zm.Vec, radius: f32, r: *Ray) f32 {
33
33
-
const oc = r.orig - center;
34
34
-
const a = zm.lengthSq3(r.dir)[0];
35
35
-
const half_b = zm.dot3(oc, r.dir)[0];
36
36
-
const c = zm.dot3(oc, oc)[0] - radius * radius;
37
37
-
const discriminant = half_b * half_b - a * c;
38
38
-
39
39
-
if (discriminant < 0) {
40
40
-
return -1.0;
41
41
-
} else {
42
42
-
return (-half_b - @sqrt(discriminant)) / a;
43
43
-
}
44
44
-
}
+26
-59
src/rayray.zig
···
5
5
const color = zigimg.color;
6
6
const zm = @import("zmath");
7
7
8
8
-
const Camera = @import("camera.zig");
8
8
+
pub const Camera = @import("camera.zig");
9
9
pub const hittable = @import("hittable.zig");
10
10
-
const Ray = @import("ray.zig");
10
10
+
pub const renderer = @import("renderer.zig");
11
11
12
12
const log = std.log.scoped(.rayray);
13
13
+
14
14
+
const ThreadTracker = struct {
15
15
+
thread: std.Thread,
16
16
+
done: std.atomic.Value(bool) = std.atomic.Value(bool).init(false),
17
17
+
marked_as_done: bool = false,
18
18
+
};
13
19
14
20
pub const Raytracer = struct {
15
21
const Self = @This();
···
48
54
49
55
log.debug("rows: {}, row_height: {}, num_threads: {}", .{ rows, row_height, num_threads });
50
56
51
51
-
var threads = try self.allocator.alloc(TaskTracker, num_threads);
57
57
+
var threads = try self.allocator.alloc(ThreadTracker, num_threads);
52
58
defer self.allocator.free(threads);
53
59
54
60
const finished_threads = try self.allocator.alloc(bool, num_threads);
55
61
56
62
for (0..num_threads) |row| {
57
57
-
const t = try std.Thread.spawn(.{}, render_thread, .{ &self.camera, &self.world, row, row_height, &threads[row].done });
63
63
+
const ctx = renderer.Context{ .cam = &self.camera, .world = &self.world, .done = &threads[row].done };
64
64
+
const t = try std.Thread.spawn(.{}, renderer.renderThread, .{ ctx, row, row_height });
58
65
threads[row].thread = t;
59
66
}
60
67
61
61
-
const stderr = std.io.getStdErr();
62
62
-
defer stderr.close();
68
68
+
// const stderr = std.io.getStdErr();
69
69
+
// // defer stderr.close();
63
70
64
64
-
var progress = std.Progress{
65
65
-
.terminal = stderr,
66
66
-
.supports_ansi_escape_codes = true,
67
67
-
};
68
68
-
var node = progress.start("Rendering Completed", num_threads);
69
69
-
node.activate();
71
71
+
// var progress = std.Progress{
72
72
+
// .terminal = stderr,
73
73
+
// .supports_ansi_escape_codes = true,
74
74
+
// };
75
75
+
// var node = progress.start("Rendering Completed", num_threads);
76
76
+
// node.activate();
70
77
71
78
while (true) {
72
79
var done = true;
73
73
-
node.activate();
74
80
75
81
for (0..num_threads) |id| {
76
76
-
if (threads[id].done and !threads[id].marked_as_done) {
82
82
+
if (threads[id].done.load(.Acquire) and !threads[id].marked_as_done) {
77
83
threads[id].thread.join();
78
84
threads[id].marked_as_done = true;
79
85
finished_threads[id] = true;
80
80
-
node.completeOne();
81
81
-
} else if (!threads[id].done) {
86
86
+
// node.completeOne();
87
87
+
} else if (!threads[id].done.load(.Acquire)) {
82
88
done = false;
83
89
}
84
90
}
85
91
92
92
+
// node.context.refresh();
93
93
+
86
94
if (done) break;
87
95
}
88
96
97
97
+
// node.end();
98
98
+
89
99
return self.camera.image;
90
100
}
91
91
-
92
92
-
fn render_thread(cam: *Camera, world: *hittable.HittableList, row: usize, height: usize, done: *bool) void {
93
93
-
spall.init_thread();
94
94
-
defer spall.deinit_thread();
95
95
-
96
96
-
log.debug("Started Render Thread {}", .{row});
97
97
-
98
98
-
const s = spall.trace(@src(), "Render Thread {}", .{row});
99
99
-
defer s.end();
100
100
-
101
101
-
for (0..height) |ij| {
102
102
-
const j = ij + height * row;
103
103
-
if (j >= cam.image_height) break;
104
104
-
105
105
-
for (0..cam.image_width) |i| {
106
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);
107
107
-
const ray_direction = pixel_center - cam.camera_center;
108
108
-
var ray = Ray.init(cam.camera_center, ray_direction);
109
109
-
const col = vecToRgba(ray.color(world));
110
110
-
111
111
-
cam.setPixel(i, j, col) catch break;
112
112
-
}
113
113
-
}
114
114
-
115
115
-
done.* = true;
116
116
-
117
117
-
// log.debug("Render Thread {} is done", .{row});
118
118
-
}
119
101
};
120
120
-
121
121
-
const TaskTracker = struct {
122
122
-
thread: std.Thread,
123
123
-
done: bool = false,
124
124
-
marked_as_done: bool = false,
125
125
-
};
126
126
-
127
127
-
fn vecToRgba(v: zm.Vec) color.Rgba32 {
128
128
-
const r: u8 = @intFromFloat(255.999 * v[0]);
129
129
-
const g: u8 = @intFromFloat(255.999 * v[1]);
130
130
-
const b: u8 = @intFromFloat(255.999 * v[2]);
131
131
-
const a: u8 = @intFromFloat(255.999 * v[3]);
132
132
-
133
133
-
return color.Rgba32.initRgba(r, g, b, a);
134
134
-
}
+80
src/renderer.zig
···
1
1
+
const std = @import("std");
2
2
+
3
3
+
const spall = @import("spall");
4
4
+
const zigimg = @import("zigimg");
5
5
+
const color = zigimg.color;
6
6
+
const zm = @import("zmath");
7
7
+
8
8
+
pub const Camera = @import("camera.zig");
9
9
+
pub const interval = @import("interval.zig");
10
10
+
pub const IntervalUsize = interval.IntervalUsize;
11
11
+
pub const IntervalF32 = interval.IntervalF32;
12
12
+
pub const hittable = @import("hittable.zig");
13
13
+
pub const Ray = @import("ray.zig");
14
14
+
15
15
+
const log = std.log.scoped(.renderer);
16
16
+
17
17
+
pub const Context = struct {
18
18
+
cam: *Camera,
19
19
+
world: *hittable.HittableList,
20
20
+
done: *std.atomic.Value(bool),
21
21
+
};
22
22
+
23
23
+
pub fn rayColor(r: *Ray, world: *hittable.HittableList) zm.Vec {
24
24
+
if (world.hit(r, IntervalF32.init(0, std.math.inf(f32)))) |rec| {
25
25
+
return zm.f32x4s(0.5) * (rec.normal + zm.f32x4(1, 1, 1, 1));
26
26
+
}
27
27
+
28
28
+
const unit_direction = zm.normalize3(r.dir);
29
29
+
const a = 0.5 * (unit_direction[1] + 1.0);
30
30
+
return zm.f32x4s(1.0 - a) * zm.f32x4s(1.0) + zm.f32x4s(a) * zm.f32x4(0.5, 0.7, 1.0, 1.0);
31
31
+
}
32
32
+
33
33
+
pub fn render(ctx: Context, height: IntervalUsize, width: IntervalUsize) void {
34
34
+
var height_iter = height.iter();
35
35
+
height_iter.upper_boundry = .inclusive;
36
36
+
37
37
+
while (height_iter.next()) |j| {
38
38
+
if (j >= ctx.cam.image_height) break;
39
39
+
40
40
+
var width_iter = width.iter();
41
41
+
height_iter.upper_boundry = .inclusive;
42
42
+
43
43
+
while (width_iter.next()) |i| inner: {
44
44
+
if (i >= ctx.cam.image_width) break :inner;
45
45
+
46
46
+
const pixel_center = ctx.cam.pixel00_loc + (zm.f32x4s(@as(f32, @floatFromInt(i))) * ctx.cam.pixel_delta_u) + (zm.f32x4s(@as(f32, @floatFromInt(j))) * ctx.cam.pixel_delta_v);
47
47
+
const ray_direction = pixel_center - ctx.cam.camera_center;
48
48
+
var ray = Ray.init(ctx.cam.camera_center, ray_direction);
49
49
+
const col = vecToRgba(rayColor(&ray, ctx.world));
50
50
+
51
51
+
ctx.cam.setPixel(i, j, col) catch break;
52
52
+
}
53
53
+
}
54
54
+
55
55
+
ctx.done.store(true, .Release);
56
56
+
}
57
57
+
58
58
+
pub fn renderThread(ctx: Context, row: usize, row_height: usize) void {
59
59
+
spall.init_thread();
60
60
+
defer spall.deinit_thread();
61
61
+
62
62
+
const height = IntervalUsize{ .min = row_height * row, .max = row_height * row + row_height };
63
63
+
const width = IntervalUsize{ .min = 0, .max = ctx.cam.image_width };
64
64
+
65
65
+
log.debug("Started Render Thread {}", .{row});
66
66
+
67
67
+
const s = spall.trace(@src(), "Render Thread {}", .{row});
68
68
+
defer s.end();
69
69
+
70
70
+
render(ctx, height, width);
71
71
+
}
72
72
+
73
73
+
fn vecToRgba(v: zm.Vec) color.Rgba32 {
74
74
+
const r: u8 = @intFromFloat(255.999 * v[0]);
75
75
+
const g: u8 = @intFromFloat(255.999 * v[1]);
76
76
+
const b: u8 = @intFromFloat(255.999 * v[2]);
77
77
+
const a: u8 = @intFromFloat(255.999 * v[3]);
78
78
+
79
79
+
return color.Rgba32.initRgba(r, g, b, a);
80
80
+
}