From d6c58417c372a56e55aa8259a50931b31c325710 Mon Sep 17 00:00:00 2001 From: huntears Date: Mon, 22 Apr 2024 15:26:39 +0200 Subject: [PATCH 1/3] Add basic scene data structures --- src/Scene.zig | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/Scene.zig diff --git a/src/Scene.zig b/src/Scene.zig new file mode 100644 index 0000000..98563f0 --- /dev/null +++ b/src/Scene.zig @@ -0,0 +1,18 @@ +const Sphere = @import("Sphere.zig").Sphere; +const Camera = @import("Camera.zig").Camera; +const Light = @import("Light.zig").Light; + +const SceneObject = union(enum) { + sphere: Sphere, +}; + +const SceneLight = union(enum) { + point_light: Light, + ambient_light: f32, // TODO: Have a properly defined ambient_light type +}; + +const Scene = struct { + camera: Camera, + objects: []SceneObject, + lights: []SceneLight, +}; From 0d088114f420c9ba7056dd038e78c2e759bd1332 Mon Sep 17 00:00:00 2001 From: huntears Date: Mon, 22 Apr 2024 16:18:24 +0200 Subject: [PATCH 2/3] Add proper scene building --- src/Scene.zig | 15 ++++++++++----- src/main.zig | 40 ++++++++++++++++++++++++++-------------- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/src/Scene.zig b/src/Scene.zig index 98563f0..5bccb32 100644 --- a/src/Scene.zig +++ b/src/Scene.zig @@ -1,18 +1,23 @@ const Sphere = @import("Sphere.zig").Sphere; const Camera = @import("Camera.zig").Camera; const Light = @import("Light.zig").Light; +const Cylinder = @import("Cylinder.zig").Cylinder; +const Plane = @import("Plane.zig").Plane; +const std = @import("std"); -const SceneObject = union(enum) { +pub const SceneObject = union(enum) { sphere: Sphere, + plane: Plane, + cylinder: Cylinder, }; -const SceneLight = union(enum) { +pub const SceneLight = union(enum) { point_light: Light, ambient_light: f32, // TODO: Have a properly defined ambient_light type }; -const Scene = struct { +pub const Scene = struct { camera: Camera, - objects: []SceneObject, - lights: []SceneLight, + objects: std.ArrayList(SceneObject), + lights: std.ArrayList(SceneLight), }; diff --git a/src/main.zig b/src/main.zig index a194beb..5880320 100644 --- a/src/main.zig +++ b/src/main.zig @@ -10,6 +10,7 @@ pub const Plane = @import("Plane.zig").Plane; const HitRecord = @import("HitRecord.zig").HitRecord; const Transformation = @import("Transformation.zig"); const Cylinder = @import("Cylinder.zig").Cylinder; +const Scene = @import("Scene.zig"); pub fn compute_lighting(intersection: Vec3, normal: Vec3, light: Pt3, ambient: f32) f32 { const L = intersection.to(light); @@ -48,20 +49,31 @@ pub fn main() !void { // .normal = .{ .x = 0, .y = 1, .z = 0 }, // .origin = .{ .x = 0, .y = -1, .z = 1 }, // }; - const sphere = Cylinder{ .radius = 0.5, .origin = Pt3{ .x = 2, .y = 0, .z = 10 } }; - const sphere_translation = Transformation.Transformation{ .rotation = .{ .x = 0.5, .y = 0.2, .z = 0 } }; + const cylinder = Cylinder{ .radius = 0.5, .origin = Pt3{ .x = 2, .y = 0, .z = 10 } }; + const cylinder_translation = Transformation.Transformation{ .rotation = .{ .x = 0.5, .y = 0.2, .z = 0 } }; const light = Light{ .color = .{ .blue = 255, .green = 255, .red = 255 }, .intensity = 1, .position = .{ .x = 0, .y = 1, .z = 2 }, }; - const ambiant_color_intensity = 0.1; - const height = 1000; - const width = 1000; + const ambient_color_intensity = 0.1; + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); - const allocator = gpa.allocator(); + + var scene = Scene.Scene{ + .camera = camera, + .lights = std.ArrayList(Scene.SceneLight).init(allocator), + .objects = std.ArrayList(Scene.SceneObject).init(allocator), + }; + try scene.objects.append(.{ .cylinder = cylinder }); + try scene.lights.append(.{ .point_light = light }); + try scene.lights.append(.{ .ambient_light = ambient_color_intensity }); + + const height = 1000; + const width = 1000; + var image = qoi.Image{ .width = std.math.cast(u32, width) orelse return error.Overflow, .height = std.math.cast(u32, height) orelse return error.Overflow, @@ -76,20 +88,20 @@ pub fn main() !void { const scaled_x: f32 = @as(f32, @floatFromInt(x)) / @as(f32, @floatFromInt(width)); const scaled_y: f32 = @as(f32, @floatFromInt((height - 1) - y)) / @as(f32, @floatFromInt(height)); const ray: Ray = camera.createRay(scaled_x, scaled_y); - const ray_object = Transformation.ray_global_to_object(ray, sphere_translation, sphere); - var record = sphere.hits(ray_object); - record = Transformation.hitRecord_object_to_global(record, sphere_translation, sphere); + const ray_object = Transformation.ray_global_to_object(ray, cylinder_translation, cylinder); + var record = cylinder.hits(ray_object); + record = Transformation.hitRecord_object_to_global(record, cylinder_translation, cylinder); record.intersection_point.x = record.intersection_point.x + record.normal.x * 0.001; record.intersection_point.y = record.intersection_point.y + record.normal.y * 0.001; record.intersection_point.z = record.intersection_point.z + record.normal.z * 0.001; if (record.hit) { const vec_to_light = record.intersection_point.to(light.position); - const vec_to_light_object = Transformation.ray_global_to_object(Ray{ .direction = vec_to_light, .origin = record.intersection_point }, sphere_translation, sphere); - var obstacle = sphere.hits(vec_to_light_object); - obstacle = Transformation.hitRecord_object_to_global(obstacle, sphere_translation, sphere); + const vec_to_light_object = Transformation.ray_global_to_object(Ray{ .direction = vec_to_light, .origin = record.intersection_point }, cylinder_translation, cylinder); + var obstacle = cylinder.hits(vec_to_light_object); + obstacle = Transformation.hitRecord_object_to_global(obstacle, cylinder_translation, cylinder); if (obstacle.hit) { image.pixels[index] = .{ - .r = @as(u8, @intFromFloat(255.0 * ambiant_color_intensity)), + .r = @as(u8, @intFromFloat(255.0 * ambient_color_intensity)), .g = 0, .b = 0, .a = 255, @@ -97,7 +109,7 @@ pub fn main() !void { } else { const norm = record.normal; const inter = record.intersection_point; - const light_color = 255.0 * compute_lighting(inter, norm, light.position, ambiant_color_intensity); + const light_color = 255.0 * compute_lighting(inter, norm, light.position, ambient_color_intensity); image.pixels[index] = .{ .r = @as(u8, @intFromFloat(light_color)), .g = 0, From 558f5bb92e69b13aaf4eb343b06ee8a1ec43b3d1 Mon Sep 17 00:00:00 2001 From: huntears Date: Mon, 22 Apr 2024 16:43:15 +0200 Subject: [PATCH 3/3] Add proper init/deinit of scenes --- src/Scene.zig | 19 +++++++++ src/main.zig | 111 +++++++++++++++++++++++++++----------------------- 2 files changed, 78 insertions(+), 52 deletions(-) diff --git a/src/Scene.zig b/src/Scene.zig index 5bccb32..e1ef341 100644 --- a/src/Scene.zig +++ b/src/Scene.zig @@ -3,6 +3,7 @@ const Camera = @import("Camera.zig").Camera; const Light = @import("Light.zig").Light; const Cylinder = @import("Cylinder.zig").Cylinder; const Plane = @import("Plane.zig").Plane; +const Transformation = @import("Transformation.zig").Transformation; const std = @import("std"); pub const SceneObject = union(enum) { @@ -17,7 +18,25 @@ pub const SceneLight = union(enum) { }; pub const Scene = struct { + const Self = @This(); + camera: Camera, objects: std.ArrayList(SceneObject), lights: std.ArrayList(SceneLight), + transforms: std.ArrayList(Transformation), + + pub fn init(allocator: std.mem.Allocator, camera: Camera) Self { + return Self{ + .camera = camera, + .objects = std.ArrayList(SceneObject).init(allocator), + .lights = std.ArrayList(SceneLight).init(allocator), + .transforms = std.ArrayList(Transformation).init(allocator), + }; + } + + pub fn deinit(self: *Self) void { + self.objects.deinit(); + self.lights.deinit(); + self.transforms.deinit(); + } }; diff --git a/src/main.zig b/src/main.zig index 5880320..4eed0e0 100644 --- a/src/main.zig +++ b/src/main.zig @@ -6,7 +6,7 @@ const Sphere = @import("Sphere.zig").Sphere; const Camera = @import("Camera.zig").Camera; const qoi = @import("qoi.zig"); const Light = @import("Light.zig").Light; -pub const Plane = @import("Plane.zig").Plane; +const Plane = @import("Plane.zig").Plane; const HitRecord = @import("HitRecord.zig").HitRecord; const Transformation = @import("Transformation.zig"); const Cylinder = @import("Cylinder.zig").Cylinder; @@ -19,6 +19,59 @@ pub fn compute_lighting(intersection: Vec3, normal: Vec3, light: Pt3, ambient: f return std.math.clamp(1.0 * n_dot_l / (normal.length() * L.length()), ambient, 1.0); } +fn calculate_image(pixels: []qoi.Color, scene: *Scene.Scene, height: u32, width: u32) void { + var index: usize = 0; + for (0..height) |y| { + for (0..width) |x| { + const scaled_x: f32 = @as(f32, @floatFromInt(x)) / @as(f32, @floatFromInt(width)); + const scaled_y: f32 = @as(f32, @floatFromInt((height - 1) - y)) / @as(f32, @floatFromInt(height)); + const ray: Ray = scene.camera.createRay(scaled_x, scaled_y); + const light = scene.lights.items[0].point_light; + const cylinder = scene.objects.items[0].cylinder; + const ambient_color_intensity = scene.lights.items[1].ambient_light; + const cylinder_translation = scene.transforms.items[0]; + const ray_object = Transformation.ray_global_to_object(ray, cylinder_translation, cylinder); + var record = cylinder.hits(ray_object); + record = Transformation.hitRecord_object_to_global(record, cylinder_translation, cylinder); + record.intersection_point.x = record.intersection_point.x + record.normal.x * 0.001; + record.intersection_point.y = record.intersection_point.y + record.normal.y * 0.001; + record.intersection_point.z = record.intersection_point.z + record.normal.z * 0.001; + if (record.hit) { + const vec_to_light = record.intersection_point.to(light.position); + const vec_to_light_object = Transformation.ray_global_to_object(Ray{ .direction = vec_to_light, .origin = record.intersection_point }, cylinder_translation, cylinder); + var obstacle = cylinder.hits(vec_to_light_object); + obstacle = Transformation.hitRecord_object_to_global(obstacle, cylinder_translation, cylinder); + if (obstacle.hit) { + pixels[index] = .{ + .r = @as(u8, @intFromFloat(255.0 * ambient_color_intensity)), + .g = 0, + .b = 0, + .a = 255, + }; + } else { + const norm = record.normal; + const inter = record.intersection_point; + const light_color = 255.0 * compute_lighting(inter, norm, light.position, ambient_color_intensity); + pixels[index] = .{ + .r = @as(u8, @intFromFloat(light_color)), + .g = 0, + .b = 0, + .a = 255, + }; + } + } else { + pixels[index] = .{ + .r = 0, + .g = 0, + .b = 0, + .a = 255, + }; + } + index += 1; + } + } +} + pub fn main() !void { const camera = Camera{ .origin = Vec3.nil(), @@ -62,14 +115,13 @@ pub fn main() !void { defer _ = gpa.deinit(); const allocator = gpa.allocator(); - var scene = Scene.Scene{ - .camera = camera, - .lights = std.ArrayList(Scene.SceneLight).init(allocator), - .objects = std.ArrayList(Scene.SceneObject).init(allocator), - }; + var scene = Scene.Scene.init(allocator, camera); + defer scene.deinit(); + try scene.objects.append(.{ .cylinder = cylinder }); try scene.lights.append(.{ .point_light = light }); try scene.lights.append(.{ .ambient_light = ambient_color_intensity }); + try scene.transforms.append(cylinder_translation); const height = 1000; const width = 1000; @@ -82,52 +134,7 @@ pub fn main() !void { }; defer image.deinit(allocator); - var index: usize = 0; - for (0..height) |y| { - for (0..width) |x| { - const scaled_x: f32 = @as(f32, @floatFromInt(x)) / @as(f32, @floatFromInt(width)); - const scaled_y: f32 = @as(f32, @floatFromInt((height - 1) - y)) / @as(f32, @floatFromInt(height)); - const ray: Ray = camera.createRay(scaled_x, scaled_y); - const ray_object = Transformation.ray_global_to_object(ray, cylinder_translation, cylinder); - var record = cylinder.hits(ray_object); - record = Transformation.hitRecord_object_to_global(record, cylinder_translation, cylinder); - record.intersection_point.x = record.intersection_point.x + record.normal.x * 0.001; - record.intersection_point.y = record.intersection_point.y + record.normal.y * 0.001; - record.intersection_point.z = record.intersection_point.z + record.normal.z * 0.001; - if (record.hit) { - const vec_to_light = record.intersection_point.to(light.position); - const vec_to_light_object = Transformation.ray_global_to_object(Ray{ .direction = vec_to_light, .origin = record.intersection_point }, cylinder_translation, cylinder); - var obstacle = cylinder.hits(vec_to_light_object); - obstacle = Transformation.hitRecord_object_to_global(obstacle, cylinder_translation, cylinder); - if (obstacle.hit) { - image.pixels[index] = .{ - .r = @as(u8, @intFromFloat(255.0 * ambient_color_intensity)), - .g = 0, - .b = 0, - .a = 255, - }; - } else { - const norm = record.normal; - const inter = record.intersection_point; - const light_color = 255.0 * compute_lighting(inter, norm, light.position, ambient_color_intensity); - image.pixels[index] = .{ - .r = @as(u8, @intFromFloat(light_color)), - .g = 0, - .b = 0, - .a = 255, - }; - } - } else { - image.pixels[index] = .{ - .r = 0, - .g = 0, - .b = 0, - .a = 255, - }; - } - index += 1; - } - } + calculate_image(image.pixels, &scene, height, width); var file = try std.fs.cwd().createFile("out.qoi", .{}); defer file.close();