Skip to content

Commit

Permalink
Add bilinear interpolation for texture maps
Browse files Browse the repository at this point in the history
  • Loading branch information
SinclaM committed Jan 13, 2024
1 parent 820add9 commit ae14613
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 139 deletions.
3 changes: 2 additions & 1 deletion scenes/earth.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@
"spherical": {
"uv-pattern": {
"image": {
"file": "earthmap1k.png"
"file": "earthmap1k.png",
"interpolation": "bilinear"
}
}
}
Expand Down
9 changes: 8 additions & 1 deletion src/parsing/scene.zig
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ fn UvPatternConfig(comptime T: type) type {
},
image: struct {
file: []const u8,
interpolation: enum { none, bilinear } = .none,
},
};
}
Expand Down Expand Up @@ -282,7 +283,13 @@ fn parseUvPattern(
defer im.deinit();

const canvas = try Canvas(T).fromImage(arena_allocator, im);
break :blk UvPattern(T).uvImage(canvas);
break :blk UvPattern(T).uvImage(
canvas,
switch (image.interpolation) {
.none => .None,
.bilinear => .Bilinear
}
);
},
}
};
Expand Down
31 changes: 27 additions & 4 deletions src/raytracer/patterns/texture_map.zig
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,10 @@ fn UvCheckers(comptime T: type) type {
fn UvImage(comptime T: type) type {
return struct {
const Self = @This();
const Interpolation = enum { None, Bilinear };

canvas: Canvas(T),
interpolation: Interpolation,

fn uvPatternAt(self: Self, u: T, v: T, object_point: Tuple(T)) Color(T) {
_ = object_point;
Expand All @@ -76,7 +78,28 @@ fn UvImage(comptime T: type) type {
const x = u * @as(T, @floatFromInt(self.canvas.width - 1));
const y = v_flip * @as(T, @floatFromInt(self.canvas.height - 1));

return self.canvas.getPixelPointer(@intFromFloat(@round(x)), @intFromFloat(@round(y))).?.*;
switch (self.interpolation) {
.None => {
return self.canvas.getPixelPointer(@intFromFloat(@round(x)), @intFromFloat(@round(y))).?.*;
},
.Bilinear => {
const x1 = @floor(x);
const x2 = @ceil(x);
const y1 = @floor(y);
const y2 = @ceil(y);

const color_11 = self.canvas.getPixelPointer(@intFromFloat(x1), @intFromFloat(y1)).?.*;
const color_21 = self.canvas.getPixelPointer(@intFromFloat(x2), @intFromFloat(y1)).?.*;
const color_12 = self.canvas.getPixelPointer(@intFromFloat(x1), @intFromFloat(y2)).?.*;
const color_22 = self.canvas.getPixelPointer(@intFromFloat(x2), @intFromFloat(y2)).?.*;

const color_x1 = color_11.mul(x2 - x).add(color_21.mul(x - x1));
const color_x2 = color_12.mul(x2 - x).add(color_22.mul(x - x1));

const color_xy = color_x1.mul(y2 - y).add(color_x2.mul(y - y1));
return color_xy;
}
}
}
};
}
Expand Down Expand Up @@ -124,8 +147,8 @@ pub fn UvPattern(comptime T: type) type {
};
}

pub fn uvImage(canvas: Canvas(T)) Self {
return .{ .variant = .{ .uv_image = .{ .canvas = canvas } } };
pub fn uvImage(canvas: Canvas(T), interpolation: UvImage(T).Interpolation) Self {
return .{ .variant = .{ .uv_image = .{ .canvas = canvas, .interpolation = interpolation } } };
}

fn uvPatternAt(self: Self, u: T, v: T, object_point: Tuple(T)) Color(T) {
Expand Down Expand Up @@ -536,7 +559,7 @@ test "Canvas-based checker pattern in 2D" {
const canvas = try Canvas(f32).fromPpm(allocator, ppm);
defer canvas.destroy();

const uv_pattern = UvPattern(f32).uvImage(canvas);
const uv_pattern = UvPattern(f32).uvImage(canvas, .None);

try testing.expect(uv_pattern.uvPatternAt(0.0, 0.0, undefined).approxEqual(Color(f32).new(0.9, 0.9, 0.9)));
try testing.expect(uv_pattern.uvPatternAt(0.3, 0.0, undefined).approxEqual(Color(f32).new(0.2, 0.2, 0.2)));
Expand Down
133 changes: 0 additions & 133 deletions www/worker.js

This file was deleted.

0 comments on commit ae14613

Please sign in to comment.