this repo has no description

added camera and rays

+125 -44
+2 -2
.gitignore
··· 1 - /zig-cache 2 - /zig-out 1 + **/zig-cache 2 + **/zig-out 3 3 /out
+79
src/camera.zig
··· 1 + const std = @import("std"); 2 + 3 + const zigimg = @import("zigimg"); 4 + const color = zigimg.color; 5 + const zm = @import("zmath"); 6 + 7 + const log = std.log.scoped(.camera); 8 + 9 + const Camera = @This(); 10 + 11 + image_height: usize, 12 + image_width: usize, 13 + aspect_ratio: f32, 14 + 15 + focal_lenght: f32, 16 + viewport_height: f32, 17 + viewport_width: f32, 18 + camera_center: zm.Vec, 19 + 20 + viewport_u: zm.Vec, 21 + viewport_v: zm.Vec, 22 + pixel_delta_u: zm.Vec, 23 + pixel_delta_v: zm.Vec, 24 + 25 + viewport_upper_left: zm.Vec, 26 + pixel00_loc: zm.Vec, 27 + 28 + image: zigimg.Image, 29 + 30 + pub fn init(allocator: std.mem.Allocator, image_width: usize, aspect_ratio: f32) !Camera { 31 + const image_height = @as(usize, @intFromFloat(@as(f32, @floatFromInt(image_width)) / aspect_ratio)); 32 + if (image_height < 1) return error.ImageWidthLessThanOne; 33 + 34 + const focal_lenght: f32 = 1.0; 35 + const viewport_height: f32 = 2.0; 36 + const viewport_width = viewport_height * (@as(f32, @floatFromInt(image_width)) / @as(f32, @floatFromInt(image_height))); 37 + const camera_center = zm.f32x4s(0.0); 38 + 39 + // Calculate the vectors across the horizontal and down the vertical viewport edges. 40 + const viewport_u = zm.f32x4(viewport_width, 0, 0, 0); 41 + const viewport_v = zm.f32x4(0, -viewport_height, 0, 0); 42 + 43 + // Calculate the horizontal and vertical delta vectors from pixel to pixel. 44 + const pixel_delta_u = viewport_u / zm.f32x4s(@as(f32, @floatFromInt(image_width))); 45 + const pixel_delta_v = viewport_v / zm.f32x4s(@as(f32, @floatFromInt(image_height))); 46 + 47 + // Calculate the location of the upper left pixel. 48 + 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); 49 + const pixel00_loc = viewport_upper_left + zm.f32x4s(0.5) * (pixel_delta_u + pixel_delta_v); 50 + 51 + log.debug("image_width: {}, image_height: {}, aspect_ratio: {d:.2}, focal_lenght: {d:.1}", .{ image_width, image_height, aspect_ratio, focal_lenght }); 52 + 53 + return Camera{ 54 + .image_width = image_width, 55 + .image_height = image_height, 56 + .aspect_ratio = aspect_ratio, 57 + 58 + .focal_lenght = focal_lenght, 59 + .viewport_height = viewport_height, 60 + .viewport_width = viewport_width, 61 + .camera_center = camera_center, 62 + 63 + .viewport_u = viewport_u, 64 + .viewport_v = viewport_v, 65 + .pixel_delta_u = pixel_delta_u, 66 + .pixel_delta_v = pixel_delta_v, 67 + 68 + .viewport_upper_left = viewport_upper_left, 69 + .pixel00_loc = pixel00_loc, 70 + 71 + .image = try zigimg.Image.create(allocator, image_width, image_height, zigimg.PixelFormat.rgba32), 72 + }; 73 + } 74 + 75 + pub fn setPixel(self: *Camera, x: usize, y: usize, c: color.Rgba32) !void { 76 + if (x >= self.image_width or y >= self.image_height) return error.OutOfBounds; 77 + const i = x + self.image_width * y; 78 + self.image.pixels.rgba32[i] = c; 79 + }
+19
src/ray.zig
··· 1 + const zm = @import("zmath"); 2 + 3 + const Ray = @This(); 4 + 5 + orig: zm.Vec, 6 + dir: zm.Vec, 7 + 8 + pub fn init(origin: zm.Vec, direction: zm.Vec) Ray { 9 + return Ray{ 10 + .orig = origin, 11 + .dir = direction, 12 + }; 13 + } 14 + 15 + pub fn color(self: *Ray) zm.Vec { 16 + const unit_direction = zm.normalize3(self.dir); 17 + const a = 0.5 * (unit_direction[1] + 1.0); 18 + return zm.f32x4s(1.0 - a) * zm.f32x4s(1.0) + zm.f32x4s(a) * zm.f32x4(0.5, 0.7, 1.0, 1.0); 19 + }
+25 -42
src/rayray.zig
··· 3 3 const spall = @import("spall"); 4 4 const zigimg = @import("zigimg"); 5 5 const color = zigimg.color; 6 + const zm = @import("zmath"); 7 + 8 + const Camera = @import("camera.zig"); 9 + const Ray = @import("ray.zig"); 6 10 7 11 const log = std.log.scoped(.rayray); 8 12 ··· 16 20 pub fn init(allocator: std.mem.Allocator) !Self { 17 21 return .{ 18 22 .allocator = allocator, 19 - .camera = try Camera.init(allocator, 256 * 10, 256 * 10), 23 + .camera = try Camera.init(allocator, 400, 16.0 / 9.0), 20 24 }; 21 25 } 22 26 ··· 30 34 defer s.end(); 31 35 32 36 const rows: usize = try std.Thread.getCpuCount(); 33 - const row_height = @divTrunc(self.camera.height, rows); 37 + const row_height = @divTrunc(self.camera.image_height, rows); 34 38 const num_threads = blk: { 35 - if (self.camera.height % rows == 0) { 39 + if (self.camera.image_height % rows == 0) { 36 40 break :blk rows; 37 41 } 38 42 break :blk rows + 1; ··· 44 48 defer self.allocator.free(threads); 45 49 46 50 for (0..num_threads) |row| { 47 - const t = try std.Thread.spawn(.{}, r, .{ &self.camera, row, row_height }); 51 + const t = try std.Thread.spawn(.{}, render_thread, .{ &self.camera, row, row_height }); 48 52 threads[row] = t; 49 53 } 50 54 ··· 55 59 return self.camera.image; 56 60 } 57 61 58 - fn r(cam: *Camera, row: usize, height: usize) void { 62 + fn render_thread(cam: *Camera, row: usize, height: usize) void { 59 63 spall.init_thread(); 60 64 defer spall.deinit_thread(); 61 65 62 66 const s = spall.trace(@src(), "Render Thread {}", .{row}); 63 67 defer s.end(); 64 68 65 - for (0..height) |iy| { 66 - const y = iy + height * row; 67 - if (y >= cam.height) break; 69 + for (0..height) |ij| { 70 + const j = ij + height * row; 71 + if (j >= cam.image_height) break; 68 72 69 - for (0..cam.width) |x| { 70 - const col = blk: { 71 - if (iy <= height - 5) { 72 - @setRuntimeSafety(false); 73 - break :blk color.Rgba32.initRgba( 74 - @intCast(x), 75 - @intCast(y), 76 - 0, 77 - 255, 78 - ); 79 - } 80 - break :blk color.Rgba32.initRgba(0, 0, 255, 255); 81 - }; 73 + for (0..cam.image_width) |i| { 74 + 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); 75 + const ray_direction = pixel_center - cam.camera_center; 76 + var ray = Ray.init(cam.camera_center, ray_direction); 77 + const col = vecToRgba(ray.color()); 82 78 83 - cam.setPixel(x, y, col) catch break; 79 + cam.setPixel(i, j, col) catch break; 84 80 } 85 81 } 86 82 } 87 83 }; 88 84 89 - pub const Camera = struct { 90 - width: usize, 91 - height: usize, 92 - image: zigimg.Image, 85 + fn vecToRgba(v: zm.Vec) color.Rgba32 { 86 + const r: u8 = @intFromFloat(255.999 * v[0]); 87 + const g: u8 = @intFromFloat(255.999 * v[1]); 88 + const b: u8 = @intFromFloat(255.999 * v[2]); 89 + const a: u8 = @intFromFloat(255.999 * v[3]); 93 90 94 - pub fn init(allocator: std.mem.Allocator, width: usize, height: usize) !Camera { 95 - const img = try zigimg.Image.create(allocator, width, height, zigimg.PixelFormat.rgba32); 96 - 97 - return Camera{ 98 - .width = width, 99 - .height = height, 100 - .image = img, 101 - }; 102 - } 103 - 104 - pub fn setPixel(self: *Camera, x: usize, y: usize, c: color.Rgba32) !void { 105 - if (x >= self.width or y >= self.height) return error.OutOfBounds; 106 - const i = x + self.width * y; 107 - self.image.pixels.rgba32[i] = c; 108 - } 109 - }; 91 + return color.Rgba32.initRgba(r, g, b, a); 92 + }