this repo has no description
1const std = @import("std");
2const random = std.crypto.random;
3
4const zigimg = @import("zigimg");
5const color = zigimg.color;
6const zm = @import("zmath");
7
8pub const Ray = @import("ray.zig");
9
10const log = std.log.scoped(.camera);
11
12const Camera = @This();
13
14pub const Options = struct {
15 image_width: usize,
16 aspect_ratio: f32,
17 samples_per_pixel: usize,
18 max_depth: usize,
19};
20
21image_height: usize,
22image_width: usize,
23aspect_ratio: f32,
24
25samples_per_pixel: usize,
26max_depth: usize,
27
28focal_lenght: f32,
29viewport_height: f32,
30viewport_width: f32,
31camera_center: zm.Vec,
32
33viewport_u: zm.Vec,
34viewport_v: zm.Vec,
35pixel_delta_u: zm.Vec,
36pixel_delta_v: zm.Vec,
37
38viewport_upper_left: zm.Vec,
39pixel00_loc: zm.Vec,
40
41image: zigimg.Image,
42
43pub fn init(allocator: std.mem.Allocator, opts: Options) !Camera {
44 const image_width = opts.image_width;
45 const aspect_ratio = opts.aspect_ratio;
46 const image_height = @as(usize, @intFromFloat(@as(f32, @floatFromInt(image_width)) / aspect_ratio));
47 if (image_height < 1) return error.ImageWidthLessThanOne;
48
49 const focal_lenght: f32 = 1.0;
50 const viewport_height: f32 = 2.0;
51 const viewport_width = viewport_height * (@as(f32, @floatFromInt(image_width)) / @as(f32, @floatFromInt(image_height)));
52 const camera_center = zm.f32x4s(0.0);
53
54 // Calculate the vectors across the horizontal and down the vertical viewport edges.
55 const viewport_u = zm.f32x4(viewport_width, 0, 0, 0);
56 const viewport_v = zm.f32x4(0, -viewport_height, 0, 0);
57
58 // Calculate the horizontal and vertical delta vectors from pixel to pixel.
59 const pixel_delta_u = viewport_u / zm.f32x4s(@as(f32, @floatFromInt(image_width)));
60 const pixel_delta_v = viewport_v / zm.f32x4s(@as(f32, @floatFromInt(image_height)));
61
62 // Calculate the location of the upper left pixel.
63 const viewport_upper_left = camera_center - zm.f32x4(0, 0, focal_lenght, 0) - viewport_u / zm.f32x4s(2.0) - viewport_v / zm.f32x4s(2.0);
64 const pixel00_loc = viewport_upper_left + zm.f32x4s(0.5) * (pixel_delta_u + pixel_delta_v);
65
66 log.debug("image_width: {}, image_height: {}, aspect_ratio: {d:.2}, focal_lenght: {d:.1}", .{ image_width, image_height, aspect_ratio, focal_lenght });
67
68 return Camera{
69 .image_width = image_width,
70 .image_height = image_height,
71 .aspect_ratio = aspect_ratio,
72
73 .samples_per_pixel = opts.samples_per_pixel,
74 .max_depth = opts.max_depth,
75
76 .focal_lenght = focal_lenght,
77 .viewport_height = viewport_height,
78 .viewport_width = viewport_width,
79 .camera_center = camera_center,
80
81 .viewport_u = viewport_u,
82 .viewport_v = viewport_v,
83 .pixel_delta_u = pixel_delta_u,
84 .pixel_delta_v = pixel_delta_v,
85
86 .viewport_upper_left = viewport_upper_left,
87 .pixel00_loc = pixel00_loc,
88
89 .image = try zigimg.Image.create(allocator, image_width, image_height, zigimg.PixelFormat.rgba32),
90 };
91}
92
93pub fn deinit(self: *Camera) void {
94 self.image.deinit();
95}
96
97pub fn getRay(self: *Camera, i: usize, j: usize) Ray {
98 const pixel_center = self.pixel00_loc + (zm.f32x4s(@as(f32, @floatFromInt(i))) * self.pixel_delta_u) + (zm.f32x4s(@as(f32, @floatFromInt(j))) * self.pixel_delta_v);
99 const pixel_sample = pixel_center + self.pixelSamplesSq();
100
101 const ray_direction = pixel_sample - self.camera_center;
102 return Ray.init(self.camera_center, ray_direction);
103}
104
105pub fn setPixel(self: *Camera, x: usize, y: usize, c: color.Rgba32) !void {
106 if (x >= self.image_width or y >= self.image_height) return error.OutOfBounds;
107 const i = x + self.image_width * y;
108 self.image.pixels.rgba32[i] = c;
109}
110
111fn pixelSamplesSq(self: *Camera) zm.Vec {
112 const px = zm.f32x4s(-0.5 + random.float(f32));
113 const py = zm.f32x4s(-0.5 + random.float(f32));
114 return (px * self.pixel_delta_u) + (py * self.pixel_delta_v);
115}