aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/rtw/texture.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/rtw/texture.zig')
-rw-r--r--src/rtw/texture.zig48
1 files changed, 48 insertions, 0 deletions
diff --git a/src/rtw/texture.zig b/src/rtw/texture.zig
index ae24265..95cbef4 100644
--- a/src/rtw/texture.zig
+++ b/src/rtw/texture.zig
@@ -1,4 +1,5 @@
const std = @import("std");
+const Image = @import("zigimg").Image;
const Color = @import("vec.zig").Color;
const Vec3 = @import("vec.zig").Vec3;
@@ -10,12 +11,14 @@ const TextureTag = enum {
solid,
checker,
noise,
+ image,
};
pub const Texture = union(TextureTag) {
solid: SolidTexture,
checker: CheckerTexture,
noise: NoiseTexture,
+ image: ImageTexture,
pub fn makeSolid(color: Color) Texture {
return .{ .solid = .{ .color = color } };
@@ -33,11 +36,16 @@ pub const Texture = union(TextureTag) {
return .{ .noise = try NoiseTexture.init(allocator, rng, scale) };
}
+ pub fn makeImage(allocator: std.mem.Allocator, file_path: []const u8) !Texture {
+ return .{ .image = try ImageTexture.init(allocator, file_path) };
+ }
+
pub fn value(tx: Texture, u: f64, v: f64, p: Vec3) Color {
return switch (tx) {
TextureTag.solid => |solidTx| solidTx.value(u, v, p),
TextureTag.checker => |checkerTx| checkerTx.value(u, v, p),
TextureTag.noise => |noiseTx| noiseTx.value(u, v, p),
+ TextureTag.image => |imageTx| imageTx.value(u, v, p),
};
}
};
@@ -102,3 +110,43 @@ pub const NoiseTexture = struct {
return rgb(1, 1, 1).mul(0.5 * (1.0 + @sin(tx.scale * p.z + 10.0 * tx.perlin.turb(p, 7))));
}
};
+
+pub const ImageTexture = struct {
+ image: Image,
+
+ fn init(allocator: std.mem.Allocator, file_path: []const u8) !ImageTexture {
+ const image = try Image.fromFilePath(allocator, file_path);
+ return .{
+ .image = image,
+ };
+ }
+
+ fn deinit(tx: ImageTexture) void {
+ tx.image.deinit();
+ }
+
+ fn value(tx: ImageTexture, u: f64, v: f64, p: Vec3) Color {
+ _ = p;
+ // Clamp input texture coordinates to [0, 1] x [1, 0]
+ const u_ = std.math.clamp(u, 0.0, 1.0);
+ const v_ = 1.0 - std.math.clamp(v, 0.0, 1.0); // Flip v to image coordinates
+ const i = @floatToInt(usize, u_ * @intToFloat(f64, tx.image.width));
+ const j = @floatToInt(usize, v_ * @intToFloat(f64, tx.image.height));
+ // Clamp integer mapping, since actual coordinates should be less than 1.0
+ const i_ = @min(i, tx.image.width - 1);
+ const j_ = @min(j, tx.image.width - 1);
+ const color_scale = 1.0 / 255.0;
+ const pixels = tx.image.pixels.asBytes();
+ const offset = j_ * tx.image.width * 4 + i_ * 4;
+ const r = @intToFloat(f64, pixels[offset + 0]);
+ const g = @intToFloat(f64, pixels[offset + 1]);
+ const b = @intToFloat(f64, pixels[offset + 2]);
+ const a = @intToFloat(f64, pixels[offset + 3]);
+ if (a == 0) {
+ // Ocean
+ return rgb(0, 0, 1.0);
+ } else {
+ return rgb(color_scale * r, color_scale * g, color_scale * b);
+ }
+ }
+};