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