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"); 9const util = @import("util.zig"); 10 11const log = std.log.scoped(.camera); 12 13const Camera = @This(); 14 15pub const Options = struct { 16 image_width: usize, 17 aspect_ratio: f32, 18 samples_per_pixel: usize, 19 max_depth: usize, 20 21 vfov: f32 = 90, 22 look_from: zm.Vec = zm.f32x4s(0), 23 look_at: zm.Vec = zm.f32x4(0, 0, -1, 0), 24 vup: zm.Vec = zm.f32x4(0, 1, 0, 0), 25 26 defocus_angle: f32 = 0, 27 focus_dist: f32 = 10, 28}; 29 30image_height: usize, 31image_width: usize, 32aspect_ratio: f32, 33 34samples_per_pixel: usize, 35max_depth: usize, 36 37vfov: f32, 38look_from: zm.Vec, 39look_at: zm.Vec, 40vup: zm.Vec, 41 42defocus_angle: f32, 43focus_dist: f32, 44 45// focal_lenght: f32, 46viewport_height: f32, 47viewport_width: f32, 48center: zm.Vec, 49 50viewport_u: zm.Vec, 51viewport_v: zm.Vec, 52pixel_delta_u: zm.Vec, 53pixel_delta_v: zm.Vec, 54u: zm.Vec, 55v: zm.Vec, 56w: zm.Vec, 57defocus_disk_u: zm.Vec, 58defocus_disk_v: zm.Vec, 59 60viewport_upper_left: zm.Vec, 61pixel00_loc: zm.Vec, 62 63image: zigimg.Image, 64 65pub fn init(allocator: std.mem.Allocator, opts: Options) !Camera { 66 const image_width = opts.image_width; 67 const aspect_ratio = opts.aspect_ratio; 68 const image_height = @as(usize, @intFromFloat(@as(f32, @floatFromInt(image_width)) / aspect_ratio)); 69 if (image_height < 1) return error.ImageWidthLessThanOne; 70 71 const vfov = opts.vfov; 72 const look_from = opts.look_from; 73 const look_at = opts.look_at; 74 const vup = opts.vup; 75 const center = look_from; 76 77 const defocus_angle = opts.defocus_angle; 78 const focus_dist = opts.focus_dist; 79 80 // const focal_lenght: f32 = zm.length3(look_from - look_at)[0]; 81 const theta = util.degreesToRadians(opts.vfov); 82 const h = @tan(theta / 2); 83 const viewport_height: f32 = 2 * h * focus_dist; 84 const viewport_width = viewport_height * (@as(f32, @floatFromInt(image_width)) / @as(f32, @floatFromInt(image_height))); 85 86 const w = zm.normalize3(look_from - look_at); 87 const u = zm.normalize3(zm.cross3(vup, w)); 88 const v = zm.cross3(w, u); 89 90 // Calculate the vectors across the horizontal and down the vertical viewport edges. 91 const viewport_u = zm.f32x4s(viewport_width) * u; 92 const viewport_v = zm.f32x4s(viewport_height) * -v; 93 94 // Calculate the horizontal and vertical delta vectors from pixel to pixel. 95 const pixel_delta_u = viewport_u / zm.f32x4s(@as(f32, @floatFromInt(image_width))); 96 const pixel_delta_v = viewport_v / zm.f32x4s(@as(f32, @floatFromInt(image_height))); 97 98 // Calculate the location of the upper left pixel. 99 const viewport_upper_left = center - zm.f32x4s(focus_dist) * w - viewport_u / zm.f32x4s(2.0) - viewport_v / zm.f32x4s(2.0); 100 const pixel00_loc = viewport_upper_left + zm.f32x4s(0.5) * (pixel_delta_u + pixel_delta_v); 101 102 // Calculate the camera defocus disk basis vectors. 103 const defocus_radius = focus_dist * @tan(util.degreesToRadians(defocus_angle / 2)); 104 const defocus_disk_u = u * zm.f32x4s(defocus_radius); 105 const defocus_disk_v = v * zm.f32x4s(defocus_radius); 106 107 // log.debug("image_width: {}, image_height: {}, aspect_ratio: {d:.2}, focal_lenght: {d:.1}", .{ 108 // image_width, 109 // image_height, 110 // aspect_ratio, 111 // focal_lenght, 112 // }); 113 114 return Camera{ 115 .image_width = image_width, 116 .image_height = image_height, 117 .aspect_ratio = aspect_ratio, 118 119 .samples_per_pixel = opts.samples_per_pixel, 120 .max_depth = opts.max_depth, 121 122 .vfov = vfov, 123 .look_from = look_from, 124 .look_at = look_at, 125 .vup = vup, 126 127 .defocus_angle = opts.defocus_angle, 128 .focus_dist = opts.focus_dist, 129 130 // .focal_lenght = opts.focal_lenght, 131 .viewport_height = viewport_height, 132 .viewport_width = viewport_width, 133 .center = center, 134 135 .viewport_u = viewport_u, 136 .viewport_v = viewport_v, 137 .pixel_delta_u = pixel_delta_u, 138 .pixel_delta_v = pixel_delta_v, 139 .u = u, 140 .v = v, 141 .w = w, 142 .defocus_disk_u = defocus_disk_u, 143 .defocus_disk_v = defocus_disk_v, 144 145 .viewport_upper_left = viewport_upper_left, 146 .pixel00_loc = pixel00_loc, 147 148 .image = try zigimg.Image.create(allocator, image_width, image_height, zigimg.PixelFormat.rgba32), 149 }; 150} 151 152pub fn deinit(self: *Camera) void { 153 self.image.deinit(); 154} 155 156pub fn getRay(self: *Camera, i: usize, j: usize) Ray { 157 const offset = sampleSquare(); 158 const pixel_sample = self.pixel00_loc + 159 (zm.f32x4s(@as(f32, @floatFromInt(i)) + offset[0]) * self.pixel_delta_u) + 160 (zm.f32x4s(@as(f32, @floatFromInt(j)) + offset[1]) * self.pixel_delta_v); 161 162 const ray_orig = if (self.defocus_angle <= 0) self.center else self.defocusDiskSample(); 163 const ray_direction = pixel_sample - ray_orig; 164 return Ray.init(ray_orig, ray_direction); 165} 166 167fn sampleSquare() zm.Vec { 168 return zm.f32x4(util.randomF32() - 0.5, util.randomF32() - 0.5, 0, 0); 169} 170 171fn defocusDiskSample(self: *Camera) zm.Vec { 172 const p = util.randomInUnitDisk(); 173 return self.center + (zm.f32x4s(p[0]) * self.defocus_disk_u) + (zm.f32x4s(p[1]) * self.defocus_disk_v); 174} 175 176pub fn setPixel(self: *Camera, x: usize, y: usize, c: color.Rgba32) !void { 177 if (x >= self.image_width or y >= self.image_height) return error.OutOfBounds; 178 const i = x + self.image_width * y; 179 self.image.pixels.rgba32[i] = c; 180} 181 182fn pixelSamplesSq(self: *Camera) zm.Vec { 183 const px = zm.f32x4s(-0.5 + random.float(f32)); 184 const py = zm.f32x4s(-0.5 + random.float(f32)); 185 return (px * self.pixel_delta_u) + (py * self.pixel_delta_v); 186}